0%

Java 反射

反射作用

1,反编译;2,通过反射机制访问类中的以下几个。

Class 类,Constructor 构造方法,Field 属性,Method 方法,Modifier 修饰符

1
public class Emp{				//Class
2
    private String name;		//Field
3
    public Emp() {}             //Constructor
4
    public void work(){			//Method
5
        System.out.println(name+"正在工作。。");
6
    }
7
}

获取class类型对象的三种方式

1
Class c1=Class.forName("com.czw.reflect.Emp");      //第一种
2
//c1引用保存内存地址指向堆中的对象,该对象代表Emp整个类
3
Class c2=Emp.class;                 //第二种
4
//java中每个类型都有class属性
5
Emp e=new Emp();
6
Class c3=e.getClass();              //第三种,c3是运行时类
7
//java中每个对象都有getClass方法

Class.forName(“Emp”);这是将Emp类装载到JVM虚拟机的过程,会执行静态语句块。

而Class c= Emp.class;不会执行。

创建类的对象实例

newInstance()方法

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Object o=c.newInstance();	//调用的是无参构造方法,Emp必须要有
3
c.getDeclaredConstructor().newInstance();//据说JDK9之后用这个方法

反射的使用

反射获取类的属性

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Field [] fs=c.getFields();		//获取所有public修饰的属性
3
getDeclaredFields()				//获取所有属性
4
    
5
for(Field f:fs){
6
    int i=f.getModifiers()		//返回属性修饰符(int类型,0-4)
7
    Modifiler.toString(i)		//返回数字所对应的字符(public。。。)
8
    Class type=f.getType()		//获取属性类型
9
	type.getName()				//获取类型名称(java.lang.String)
10
    type.getSimpleName()		//获取类型名称(String)
11
	f.getName()					//获取属性名字(name)
12
}

综合起来,打印类的结构

1
Class c=Class.forName("com.czw.reflect.Emp");//获取类
2
Field[] fs= c.getDeclaredFields();		//活获取类的所有属性集合
3
StringBuilder sb=new StringBuilder();
4
sb.append(Modifier.toString(c.getModifiers())+" class "+c.getName()+" {\n");
5
for (Field f:fs){		//遍历所有属性
6
    sb.append("\t"+Modifier.toString(f.getModifiers())+" ");//修饰符
7
    sb.append(f.getType().getSimpleName()+" ");			//属性类型
8
    sb.append(f.getName()+";\n");					//属性名
9
}
10
sb.append("}");
11
System.out.println(sb);

结果完美

针对获取某一个属性

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Field fid=c.getDeclaredField("id");
3
fid.setAccessible(true);        //打破封装,使其可以获取私有属性
4
Object o=c.newInstance();       //得到Emp类的实例
5
fid.set(o,100);               	//给对象o的id属性赋值100
6
System.out.println(fid.get(o));	//获取o的id

反射获取类的方法

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Method [] ms=c.getMethods();	//获取所有public修饰的方法
3
getDeclaredMethods()			//获取所有方法
4
    
5
for(Method m:ms){
6
    int i=m.getModifiers()		//返回方法修饰符(int类型,0-4)
7
    Modifiler.toString(i)		//返回数字所对应的字符(public。。。)
8
    Class type=m.getReturnType()//获取方法返回值类型
9
	type.getName()				//获取返回值类型全称(java.lang.String)
10
    type.getSimpleName()		//获取返回值类型名称(String)
11
	m.getName()					//获取方法名字(name)
12
}
13
14
Class[] pts=getParameterTypes()//获取方法参数列表
15
pts[index].getSimpleName()					//获取方法参数类型名称

打印类的方法集

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Method[] ms=c.getDeclaredMethods();		//获取方法集合
3
StringBuilder sb=new StringBuilder();
4
sb.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+" {\n");			//打印类信息
5
for(Method m:ms){
6
    sb.append("\t"+Modifier.toString(m.getModifiers())+" ");//方法修饰符
7
    sb.append(m.getReturnType().getSimpleName()+" ");//方法返回值
8
    sb.append(m.getName()+"(");					//方法名称
9
    Class[] pts=m.getParameterTypes();		//获取方法参数列表
10
    for(int i=0;i<pts.length;i++){
11
        sb.append(pts[i].getSimpleName());	//参数类型
12
        if(i!=pts.length-1){
13
            sb.append(",");
14
        }
15
    }
16
    sb.append("){}\n");
17
}
18
sb.append("}");
19
System.out.println(sb);

