一、反射
Java的反射机制?
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
为什么需要反射?
1.提高程序的灵活性
2.屏蔽掉实现的细节,让使用者更加方便好用
3.运行期类型的判断,动态加载类
反射的缺点:
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
反射主要用于哪些场景?
1.逆向代码 ,例如反编译
2.与注解相结合的框架 例如Retrofit
3.单纯的反射机制应用框架 例如EventBus 2.x
4.动态生成类框架 例如Gson
5.动态代理
创建反射的三种方式
1.通过new对象实现反射机制
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
缺点:必须要创建出对象才能获得其类反射
2.通过类名实现反射机制
Class classobj3 = Student.class;
System.out.println(classobj3.getName());
缺点:必须要创建出这个类才可以
3.通过路径实现反射机制
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
注意:这个是比较常用的
使用反射创建实例和new一个对象的区别?
new只能用于编译期就能确定的类型, 而反射可以在运行时才确定类型并创建其对象。反射在框架中用的比较多,并且使用反射可以降低耦合
二、对象相等判断
== 和 equals 的区别是什么
==的作用是判断两个对象的地址是否相等,即,判断两个对象是不是同一个对象。(基本数据类型 = = 比较的是值,引用数据类型 == 比较的是内存地址)
equals如果没有重写的话,默认是与==一样的,如果重写过equals则根据重写的具体内容类进行判断
String a = new String("a"); // a 为一个引用
String b = new String("a"); // b为另一个引用,对象的内容一样
String aa = "b"; // 放在常量池中
String bb = "b"; // 从常量池中查找
if(a==b){//false}
if(a.equals(b)){//true}
if(aa==bb)//true
if(aa.equalse(bb))//true
}
.说明:String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
hashCode 与 equals
1.hashCode是根类Obeject中的方法。默认情况下,Object中的hashCode() 返回对象的32位jvm内存地址。也就是说如果对象不重写该方法,则返回相应对象的32为JVM内存地址。
重写equals必须重写hascode
hashCode()与equals()的相关规定
如果两个对象相等,则hashcode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashcode值,它们也不一定是相等的
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
2.为什么需要hashcode方法?
因为在批量的对象比较中,hashCode要比equals速度更快效率更高,所以很多集合都用到了hashCode,比如HashTable。
对象的比较的原则如下:
两个obj,如果equals()相等,hashCode()一定相等。
两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。
3 改写equals时总是要改写hashcode
java.lnag.Object中对hashCode的约定:
1.在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
2.如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
3.如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
在集合中,equals通常重写用来比较内容是否相等,如果不重写hashcode,两个对象的内存地址不一样,hashcode不同,因为是先检索hashcode值,不等的情况下才会去比较equals方法的。举例:假如hashset集合放入两个对象,值相等,如果没重写hashcode,则会放入两个,与无重复冲突,需重写hashcode。
总结来说就是两点
1.使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率
2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。
三、什么是序列化?如何实现序列化?
1.简要解释:
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
2. 通过什么方式进行序列化?
简单来说,只要对象实现了Serializable接口,该对象就可以进行序列化。Serializable接口只是一个标记接口,不包括任何属性和方法。
被序列化类:
public class Person implements Serializable {
private static final long serialVersionUID = 8580374262428896565L;
private String name;
private int age;
private String height;
public Person(String name, int age, String height) {
this.name = name;
this.age = age;
this.height = height;
}
}
序列化person对象:
FileOutputStream fos = new FileOutputStream("e://out.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person person = new Person("starry", 25, "177");
oos.writeObject(person);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("e://out.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Person personOut = (Person) ois.readObject();
注意:serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常即是InvalidCastException。
3.用途
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
四、Java中有哪些IO流?字节流和字符流如何进行转换?
java 中 IO 流分为几种?
1.按照流的流向分,可以分为输入流和输出流;
2.按照操作单元划分,可以划分为字节流和字符流;
3.按照流的角色划分为节点流和处理流。
字节流和字符流如何进行转换?
InputStreamReader: 字节流转换为字符流的桥梁
OutputStreamWriter: 字符流转换为字节流的桥梁
五、String、StringBuffer、StringBuilder的区别?
String有哪些特性
1.不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
2.常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
3.final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。
String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
String、StringBuffer、StringBuilder的区别?
继承关系:
从结构图上可以直到,StringBuffer和StringBuiler都继承自AbstractStringBuilder类
线程安全
StringBuffer:线程安全。StringBuilder:线程不安全。
因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 StringBuilder 修饰。
String
String类底层是字符数组常量,加法操作。
String在进行字符串拼接时,会产生大量的垃圾数据,占用内存空间,消耗资源、执行效率是非常慢的。
总结
1.String是内容不可变的,而StringBuffer、StringBuilder都是内容可变的。
2.StringBuffer是同步的,数据安全的,但是效率低; StringBuilder是不同步的,数据不安全,相比来说,效率高。
六、包装类
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
Integer
如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false
public static void main(String[] args) {
Integer a = new Integer(3);
Integer a2 = new Integer(3);
Integer a3 = 3;
Integer a4 = 3;
Integer a5 = 128;
Integer a6 = 128;
System.out.println(a==a2);//false
System.out.println(a.equals(a3));//true
System.out.println(a == a4);//false
System.out.println(a3 == a4);//true
System.out.println(a5==a6);//false
}
七、JDK1.7和JDK1.8有哪些区别?
1.JDK1.7:
- 1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。
- 1.2 Switch语句支持String类型
- 1.3 Try-with-resource语句:
- 1.4 Catch多个异常:
- 1.5 数字类型的下划线表示 更友好的表示方式,不过要注意下划线添加的一些标准。
字面常量数字里加下划线的规则:下划线只能在数字之间,在数字的开始或结束一定不能使用下划线。 - 1.6 泛型实例的创建可以通过类型推断来简化 可以去掉后面new部分的泛型类型,只用<>就可以了。
- 1.7 并发工具增强: fork-join框架最大的增强,充分利用多核特性,将大问题分解成各个子问题,由多个cpu可以同时解决多个子问题,最后合并结果,继承RecursiveTask,实现compute方法,然后调用fork计算,最后用join合并结果。
2.JDK1.8(重要)
- 2.1 接口的默认和静态方法:
- 2.2 Lambda 表达式:(例如: (x, y) -> { return x + y; } ;λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。)
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
- 2.3 方法与构造函数引用:
- 2.4 函数式接口
- 2.5 Annotation 注解:支持多重注解
- 2.6 新的日期时间 API
- 2.7 Base64编码
- 2.8 JavaScript引擎Nashorn
- 2.9 Stream的使用
- 2.10 Optional:
- 2.11 扩展注解的支持:
- 2.12 并行(parallel)数组:
- 2.13 编译器优化: