java中的一些惯用法总结

字符串

字符串是存储在字符串常量池中的。例如如下的两个字符串的内存地址值是同样的:html

String str1 = "hello" + "world";
String str2 = "helloworld";
System.out.println(str1 == str2);      // true
System.out.println(str1.equals(str2)); // true

String str3 = "hello";
String str4 = "world";
String str5 = str3 + str4;
System.out.println(str5 == str2);      // false
System.out.println(str5.equals(str2)); // true

在以上的代码中str2和str5的地址值不相同,若是咱们对str5使用intern()方法即:java

String str6 = str5.intern();           // native方法
System.out.println(str6 == str2);      // true

就能够返回true。面试

字符串的intern方法

面试题:假设字符串常量池中不存在字符串"hello",那么String s = new String("hello")建立了几个对象?spring

:两个堆空间的value值(字符数组)和字符串常量池中的hello实体。咱们能够经过查看new String(String str)的源码:数组

/** The value is used for character storage. */
 private final char value[];

 /**
 * Initializes a newly created {@code String} object so that it represents
 * the same sequence of characters as the argument; in other words, the
 * newly created string is a copy of the argument string. Unless an
 * explicit copy of {@code original} is needed, use of this constructor is
 * unnecessary since Strings are immutable.
 *
 * @param  original
 *         A {@code String}
 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

用句柄操做对象

java中一切皆对象,可是咱们操做的其实是指向这个对象的句柄(Handle),这个句柄也叫作引用(Reference)或者指针(Pointer)。咱们能够将这一情形想象成用遥控器(Handler)操做电视机(Object)。没有电视机,遥控器。安全

即便没有电视机,遥控器也能够单独存在。即句柄是能够单独存在的(并不指向任何实体)。例如:less

String s;

以上的java代码建立的仅仅是句柄而不是对象。此时若是向s发送一条消息,将会或者一个运行时异常,所以更安全的作法是:建立一个句柄的时候进行显式初始化:ide

String s = "";

java程序运行时数据的存放位置

  • Register。处理器内部(最快),因为数量有限,因此寄存器是根据须要有编译器分配的。咱们对它没有直接的控制权,也不可能在程序中找到寄存器的任何踪影。this

  • Stack。驻留与常规RAM区域,速度仅次于寄存器。咱们能够经过它的“堆栈指针”得到直接的处理支持。(指针下移建立新的内存;指针上移释放内存)。建立程序时java编译器必须准确知道堆栈内保存的全部数据的“长度”以及“存在的时间”(必须生成相应的代码,以便于向上或者向下移动指针)。这一限制无疑影响了程序的灵活性,因此java将对象的句柄保存在堆栈中,可是对象并不存放在其中。spa

  • Heap。一种常规用途的内存池(也是在RAM区域),保存java对象。Heap最吸引人的地方在于:编译器并没必要要知道要从堆中分配多少存储空间,也没必要要知道存储的数据要在堆中停留多长时间——用堆保存数据会获得更大的灵活性。然而every coin have two slices,在堆中分配存储空间会花费更长的时间!

  • 静态存储。“静态”(Static)指的是“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。咱们能够用static关键字指出一个对象的特定元素是静态的,可是java对象自己永远不会置入静态存储空间

  • 常数存储。常数存储一般直接置于一个程序代码内部。这样作是安全的,由于他们永远不会被改变。有的常数须要严格保护,能够考虑将他们置入只读存储器(ROM)。

  • 非RAM存储:将数据彻底保存子啊一个程序以外。对典型的2个例子就是“流式对象”和“固定对象”。

    • 流式对象:对象-->字节流-->另外一台机器

    • 固定对象:对象-->磁盘

基本数据类型

基本数据类型因为比较经常使用,而堆栈的效率又高于堆。因此基本数据类型都是保存在堆栈中。对于基本数据类型咱们不须要用new,而是建立了一个并不是句柄的“自动变量”,该变量容纳了具体的值,并保存在堆中能够更高效的存取。

java中的数组

在C、C++中使用数组是很是危险的,由于那些数组只是内存块,若是程序访问本身内存块以外的数据或者在初始化以前使用内存会产生不可预料的后果。在C++中应该尽可能避免使用数组而换用Standard TemplateLibrary中更安全的容器。java中的数组会自动记性范围检查会形成少许的内存开销。可是咱们能够换来更高的工做效率。

变量做用域

变量的做用域是由花括号的位置决定的。
在C、C++中如下的代码是合法的:

{
    int x = 10;
    {
        int x = 100; // 不合法Duplicate local variable x
    }
}

可是在java中编译器会认为变量x已经被定义,因此C、C++能将一个变量“隐藏”在一个更大的做用域中,java的设计者认为这样使程序产生了混淆。

注意区分红员变量和局部变量

成员变量都有默认值,而局部变量必须进行初始化。

文档注释

文档注释只能为public和protected的成员处理文档,private和default的不会被javadoc提取。文档注释中能够嵌入html,例如:

javadoc

异常

方法抛出异常的时候,该方法会从栈上当即被取出,而异常再度丢给栈上的方法(也就是调用方),若是调用方没有对异常进行处理而是继续抛出异常,调用方就会从栈上弹出,异常再度交给此时的栈顶方法,如此一路下去……
finally中的代码有一种状况下是执行不到的:finally的前面出现了System.exit(0)。

static和final

static只能修饰类的成员(变量和方法),不能修饰局部变量。static变量存放在方法区。随着类的加载而加载,存在方法区中,随着类的消失而消失,生命周期最长。若是没有给定初值,static变量会被默认置0.(或者null)。

Q:若是一个类被标记为final,再将该类中的方法标记位final是否是不少余?
A:不仅是多余,并且是多了不少!若是一个类为final,那么它根本就没有子类,根本不可能覆写父类中的方法(只有继承才有覆写)。

使用类加载器加载配置文件

工程目录以下:

使用类加载器加载配置文件

加载该配置文件应该这样写:

public class Test {

    @org.junit.Test
    public void test() throws IOException {
        
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream is = classLoader.getResourceAsStream("org/gpf/conf/db.properties");
        Properties properties = new Properties();
        properties.load(is);
        properties.list(System.out);
    }
}

利用反射获取父类的泛型

public class Person <K,V>{
    // some code...
}

public class Student extends Person<String,Integer> {
    // some code...
}

Class<?> clazz =  Student.class;
Type type = clazz.getGenericSuperclass(); // org.gpf.Person<java.lang.String, java.lang.Integer>
ParameterizedType parameterizedType = (ParameterizedType) type; // type的子接口
for (Type c : parameterizedType.getActualTypeArguments()) {
    System.out.println(((Class<?>)c).getName()); // Class是Type接口的实现类
}

java中的枚举

所谓枚举,就是枚举类的对象的个数是有限的,能够穷举出来的类。实际上单例模式也可使用枚举来实现(Effective Java中的单例经典实现,枚举只有一个成员)。jdk1.5以前须要自定义枚举类,jdk1.5以后提供了enum关键字用于定义枚举类。

例如季节是有限的:春夏秋冬。

// jdk1.5以前的枚举类
public class Season {

    // 1.声明final属性
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    //  2.为保证明例的数目是肯定的须要私有化构造器,在构造器中初始化final的属性
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    // 3.经过公用的方法调用属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    // 4.内部实例化枚举类的对象
    public static final Season SPRING = new Season("spring", " 春暖花开");
    public static final Season SUMMER = new Season("summer", " 夏日炎炎");
    public static final Season FALL = new Season("fall", " 秋高气爽");
    public static final Season WINTER = new Season("spring", " 白雪皑皑");

    public String show() {
        return "Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]";
    }
    
}

咱们可使用如下的方式进行调用

Season season = Season.FALL;
System.out.println(season.show());
System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());

jdk1.5以后咱们可使用enum关键字来简化枚举类的定义:

// jdk1.5以后的枚举类
public enum Season {

    SPRING("spring", " 春暖花开"),
    SUMMER("summer", " 夏日炎炎"),
    FALL("fall", " 秋高气爽"),
    WINTER("spring", " 白雪皑皑");
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    public String show() {
        return "Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]";
    }
    
}

这样使用枚举类:

Season season = Season.FALL;
System.out.println(season.show());
System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());

Season[] seasons = Season.values(); // 返回全部枚举类的对象的数组
for (Season s : seasons) {
    System.out.println(s.getSeasonName());
}

season = Season.valueOf("SUMMER"); // 返回枚举类型的对象
System.out.println(season);

咱们也可让枚举类型实现接口:

// jdk1.5以后的枚举类
public enum Season implements Info{

    SPRING("spring", " 春暖花开"){
        @Override
        public void show() {
            System.out.println(1);
        }
    },
    SUMMER("summer", " 夏日炎炎"){
        @Override
        public void show() {
            System.out.println(2);
        }
    },
    FALL("fall", " 秋高气爽"){
        @Override
        public void show() {
            System.out.println(3);
        }
    },
    WINTER("spring", " 白雪皑皑"){
        @Override
        public void show() {
            System.out.println(4);
        }
    };
    
    private final String seasonName;     // 季节名
    private final String seasonDescribe; // 季节描述
    
    private Season(String seasonName,String seasonDescribe) {
        this.seasonName = seasonName;
        this.seasonDescribe = seasonDescribe;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDescribe() {
        return seasonDescribe;
    }
    
    @Override
    public void show() {
        System.out.println("Season [seasonName=" + seasonName + ", seasonDescribe="
                + seasonDescribe + "]");
    }
    
}

以上的程序中每一个枚举类型的实例都各自实现本身的方法!

相关文章
相关标签/搜索