- JAVA基础
- String类、类型转换和BigDecimal
- 类的加载顺序题
- 请问:String a = "12" + "34" + new String("12") + "1234" ;创建了几个字符串对象?请试着从原理的层次分析一下为什么;
答: 创建了5个对象:
- "1234"
- "12"
- "1234121234"
- new String()
- new StringBuilder().append("12").append("34").append("12").append("1234").toString()
> 分析:字符串常量在编译器会进行优化,`"12" + "34"`被优化为 `"1234"`,在字符串常量池新建字
> 符串对象`"1234"`;`new String("12")`这里面的`"12"`在字符串常量池不存在,所以会在字符串
> 常量池新建字符串对象`"12"`;后面的 `"1234"`由于在常量池已经创建好了,所以不会再创建。
> 那么加上`new String()`创建的对象、`StringBuilder`拼接产生的新字符串
> 对象`"1234121234"`以及StringBuilder本身toString方法产生的对象,
> 理想情况下会产生共5个字符串对象。
> ```java
> // 扩展:来看看这个例子:
> String a = "123";
> String b = new String("123");
> String c = "1" + "23";
> String d = "1";
> String e = d + "23";
> System.out.println(System.identityHashCode(a)); // 打印字符串的原始内存地址的hash值 460141958
> System.out.println(System.identityHashCode(b)); // 1163157884
> System.out.println(System.identityHashCode(c)); // 460141958
> System.out.println(System.identityHashCode(e)); // 1956725890
> // 可以看到,a和 c的内存地址一致,因为都是在字符串常量池里面,引用的是同一个对象
> // b 新建了一个字符串对象 new String()
> // e 是拼接而成,也就是 new StringBuilder().toString(),toString()里面还是新建了一个字符串new String()
> ```
难疑点:字符串常量? 字符串常量池?为什么会调用StringBuilder`拼接?
- short s1 = 1; s1 = s1 + 1;会正确运行吗?short s1 = 1; s1 += 1;会正确运行吗?请问为什么会出现这样的结果?
答:. s1 = s1 + 1;会报错,因为short、byte、char等类型在做运算的时候会提升类型到int类型,int类型运算完成赋值给short类型会发生类型转换错误,可以强制进行转换:s1 = (short) (s1 + 1);s1 += 1;可以正常运行,因为+=` 操作符会自动进行转换操作,也就是使用 short(1) 进行运算
难疑点:数据类型的类型转换? - double a = 1d - 0.9d; double b = 0.9d - 0.8d;请问a == b 成立吗?为什么?在Java中涉及到这样的精确计算应该怎么办?
答:a == b 成立,结果都是0.09999999999999998,Java在进行 - 和 / 运算的时候,无法精确计算,可以使用 BigDecimal 类型进行计算。需要注意的是 BigDecimal.valueOf(0).equals(BigDecimal.valueOf(0.0)) 返回的结果是false,因为它的equals方法比较了小数位,所以不相等,可以使用 compareTo方法比较。
- (必做)有下面的一个类,请问最后main方法输出是什么?请试着分析打印输出的过程,写出自己的理解。
```java
public class Book {
static Book book = new Book();
static int amount = 112;
static {
System.out.println("书的静态代码块");
System.out.println("amount=" + amount);
}
int price = 110;
{
System.out.println("书的普通代码块");
}
Book() {
System.out.println("书的构造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
public static void staticFunction() {
System.out.println("书的静态方法");
}
public static void main(String[] args) {
staticFunction();
}
}
答案:
运行的结果为:
> 书的普通代码块
>
> 书的构造方法
>
> price=110,amount=0
>
> 书的静态代码块
>
> amount=112
>
> 书的静态方法
分析:
> 1. 运行main方法,会先发生 Book 类的加载,类加载的过程是:`加载->验证->准备->解析->初始化->使用->卸载`,在执行初始化这个阶段的时候,会对类所有的静态变量进行初始化,也就是`book = null`,`amount=0`。注意:假如使用final修饰静态变量,它是不会初始化的,直接赋值,因为final修饰的变量不可改变;
>
> 2. 第二步是对静态变量赋值,先赋值`book = new Book();`这个地方new 了一个Book,new Book()需要**类加载**,然后再进行**对象实例化**,但是类加载已经进行过了,那么这次就直接实例化对象了。
>
> 3. 实例化Book对象,需要先对Book的成员变量赋值` int price = 110`,然后执行代码块 `{ System.out.println("书的普通代码块"); }`,所以我们看到控制台先打印了这句话,接着执行**构造方法**,打印输出 `书的构造方法`,打印输出`price=110,amount=0`,`price=110`很好理解,就是刚才在实例化对象的时候成员变量赋值,那么`amount=0`呢?这就得联系到刚才初始化类的时候我们给类的静态变量初始化,amount被初始化为0,所以打印了`amount=0`。
>
> 4. 再往下继续给类的静态变量赋值,`amount = 112;`,赋值完所有的静态变量后开始执行类的静态方法块,
>
> `static {
> System.out.println("书的静态代码块");
> System.out.println("amount=" + amount);
> }`
>
> 于是,打印输出了 `书的静态代码块`和 `amount=112`。
>
> 5. 类加载结束之后,开始执行main方法里面的代码,打印 `书的静态方法` 这句话。
>
> 以上就是整个类加载到执行main方法的全部过程,只有静下心来认真思考,并且对类加载的知识有一定的掌握,才能分析的出来。
 ### 三级目录 ### 三级目录



