今日分享开始啦,请大家多多指教~
明天给大家分享一下装箱和拆箱以及Java反射机制。Java为每种根本数据类型都提供了对应的包装型,而且还提供了包装类和根本数据类型之间的互相转化机制,也就是所谓的“装箱”和“拆箱”。Java反射机制其实是动静地获取信息以及动静调用对象的性能。明天将从多方面进行总结,以便大家更好地意识及利用。
一、什么是装箱?什么是拆箱?
在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
而在从Java SE5开始就提供了主动装箱的个性,如果要生成一个数值为10的Integer对象,只须要这样就能够了:
Integer i = 10;
这个过程中会主动依据数值创立对应的 Integer对象,这就是装箱。
那什么是拆箱呢?顾名思义,跟装箱对应,就是主动将援用类型转换为根本数据类型:
Integer i = 10; //装箱int n = i; //拆箱
简略一点说,装箱就是 主动将根本数据类型转换为援用类型;拆箱就是主动将援用类型转换为根本数据类型。
下表是根本数据类型对应的援用类型:
二、装箱和拆箱是如何实现的
上一大节理解装箱的基本概念之后,这一大节来理解一下装箱和拆箱是如何实现的。
咱们就以Interger类为例,上面看一段代码:
public class Main {public static void main(String[] args) {Integer i = 10;int n = i;}}
反编译class文件之后失去如下内容:
从反编译的字节码中能够看出,在装箱的时候主动调用的是interger的valueOf(int)办法。而在拆箱的时候主动调用的是interger的intValue办法
因而用一句话总结装箱和拆箱的实现过程:
装箱过程是通过调用包装器的valueOf办法实现的,而拆箱过程是通过援用类型调用xxxValue实现的。
三、面试中的相干问题
尽管大多数人对装箱和拆箱的概念都分明,然而在面试和口试中遇到了与装箱和拆箱的问题却不肯定会答得上来。上面列举一些常见的与装箱/拆箱无关的面试题。
1、上面这段代码的输入后果是什么?
public class Main { 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); }}
兴许有些敌人会说都会输入false,或者也有敌人会说都会输入true。然而事实上输入后果是:
true
false
为什么会呈现这样的后果?输入结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知到底,上面这段代码是Integer的valueOf办法的具体实现:
public static Interger valueOf(int i){ if(i>=-128&&i<=IntergerCache.high){ return IntergerCache.cache[i+128]; }else{ return new Interger(i); }}
通过valueOf办法创立Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中曾经存在的对象的援用;否则创立一个新的Integer对象。
下面的代码中i1和i2的数值为100,因而会间接从cache中取曾经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是别离指向不同的对象。
其它的援用类型,能够去查看valueOf的实现。
弦外音:八种数据类型取值范畴
整型:
byte:-2^7 ~ 2^7-1,即-128 ~ 127。1字节。Byte。开端加B
short:-2^15 ~ 2^15-1,即-32768 ~ 32767。2字节。Short。开端加S
int:-2^31 ~ 2^31-1,即-2147483648 ~ 2147483647。4字节。Integer。
long:-2^63 ~ 2^63-1,即-9223372036854774808 ~ 9223372036854774807。8字节。Long。开端加L。(也能够不加L)
浮点型:
float:4字节。Float。
double:8字节。Double。
字符型:
char:2字节。Character。
布尔型:
boolean:Boolean。
类型转换:
boolean类型与其余根本类型不能进行类型的转换(既不能进行主动类型的晋升,也不能强制类型转换), 否则,将编译出错。
byte型不能主动类型晋升到char,char和short间接也不会产生主动类型晋升(因为正数的问题),同时,byte当然能够间接晋升到short型。
当对小于int的数据类型(byte, char, short)进行运算时,首先会把这些类型的变量值强制转为int类型进行计算,最初会失去int类型的值。因而,如果把2个short类型的值相加,最初失去的后果是int类型,如果须要失去short类型的后果,就必须显示地运算后果转为short类型。
上面程序的输入后果是什么?
当 "=="运算符的两个操作数都是 包装器类型的援用,则是比拟指向的是否是同一个对象,而如果其中有一个操作数是表达式(即蕴含算术运算)则比拟的是数值(即会触发主动拆箱的过程)。另外,对于包装器类型,equals办法并不会进行类型转换。
第一个和第二个输入后果没有什么疑难。第三句因为 a+b蕴含了算术运算,因而会触发主动拆箱过程(会调用intValue办法),因而它们比拟的是数值是否相等。而对于c.equals(a+b)会先触发主动拆箱过程,再触发主动装箱过程,也就是说a+b,会先各自调用intValue办法,失去了加法运算后的数值之后,便调用Integer.valueOf办法,再进行equals比拟。同理对于前面的也是这样,不过要留神倒数第二个和最初一个输入的后果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf办法)。
一、类的加载与ClassLoader的了解
1、加载
将class文件字节码内容加载到内存中,并将这些静态数据转换成办法区的运行时数据结构,而后生成一个代表这个类的java.lang.class对象。
2、链接
将Java类的二进制代码合并到JVM的运行状态之中的过程。
验证:确保加载的类信息合乎JVM标准,没有平安方面的问题;
筹备:正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在办法区内进行调配;
解析:虚拟机常量池内的符号援用(常量名)替换为间接援用(地址)的过程。
3、初始化
执行类结构器<clinit>()办法的过程。类结构器<clinit>()办法是由编译器主动收集类中所有类变量的赋值动作和动态代码块中的语句合并产生的。(类结构器是结构类信息的,不是结构该类对象的结构器)。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则须要先触发其父类的初始化。
虚构机会保障一个类的<clinit>()办法在多线程环境中被正确加锁和同步。
二、什么时候会产生类初始化
1、类的被动援用(肯定会产生类的初始化)
当虚拟机启动,先初始化main办法所在的类;
new一个类的对象;
调用类的动态成员(除了final常量)和静态方法;
应用java.lang.reflect包的办法对类进行反射调用;
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类;
2、类的被动调用(不会产生类的初始化)
当拜访一个动态域时,只有真正申明这个域的类才会被初始化。如:当通过子类援用父类的动态变量,不会导致子类初始化;
通过数组定义类援用,不会触发此类的初始化;
援用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了);
三、类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成办法区的运行时数据结构,而后在堆中生成一个代表这个类的java.lang.Class对象,作为办法区中类数据的拜访入口。
四、动静创建对象执行办法
package com.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method; public class Test03 { public static void main(String[] args) throws Exception { //取得class对象 Class c1 = Class.forName("com.reflection.User"); //1、结构一个对象,实质是无参结构器 User user1 = (User) c1.newInstance(); System.out.println(user1); //2、通过结构器创建对象 Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, int.class); User user2 = (User) constructor.newInstance(1,"郭一诺",1); System.out.println(user2); //3、通过反射调用一般办法 User user3 = (User) c1.newInstance(); Method setName = c1.getDeclaredMethod("setName", String.class); //invoke激活 setName.invoke(user3,"素小暖"); System.out.println(user3.getName()); //4、通过反射操作属性 User user4 = (User) c1.newInstance(); Field name = c1.getDeclaredField("name"); //true:勾销Java语言拜访查看 name.setAccessible(true); name.set(user4,"素小暖2"); System.out.println(user4.getName()); }}
五、通过反射获取泛型信息
1、代码实例
package com.reflection;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map; public class Test04 { public void test01(Map<String,User> map, List<User> list){ System.out.println("test01"); } public Map<String,User> test02(){ System.out.println("test02"); return null; } //通过反射获取泛型信息 public static void main(String[] args) throws Exception { Method method = Test04.class.getMethod("test01", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("***"+genericParameterType); if(genericParameterType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } method = Test04.class.getMethod("test02", null); Type genericReturnType = method.getGenericReturnType(); if(genericReturnType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("test02,"+actualTypeArgument); } } }}
2、控制台输入
3、反射解决泛型问题
六、通过反射获取注解信息
1、代码实例
package com.reflection; import java.lang.annotation.*;import java.lang.reflect.Field; public class Test05 { public static void main(String[] args) throws Exception { Class c1 = Class.forName("com.reflection.Student"); //通过反射获取注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //取得注解value的值 TableSu tableSu = (TableSu) c1.getAnnotation(TableSu.class); String value = tableSu.value(); System.out.println(value); //取得类指定的注解 Field field = c1.getDeclaredField("name"); FieldSu annotation = field.getAnnotation(FieldSu.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); }} @TableSu("db_student")class Student{ @FieldSu(columnName = "db_id",type = "int",length = 10) private int id; @FieldSu(columnName = "db_name",type = "varchar2",length = 10) private String name; @FieldSu(columnName = "db_age",type = "int",length = 10) private int age; public Student() { } public Student(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; }}//类名的注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface TableSu{ String value();} //属性的注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface FieldSu{ String columnName(); String type(); int length();}
2、控制台输入
七、通过配置文件动静调用办法
1、在我的项目根目录建Class.txt文件,内容如下
classname=com.guor.reflect.Person
methodname=getPerson
2、建一个Person类
3、通过反射获取配置文件中内容调用类中办法
4、控制台输入
小结:
Java反射机制其实是动静地获取信息以及动静调用对象的性能;而所谓动静是指,对于任意一个运行状态的类,都可能晓得这个类的所有属性和办法;并且对于任意一个对象,都能调用他的任意一个办法。
今日份分享已完结,请大家多多包涵和指导!