Java 基础
OOP相关
11、什么是拆装箱?
答:
在理解拆箱和装箱之前需要理解一个概念:基本类型和封装类型,比如int是基本类型,integer是封装类型(包装器类型)。
基本类型其实对应到c++或者直接说内存中的某个数据具体样子。int比integer占的内存小的多,integer的功能比int多得多。
这两者之间jdk为我们提供了一些转换的方式:拆装箱。
这是jdk5之后提供的,为的就是让基本类型和jdk封装类型之间能自动转换。这种自动转换也叫自动拆装箱。
在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
在jdk5之后,
Integer i = 10;
其实使用反编译文件查看后可以发现上面的语句被翻译成下面的:
Integer i = Integer.valueOf(10);
这是编译器帮我们做的,这就是自动装箱。
所谓的拆箱就是反过来的过程:
比如:
Integer i1 = 100;
int i5 = i1;
编译器会帮我们翻译成:
Integer i1 = Integer.valueOf(100);
int i5 = i1.intValue();
如上就是自动拆箱装箱。
拓展
我们经常遇到如下面试题:
public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; System.out.println(i1 == i2); System.out.println(i3 == i4); }
最后的结果是:
true false
按照自动装箱拆箱的原理不难理解i3,i4是不同的对象,所以是false。但是为什么i1==i2是true,按理说i1和i2也是不同的对象,具体的原理可以点进Integer.valueOf()去看,这里直接说结论:int有缓存,缓存的代码如下:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
从中可以看出,jdk会缓存-128~127之间的整数。每次去执行valueOf的时候都会检查一下int是否在何处里面已经有了,有的话就不会创建新的对象,而是把引用都指到同一个对象;所以i1==i2为true。
Java中的包装类包括:
byte:Byte,short:Short,int:Integer,long:Long,flfloat:Float,double:Double,char:Character ,boolean:Boolean。
12、equals与==的区别
答:
==: 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是
指相同一个对象。比较的是真正意义上的指针操作。
equals:
equals比较的是两个对象是否相等,所有的对象都基础自Object,即使你没声明,编译器也会为你加上。Object有着方法equals
,子类需要重写equals方法来为子类做一些业务性上的判断,比如:两个对象是否相等,业务上只需要比较两个对象的所有filed都相同即可,不需要内存地址都相同的;他们可能是不同地方创建的两个对象,但是他们就是相同。
拓展
一般重写
equals
,也需要重写hashCode
,这是为什么呢?因为:按照一般
hashCode()
方法的实现来说,相等的对象,它们的hash code一定相等。
hashCode()
方法给对象返回一个hash code值。这个方法被用于hash tables,例如HashMap。
- 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用
hashCode()
方法,该方法必须始终如一返回同一个integer。- 如果两个对象根据
equals(Object)
方法是相等的,那么调用二者各自的hashCode()
方法必须产生同一个integer结果。- 并不要求根据
equals(java.lang.Object)
方法不相等的两个对象,调用二者各自的hashCode()
方法必须产生不同的integer结果。然而,程序员应该意识到对于不同的对象产生不同的integer结果,有可能会提高hash table的性能。大量的实践表明,由
Object
类定义的hashCode()
方法对于不同的对象返回不同的integer。在object类中,hashCode定义如下:
public native int hashCode();
Java对象的eqauls方法和hashCode方法是这样规定的:
- 1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。
- 2、如果两个对象的hashCode相同,它们并不一定相同。
hashCode的重写还会有助于HashMap,HashSet等集合的检索,所以这点请引起重视,特别是数据量比较大的时候。
有关hashCode和equals更多的知识,请看这里:https://www.cnblogs.com/Qian123/p/5703507.html#_labelTop
hashCode的编写涉及散列算法,可以根据自己的对象特点去实现。
13、java中是值传递引用传递
答:
对于基本数据类型,传递是值的副本,而不是值本身。
对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。
14、构造方法能不能显式调用
答:
不能,构造方法其实实际上是我们编译之后的字节码的init方法,我们声明使用那个构造方法之后,由系统调用的。
15、内部类与静态内部类的区别
答:
内部类访问等其实需要通过它的包含类(外部类),它保留了自己和外部类的一个连接。内部类一个很大的优势就是内部类可以继承其它的类,并且可以直接使用包含类(外部类)字段、方法。这是一个变相的多继承。 这也是内部类最后诱人的地方。
静态内部类和其实是嵌套类,好处是组织结构上清晰点。它会切断和包含类(外部类)的连接,使用静态内部类的时候就像使用别的类差不多。这也意味着:
- 要创建嵌套类的对象,并不需要其外围类的对象。
2. 不能从嵌套类的对象中访问非静态的外围类对象。
https://www.cnblogs.com/aademeng/articles/6192954.html
16、String、 StringBuffer 和 StringBuilder 的区别是什么?
答:
String内部定义了一个private final byte[] value;
,每次修改String其实是新创建一个String,然后将引用修改到新的String,效率比较低。
为了解决上面的问题,JDK提供了StringBuffer和StringBuilder来构建String,这两个构建String的效率比String高。StringBuffer是线程安全的,因为内部实现使用了大量的synchronized
。StringBuilder是非线程安全的。
为了获得比较高的性能,我们可以使用ThreadLocal和StringBuilder结合的方式,来创建字符串。
17、String a = new String(“zxc”); 创建了多少个对象。
答:
“zxc”
会去字符串常量池看看有没有这个字符串,有就直接返回这个对象,不会创建新的对象,否则就会在常量池创建一个字符串。
然后在堆里面创建一个String对象,字符串赋值给String对象里面的value。
创建一个对象,或者两个对象。
18、String str=”aaa”,与String str=new String(“aaa”)一样吗?
答:
不一样,String str=”aaa”
会把字符串检查在常量池中是否拥有,如果有的话就直接拿常量池的字符串引用关联起赋值的对象,如果不存在就放入常量池,所谓的常量池在jdk8之中放在方法区,再把引用给这个对象。
new String(“aaa”)
是在堆里面之间创建一个字符串对象。String提供了String.intern()
,将字符串变量放入线程池。
两者不相同,但是使用equals方法可以判断两个字符串是相等的。
为了更好的理解,可以结合以下代码一起看:
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
System.out.println(s1 == s9); // false
System.out.println(s4 == s5); // false
System.out.println(s1 == s6); // true
}
19、Java的四种引用
答:
强引用:OutOfMemory也不能释放的引用。
弱引用:内存不足的时候,强制回收。
软引用:只要JVM垃圾回收器发现就回收。ThreadLocal就是使用这个。
虚引用:这个和弱引用差不多,只不过回收之前会放入ReferenceQueue,异步回收。
20、Java创建对象有几种方式?
答:
- new 一个对象
- 反射
- clone
- 反序列化