什么是不可变类
不可变类:当创建了一个类的实例之后,就不允许修改它的值了。
特别注意:String和包装类(Integer、Long、Float。。。)都是不可变类。String采用了享元设计模式。
Java中基本数据类型各占几个字节?
在Java中
占1个字节:byte、boolean
占2个字节:char、short
占4个字节:int、float
占8个字节:long、double
它们对应的封装类型是:Integer、Double、Long、Float、Short、Byte、Character、Boolean
基本数据类型和对应的包装类的区别:
初始值的不同。当基本数据类型变量和包装类的对象作为类的实例变量时,默认初始值是不同的。包装类的对象默认初始值是null。基本数据类型变量的默认初始值根据变量类型不同而不同,如Int的默认初始值是0,。
包装类是不可变类
public class Test{ Integer i; int j; public static void main(String[] args){ System.out.println(new Test().i); System.out.println(new Test().j); } } //结果输出为L null 0
其他需要注意的问题:
1.默认声明的小数是double类型的,比如1.2默认就是double类型的,因此在对float类型的变量进行复制的时候需要进行类型转换。如float i = 1.2f;
2.null不是一个合法的object实例,所以没有为其分配内存。null仅仅用于表明该引用目前没有指向任何对象。
3.Integer的缓存策略。
在类加载时就将-128到127的Integer对象创建了,并保存cache数组中(Integer cache[]),一旦程序调用Integer.valueOf(i)方法,如果i的值是在-128到127之间就直接在cache缓存数组中去取Integer对象,不在的话,就创建信道包装类对象。
4.包装类作为参数传递时,仍是按值传递。
序列化与反序列化
1.定义
把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为Java对象的过程称为对象的反序列化。2.实现方式
所有实现序列化的类都必须实现Serializable接口,它是一种标记接口,里面诶呦任何方法。当序列化的时候,需要用到ObjectOutPutStream里面的writeObject();当反序列化的时候,需要用到ObjectIntputSteam里面的readObject()方法。3.特点
- 序列化时,只对对象的状态进行保存,而不管对象的方法。
- 当一个父类实现序列化时,子类自动实现序列化,不需要显示实现Serializable接口。
- 当一个对象的实例变量引用了其他对象时,序列化该对象时,也把引用对象进行序列化。
- 对象中被static或者transient修饰的变量,在序列化时其变量值是不被保存的
4.好处
一是,实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上;二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。
Switch能否用string做参数?
在Java7之前,switch只能支持byte、short、char、int或者其对应的包装类以及Enum类型。在Java7中,String支持被加上了。
在使用switch时,需要注意另外一个问题,如果和匹配的case情况中省略了break,那么匹配的case值后的所有情况都会执行,而不管case是否匹配,一直遇到break结束。
equals和==的区别
== 可用于比较基本数据类型(比较的是他们的值是否相等),也可以用于比较对象在内存中存放的地方是否相等。
Java当中所有的类都是机车于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初识行为是比较对象的内存地址,但在一些类库中这个方法被覆盖掉了,如String、包装类在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了(对于String的equals()方法比较二个对象的内容是否相等)。
因此,对于符合数据类型之间进行的equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用 == 进行比较的,所以比较后的结果跟 == 的结果相同。
public class Demo{ public static void mian(String[] args){ Demo demo1 = new Demo(); Deom demo2 = new Demo(); System.out.println(demo1.equals(demo2)); //结果为false System.out.println(demo1 == demo2); //结果为false } }
HashCode的作用
HashCode()方法是从Object类继承过来的,Object类中的hashCode()方法返回的是对象在内存中的地址转换成int 的值,如果对象没有重写hashCode()方法,任何对象的hashCode()方法的返回值都是不相等的。
重写方法:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。
主要作用是用于查找的,为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable,hashCode是用来在散列存储结构中确定对象的存储地址的。
考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在改对象了?(注意:集合中不允许重复的元素存在)。
当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashCode值;如果在该位置有值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其他的地址。
为什么在重写equals方法的同时,还需要重写hashCode方法?
比如在使用set集合时,往其中放入内容相同的对象,如果没有重写hashCode()方法,那么set中将会放入内容相同的对象(因为2个对象的地址不同),这和set集合的性质不同。因此需要在重写equals方法的同时,必须重写hashCode方法。
Java中对于equals()方法和hashCode()方法是这样规定的:
- 如果2个对象的equals方法返回true,则hashCode返回的值也相同。
- 如果2个对象的equals方法返回false,则hashCode返回的值不一定相同。
- 如果2个对象的hashCode方法返回值相同,则equals返回值不一定相同。
- 如果2分对象的hashCode方法返回值不同,则equals返回的值为false。
尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。
String、StringBuffer与StringBuilder的区别
1.可变与不可变
String对象时不可变的;StringBuffer与StringBuilder对象是可变的。
因此在每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象,所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM的CG就会开始工作,性能就会降低。
修改String对象的原理:首先创建一个StringBuffer对象,然后调用append()方法,最后调用toString()方法。
String s= "Hello"; s += "World"; 等价于 | V StringBuffer sb = new StringBuffer(s); sb.append("World"); sb.toString();
使用StringBuffer类时,每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。所以多数情况下都使用StringBuffer,特别是字符串对象经常改变的情况下。
2.是否线程安全
String和StringBuffer是线程安全的,StringBuilder并没有对方法进行加同步锁,所以不安全。
3.初始化方式不同
StringBuffer和StringBuilder只能用构造函数的形式来初始化。String除了用构造函数进行初始化外,还可以直接赋值。
StringBuilder和StringBuffer的底层是怎么实现的
每个StringBuffer对象都有一定的缓冲区容量(可变长的字符数组,类似于ArrayList的底层实现),哦任初始容量为16个字符。当字符串大小没有超过容量时,不会分配新的容量;当字符串大小超过容量时,会自动扩容(扩容后的容量大约是之前的2倍)。StringBuffer和StringBuilder,字符串都是存放在char[]中的。
try-cathh-finally里有return,finally还执行吗?如果会的话,什么时候执行,在return前还是后
1.finally块里的代码在return之前执行。
2.如果finally块中有return语句的话,它将覆盖掉函数中其他return语句。
如果finally块中,改变了try块中返回变量的值,该变量为基本数据类型的haul,则finally块中改变变量值的语句将不起作用;如果该变量为引用变量的话,则起作用。
Finally块中的代码不一定执行。
比如,try块执行之前,出现了异常,则程序终止。
比如,在try块中执行了System.exit(0)。
说下异常的原理
异常是指程序运行时所发生的错误。
Throwable是所有异常的父类,它有两个子类:Error和Exception。
1.Error表示程序在运行期间发生了非常严重的错误,并且该错误是不可恢复的。Error不需要捕捉,如OutOfMemoryError。
2.Exception是可恢复的异常。它包含两种类型:检查异常和运行时异常。
- 检查异常(Checked Exception)
比如IOException、SQLException和FileNotFountException都是检查异常。它发生在编译阶段,编译器会强制程序去捕获此类异常,需要在编码时用try-catch捕捉。- 运行时异常(RuntimeException)
它发生在运行阶段,编译器不会检查运行时异常。比如空指针异常,算术运算异常,数组越界异常等。如果代码会产生RuntimeException,则需要通过修改代码进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!当捕获异常的时候,先捕获子类异常,再捕获父类异常。
static的使用方式
static有4种使用方式:修饰类(静态内部类)、修饰成员变量(静态变量)、修饰成员方法(静态成员方法)、静态代码块。
- 修饰类(静态内部类)
- 修饰成员变量(静态变量)
静态变量属于类,只要静态变量所在的类被加载,这个静态变量就会被分配空间,在内存中只有一份,所有对象共享这个静态变量。使用有二种方式,一个是类名.静态变量,还有一张是对象.静态变量。不能在方法体中定义静态变量(无论该方法是静态的还是非静态的)。VS实例变量属于类,只有对象创建后,实例变量才会分配空间。- 修饰成员方法(静态成员方法)
静态成员方法属于类,不需要创建对象就可以使用。而非静态方法属于对象,只有在对象创建出来以后才可以被使用。静态方法里面只能访问所属类的静态成员变量和静态成员方法。- 静态代码块
静态代码块经常被用来初始化静态变量,在类加载的初始化阶段会执行为静态变量赋值的语句和静态代码块的内容,静态代码块只会被执行一次。