1. 为什么要有包装类
Java语言是面向对象的语言,其设计理念是“一切皆对象”。但8种基本数据类型却出现了例外,它们不具备对象的特性。正是为了解决这个问题,Java为每个基本数据类型都定义了一个对应的引用类型,这就是包装类。
Java之所以提供8种基本数据类型,主要是为了照顾程序员的传统习惯。这8种基本数据类型的确带来了一定的方便性,但在某些时候也会受到一些制约。比如,所有的引用类型的变量都继承于Object类,都可以当做Object类型的变量使用,但基本数据类型却不可以。如果某个方法需要Object类型的参数,但实际传入的值却是数字的话,就需要做特殊的处理了。有了包装类,这种问题就可以得以简化。
2. 自动装箱和自动拆箱
自动装箱:可以把一个基本类型的数据直接赋值给对应的包装类型;
自动拆箱:可以把一个包装类型的对象直接赋值给对应的基本类型;
3. int和Integer在做==运算时会得到什么结果?
int是基本数据类型,Integer是int的包装类。二者在做==运算时,Integer会自动拆箱为int类型,然后再进行比较。届时,如果两个int值相等则返回true,否则就返回false。
4. Double和Integer在做==运算时会得到什么结果?
Integer、Double不能直接进行比较,因为自动拆箱后不属于相同的基本数据类型,所以需要将Integer、Double先转为转换为相同的基本数据类型(如double),然后使用==进行比较。
Integer i = 100;
Double d = 100.00;
System.out.println(i.doubleValue() == d.doubleValue());
Java语言之所以摒弃了多继承的这项特征,是因为多继承容易产生混淆。比如,两个父类中包含相同的方法时,子类在调用该方法或重写该方法时就会迷惑。
准确来说,Java是可以实现"多继承"的。因为尽管一个类只能有一个直接父类,但是却可以有任意多个间接的父类。这样的设计方式,避免了多继承时所产生的混淆。
1. Object类提供的几个常用方法
getClass():返回对象的运行时类
equals(Object obj):判断指定对象obj与该对象是否相等
hashCode():返回该对象的hashCode值
toString():返回对象的字符串标识
wait():线程阻塞
notify():唤醒单个线程
notifyAll():线程所有线程
clone():对象的自我克隆
finalize():当系统中没有引用变量引用到该对象时,垃圾回收器调用此方法来清理该对象的资源。
2. hashCode()和equals()的重写
我们先来看一下,没有重写的hashCode和equals的源码,不用怀疑,这就是全部源码。
//返回对象在JVM中的32位内存地址,native解释请看参看文章
public native int hashCode();
//比较两个对象的32位内存地址
public boolean equals(Object obj) { return (this == obj); }
一般来说对象的hashCode()重写是根据对象属性来计算对象的hash值
@Overridepublic int hashCode() {int result = 17;result = 31 * result + name.hashCode();result = 31 * result + age;return result;}
一般来说对象的equals()重写是判断每个属性的值是否相同
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;boolean nameCheck=false;boolean ageCheck=false;if (this.name == student.name) {nameCheck = true;} else if (this.name != null && this.name.equals(student.name)) {nameCheck = true;}if (this.age == student.age) {ageCheck = true;} else if (this.age != null && this.age.equals(student.age)) {ageCheck = true;}if (nameCheck && ageCheck){return true;}return false;
1. String可以被继承吗?
在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。
Java 9之前字符串采用char[]数组来保存字符,即 private final char[] value ;
Java 9做了改进,采用byte[]数组来保存字符,即 private final byte[] value ;
2. String、StringBuffer、StringBuilder的区别
String对象的不可变性,导致每次修改值都要创建新的对象,性能肯定就很慢,没有的对象还要GC来清除,性能就更低了。不可变
线程安全
性能低
StringBuffer对象可以改变值,性能就提升了不少啦。但是也要面临着线程同步的问题,所以加了synchronized进行同步,耗费了一点点性能。可变
线程安全
性能较高
StringBuilder对象也可以改变值,但是这家伙为了追求性能,连同步都不要了,所以是线程不安全的。可变
线程不安全
性能最高
3. 字符串与常量池
String s1 = "coder";
String s2 = "coder";
String s3 = "coder" + s2;
String s4 = "coder" + "coder";
String s5 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s4 == "codercoder");
- 解析:false、false、true
4. “hello” 和 new String(“hello”) 的区别
当Java程序直接使用 “hello” 的字符串直接量时,JVM将会使用常量池来管理这个字符串;
当使用 new String(“hello”) 时,JVM会先使用常量池来管理 “hello” 直接量,再调用String
类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中。
显然,采用new的方式会多创建一个对象出来,会占用更多的内存,所以一般建议使用直接量的方式创建字符串。
接口只能有抽象方法
静态常量
,没有构造方法。
1. 异常处理
在Java中,处理异常的语句由try、catch、finally三部分组成。其中,try块用于包裹业务代码,catch块用于捕获并处理某个类型的异常,finally块则用于回收资源。
try {p.age = 18;return p; //被保存起来}catch (Exception E){return null;}finally {p.age = 28;}
2. 异常分类
Error、Exception
3. finally关键字
不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了
return语句,finally块总会被执行。
1. 为什么要设计泛型
因为Java设计者不知道程序员会将什么类型的数据放入集合中,Java引入了“参数化类型”的概念,即泛型(Generic)允许程序在创建集合时指定集合元素的类型。例如 List< String> ,表明该List只能保存字符串类型的对象。
2. 泛型擦除
当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。例如list2没有声明数据类型,那么list2会把list1的字符串元素当作Object元素处理。
List list1 = ...;
List list2 = list1; // list2将元素当做Object处理
3. List super T>和List extends T>
? 是类型通配符, List> 可以表示各种泛型List的父类,意思是元素类型未知的List;
List super T> 用于设定类型通配符的下限,此处 ? 代表一个未知的类型,但它必须是T的父
类型;
List extends T> 用于设定类型通配符的上限,此处 ? 代表一个未知的类型,但它必须是T的
子类型。
强引用
:对象没有被引用就会被GC回收
软引用
:内存不够就会被GC回收
弱引用
:GC扫描到就会被回收
虚引用
:完全类似没有引用对象