结果同样完美

获取某个特定方法并调用

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Method m1=c.getDeclaredMethod("work",String.class);//与属性不同,要加参数列表
3
Method m2=c.getDeclaredMethod("say",String.class,int.class);
4
m1.setAccessible(true);        	//打破封装,使其可以获取私有属性
5
Object o=c.newInstance();       //得到Emp类的实例
6
m1.invoke(o,"ccc");				//传入对象和对应参数,无返回值直接使用
7
Object or=m2.invoke(o,"czw",999);//有返回值的用对象接受
8
System.out.println(or);			//输出返回值

反射获取构造方法

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Constructor[] cs=c.getConstructors();
3
StringBuilder sb=new StringBuilder();
4
for(Constructor ctr:cs){
5
    sb.append(Modifier.toString(ctr.getModifiers())+" "+c.getSimpleName()+"(");
6
    Class[] cpts=ctr.getParameterTypes();
7
    for(int i=0;i<cpts.length;i++){
8
        sb.append(cpts[i].getSimpleName());
9
        if(i!=cpts.length-1){
10
            sb.append(",");
11
        }
12
    }
13
    sb.append("){}\n");
14
}
15
System.out.println(sb);

获取某个特定构造方法并创建对象

1
Class c=Class.forName("com.czw.reflect.Emp");
2
Constructor ctr=c.getDeclaredConstructor(String.class,int.class,boolean.class);//获取有参构造方法
3
Object oc=ctr.newInstance("ccc",18,true);//这个newInstance属于ctr的有参构造方法
4
System.out.println(oc);//打印对象

反射获取父类和实现接口

1
public class Emp extends Person implements sleep{}
2
3
Class sc=c.getSuperclass();		//获取父类
4
System.out.println(sc.getSimpleName());
5
Class[] ics=c.getInterfaces();	//获取接口集合
6
for(Class i:ics){
7
	System.out.println(i.getSimpleName());
8
}

反射的缺点

反射是一种解释操作,性能上是有一定差距的,不如直接new对象。

反射可以获取到私有的属性和方法,所以有一点不安全。

可变长参数

Java中的可变长参数,可以看作数组,如果有精准匹配的,则不会去执行可变长参数方法。

1
public static void test(int... t){		//参数个数是0-n个,记为方法1
2
    System.out.println(t);
3
}
4
public static void test(int t){			//记为方法2
5
    System.out.println("精准匹配"+t);
6
}
7
8
test();//执行方法1
9
test(1)//此时执行方法2
10
test(1,2,3,4,5);//执行方法1
11
int[] is=[1,2,3]
12
test(is);//执行方法1
13
14
//可变长参数只能出现一次,并且必须是最后一位
15
public static void test(int... t,int i){		//不可以
16
    System.out.println(t);
17
}
18
public static void test(String s,int... t){		//可以
19
    System.out.println(t);
20
}

配置文件

xxx.properties:每行第一个符号(.不算)前的字符串作为键,之后作为值,键是唯一的。

1
Properties p = new Properties();
2
FileInputStream fis=new FileInputStream("xxx.properties");
3
p.load(fis);			//通过输入流加载出配置文件内容
4
fis.close();
5
String value1=p.getProperty("key1");	//获取每一个键对应的值
6
String value2=p.getProperty("key2");
7
//然后便可以将值用来进行自定义配置

这样增强了代码的灵活性,同样的代码,不同的配置,产生不同的效果,比如Spring Boot框架的配置文件就是这样。

注意:在出现中文时,要处理好编码,免得发生乱码。

使用反射+IO+properties配置文件联合应用,可以动态创建对象,在运行时才装载类,更加的灵活,降低了耦合度,增加了扩展性。

总结

头发-1