今天要讲的内容:如下图
反射的感念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
上面的概念是来自百度百科上的解释,其实概念知道点就行,只要还是要回运用,这才是关键.
java是面向对象语言,在现实的世界里,我们是将实现的东西抽象化,抽象化就是描述这世界的东西用java语言去模拟抽象出来,比如人类,它应该有的属性是什么,人的头发,姓名,年纪,衣服颜色等,他的行为呢?比如吃饭,睡觉,搞对象,看电影等,在java语言中,用类来将这些抽象成的东西体现出来:
package smart.com.demo; /** * Created by admin on 2017/1/14. */ public class Person { private String name; private int age; public void eat(){ System.out.print("吃饭"); } public void sleep(){ System.out.print("睡觉"); } }但是具体说一个人比如小明的话,就要new Person()----->这个代表小明,如图说明:
上面的三个对象是通过创建Person的实例而来,但是像类这种,比如上面的Person类,String,Date等,用什么类表示呢?在java中用Class来表示,我们平时定义一个类是用class XXX,我们知道我们在eclipse或者studio中写的代码是以.java为后缀的文件,这是java源文件,然后通过javac去编译这个.java文件,生成.class文件,然后通过java命令运行.class文件,这就涉及到类加载器加载文件到JVM中,下面的图是粗略的描述类到运行时候的几个步骤:
Class对象的三种方式获取:
第一种:
Class第二种方式:person = Person.class;
Person p1 = new Person(); Class第三种clazz = (Class ) p1.getClass();
try { Class我们平时基本上都是使用第三种方式,使用第三种的原因是:clazz = (Class ) Class.forName("smart.com.demo.Person"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
Class.forName(类的全路径);//返回字节码 此字节码已经在JVM中,直接返回,如果没有则用类加载器加载到JVM中,缓存起来,
Class就是我们常说的字节码了,那么Class类给我们提供了什么常用的方法呢?类有它所属的包名吧,类的属性,方法,注解等等!
获取类相关的属性:
getDeclaredField(Object obj);获取类上声明的字段,包括private修饰的,但是最终要获取这个属性的值一定要设置这行代码:field.setAccessible(true);//暴力反射也就是在读取private的时候不要做检查
getField(String name) :返回一个 Field 对象,形参就是属性名,但是这获取类上非private修饰的属性,否则会报错
getFields():获取类中所有的非private修饰的属性
getDeclaredFields()获取类中所有的属性,包括private修饰的,
获取类上相关方法:
public Method getDeclaredMethod(String name, Class>... parameterTypes)
返回一个Method对象,表示的是方法,但是这个方法必须是非private修饰的,否则会报错,和获取字段一样
name是方法名:
Class>... parameterTypes:这个(name)方法的形参类型
package com.reflect;
/**
* Created by admin on 2017/1/14.
*/
public class Person {
public String name="xiaozhou";
public int age = 10;
public void eat(String name){
System.out.print("name=============="+name);
}
public void sleep(){
}
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;
}
}
测试:
package com.reflect;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
Method method = clazz.getDeclaredMethod("eat",String.class);
method.invoke(person, "今天天气真好");
}
}
public Method getMethod(String name, Class>... parameterTypes)
获取非private修饰的方法
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
clazz.getMethod("eat", String.class);
}
}
报错:
这是和上面方法不一样的地方!
public Method[] getMethods() throws SecurityException
获取非private中类的所有方法,包括父类的方法:
测试:
package com.reflect;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
Method[] methods = clazz.getMethods();
for(Method m:methods){
System.out.println("方法名="+m.getName());
}
}
}
打印结果:
public Method[] getDeclaredMethods() throws SecurityException
获取本类中声明的所有方法,包括private修饰的方法,不包括父类的方法
获取类所在包名
public Package getPackage()
获取类的修饰符
public native int getModifiers()
测试:
package com.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
int i = clazz.getModifiers();
String retval = Modifier.toString(i);
System.out.println("retval="+retval);
}
}
结果:
retval=public
获取类的名字
public String getTypeName() 获取类的类型全名 在jdk1.8
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
System.out.println("typeName="+clazz.getTypeName());
System.out.println("name="+clazz.getName());
System.out.println("SimpleName="+clazz.getSimpleName());
}
}
结果:
typeName=com.reflect.Person
name=com.reflect.Person
SimpleName=Person
获取类的父类
public native Class super T> getSuperclass()
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
Class cls = clazz.getSuperclass();
System.out.println("cls="+cls);
}
}
log:
cls=class java.lang.Object
获取类实现的接口对象:
public Class>[] getInterfaces()
判断类类型是否是基本类型
isPrimitive():判定指定的 Class
对象是否表示一个基本类型
public native boolean isPrimitive()
java中九种预定义的 Class
对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即boolean
、byte
、char
、short
、int
、long
、float
和double
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getMethod() throws Exception{
Person person = new Person();
person.setName("zhouguizhi");
person.setAge(20);
Class clazz = (Class) Class.forName("com.reflect.Person");
System.out.println(int.class.isPrimitive());
System.out.println(Integer.class.isPrimitive());
System.out.println(int.class==Integer.class);
}
}
true
判断Class是否是数组:
public native boolean isArray();
判断Class对象是否是注解:
public boolean isAnnotation()
判断Class对象是否是枚举
public boolean isEnum()
发现还有一个很重要的知识点没讲:就是构造函数
通过Class对象获取Constructor类,它代表某个类的构造函数
public Constructor>[] getConstructors() throws SecurityException
获取这个类声明的所有构造函数
public Constructor
获取某一个构造函数,根据上面的形参类型parameterTypes,比如Person类有一个构造函数为
Person(String name){}
那么parameterTypes =String.class
比如我Person类有三个构造函数:
package com.reflect;
/**
* Created by admin on 2017/1/14.
*/
public class Person {
private String name;
private int id;
public Person(int id) {
super();
this.id = id;
}
public Person(String name) {
super();
this.name = name;
}
public Person(String name, int id) {
super();
this.name = name;
this.id = id;
}
}
那么通过反射要创建哪一个构造函数呢?如图解释:
通过反射生成一个无参的构造函数
package com.reflect;
/**
* Created by admin on 2017/1/14.
*/
public class Person {
private String name;
private int id;
}
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getConstructor( );
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getConstructor() throws Exception{
Class clazz = (Class) Class.forName("com.reflect.Person");
Person person = clazz.newInstance();
}
}
发现测试并没有报错,我现在生成有参的构造函数再去创建一个无参的构造函数生成的对象试试:
public Person(String name, int id) {
super();
this.name = name;
this.id = id;
}
测试:
package com.reflect;
public class Test {
public static void main(String[] args) {
try {
getConstructor( );
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getConstructor() throws Exception{
Class clazz = (Class) Class.forName("com.reflect.Person");
Person person = clazz.newInstance();
}
}
报错log:
所以为什么一般要写一个无参的构造函数,
第二呢?通过反射创建带参数的构造函数对象
public static void getConstructor() throws Exception{
Class clazz = (Class) Class.forName("com.reflect.Person");
Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
Person person = constructor.newInstance("zhouguizhi",18);
System.out.println(person.getName());
System.out.println(person.getId());
}
比如把一个对象中的String值中如果有u就改成b:
package com.reflect;
/**
* Created by admin on 2017/1/14.
*/
public class Person {
private String name ;
private int id ;
public Person(String name, int id) {
super();
this.name = name;
this.id = id;
}
@Override
public String toString() {
return "[name=" + name + ", id=" + id + "]";
}
}
动态修改属性
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) {
Person p = new Person("zhouguizhi",8);
try {
System.out.println("修改前-----"+p);
changField( p);
System.out.println("修改后-----"+p);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void changField(Person p){
try {
Class clazz = (Class) Class.forName("com.reflect.Person");
Field[] fields = clazz.getDeclaredFields();
if(fields!=null&&fields.length>0){
for(Field field:fields){
field.setAccessible(true);
if(field.getType()==String.class){
String oldValue = (String) field.get(p);
String newValue = oldValue.replace('u', 'b');
field.set(p, newValue);
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
结果:
修改前-----[name=zhouguizhi, id=8]
修改后-----[name=zhobgbizhi, id=8]
反射用于工厂模式
随便写一个例子:
package com.reflect;
/**
* Created by admin on 2017/1/14.
*/
public abstract class Person {
public String name = "zhougizhi" ;
private int id ;
public Person() {
super();
}
public Person(String name, int id) {
super();
this.name = name;
this.id = id;
}
public abstract void eat();
}
Man.java
package com.reflect;
public class Man extends Person {
public Man() {
super();
}
public Man(String name, int id) {
super(name, id);
}
@Override
public void eat() {
System.out.println("男人吃饭");
}
}
Woman.java
package com.reflect;
public class Woman extends Person{
public Woman() {
super();
}
public Woman(String name, int id) {
super(name, id);
}
@Override
public void eat() {
System.out.println("女人吃饭");
}
}
工厂:
package com.reflect;
public class Factory {
public static Person getPerson(String className){
if(className==null||"".equals(className)){
return null;
}
Person person = null;
try {
person = (Person) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return person;
}
}
使用:
package com.reflect;
public class Test {
public static void main(String[] args) {
Man man = (Man) Factory.getPerson("com.reflect.Man");
Woman woman = (Woman) Factory.getPerson("com.reflect.Woman");
}
}
关于反射性能问题,我们通过和普通方法进行对比以及使用暴力反射也进行下对比
package com.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
test1();
test2();
test3();
}
/**
* 使用setAccessible(true)检查调用getName()方法
*/
private static void test3() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Person person = new Person();
Class clazz = person.getClass();
Method method = clazz.getDeclaredMethod("getName", null);
//检查方法是否是private修饰 是就暴力反射
method.setAccessible(true);
long startTime = System.currentTimeMillis();
for(int i=0;i<1000000000;i++){
method.invoke(person, null);
}
long endTime = System.currentTimeMillis();
System.out.println("调用setAccessible方法检查十亿次总耗时时间:"+(endTime-startTime)+"ms");
}
/**
* 普通方法调用
*/
private static void test1() {
Person person = new Person();
long startTime = System.currentTimeMillis();
for(int i=0;i<1000000000;i++){
person.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法调用十亿次总耗时时间:"+(endTime-startTime)+"ms");
}
/**
* 反射调用getName()方法
*/
private static void test2() throws Exception{
Person person = new Person();
Class clazz = person.getClass();
Method method = clazz.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for(int i=0;i<1000000000;i++){
method.invoke(person, null);
}
long endTime = System.currentTimeMillis();
System.out.println("使用反射调用十亿次总耗时时间:"+(endTime-startTime)+"ms");
}
}
测试结果:
普通方法调用十亿次总耗时时间:3ms
使用反射调用十亿次总耗时时间:1623ms
调用setAccessible方法检查十亿次总耗时时间:912ms
发现使用了反射调用方法和普通调用真是差了500多倍,所以为什么说使用反射会影响性能。
就写到这里吧,干需求去了!