java的动态性_Java动态性(2) – 之反射机制(Reflection)

  • Post author:
  • Post category:java


1.Java的动态性

反射机制

动态编译

动态执行JavaScript代码

动态字节码操作

2.动态语言

程序运行时,可以改变程序得结构或变量类型.典型语言:

Python,Ruby,JavaScript等.

如下JavaScript代码

function test(){

var s = “var a=3;var b=5;alert(a+b);”;

eval(s);

}

C,C++,Java不是动态语言,但Java有一定的动态性,我们可以利用反射机制,字节码操作获得类似动态语言的特性

Java的动态性让编程的时候更加灵活

3.反射机制

反射机制指的是可以在运行期间加载一些知道名字的类

对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法或属性

Class c = Class.forName(“com.test.User”);

类加载完之后,在堆内存中会产生一个Class类的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结果

4.Class类介绍

java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身

Class类的对象包含了某个被加载类的结构,一个被加载的类对应一个Class对象

当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便会自动产生一个Class对象

Class类是Reflection的根源

针对任何你想动态加载,运行的类,只有先获得相应的Class对象

User bean:

package com.lorinda.bean;

public class User {

private int id;

private int age;

private String uname;

public User(int id, int age, String uname) {

super();

this.id = id;

this.age = age;

this.uname = uname;

}

public User() {

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getUname() {

return uname;

}

public void setUname(String uname) {

this.uname = uname;

}

}

Demo01 测试各种类型对应Class对象的获取方式:

/**

* 测试各种类型对应Class对象的获取方式

* @author Matrix42

*

*/

public class ReflectionDemo01 {

public static void main(String[] args) {

String path = “com.lorinda.bean.User”;

try {

Class> clazz = Class.forName(path);

System.out.println(clazz); //class com.lorinda.bean.User

System.out.println(clazz.hashCode()); //366712642

//同样的类只会被加载一次

Class> clazz2 = Class.forName(path);

System.out.println(clazz2.hashCode()); //366712642

Class strClazz = String.class; //类名.class

Class> strClazz2 = path.getClass(); //对象.getClass();

System.out.println(strClazz==strClazz2);//true

Class> intClazz = int.class;

int[] arr01 = new int[10];

int[] arr02 = new int [30];

int[][] arr03 = new int[30][3];

//数组的Class对象只与类型和维度有关

System.out.println(arr01.getClass()==arr02.getClass()); //true

System.out.println(arr01.getClass().hashCode()); //1829164700

System.out.println(arr03.getClass().hashCode()); //2018699554

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

5.Class类的对象如何获取?

对于对象可以使用getClass()

使用Class.forName() (最常使用)

使用.class

6.反射机制的常见作用

动态加载类,动态获取类的信息(属性,方法,构造器)

动态构造对象

动态调用类和对象的任意方法,构造器

动态调用和处理属性

获取泛型信息

处理注解

Demo02 获取方法,属性,构造器等的信息:

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

/**

* 获取方法,属性,构造器等的信息

* @author Matrix42

*

*/

public class ReflectionDemo02 {

public static void main(String[] args) {

String path = “com.lorinda.bean.User”;

try {

Class> clazz = Class.forName(path);

//获取类的名字

System.out.println(clazz.getName());//获得包名+类名:com.lorinda.bean.User

System.out.println(clazz.getSimpleName());//获得类名:User

//获取属性信息

//Field[] fields = clazz.getFields();//只能获取public的field

Field[] fields = clazz.getDeclaredFields();//获得所有的field

Field field = clazz.getDeclaredField(“uname”);//根据名字获取field

for(Field temp:fields){

System.out.println(“属性: “+temp);

}

//获取方法

Method[] methods = clazz.getDeclaredMethods();

Method method01 = clazz.getDeclaredMethod(“getUname”, null);

//如果方法有参数,则必须传递参数类型对应的Class对象

Method method02 = clazz.getDeclaredMethod(“setUname”, String.class);

for(Method m:methods){

System.out.println(“方法: “+m);

}

//获得构造器信息

Constructor[] constructors = clazz.getDeclaredConstructors();

//单独获取,无参

Constructor c1 = clazz.getDeclaredConstructor(null);

System.out.println(“构造器: “+c1);

//单独获取,有参

Constructor c2 = clazz.getDeclaredConstructor(int.class,int.class,String.class);

System.out.println(“构造器: “+c2);

for(Constructor c:constructors){

System.out.println(“构造器: “+c);

}

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (NoSuchFieldException e) {

e.printStackTrace();

} catch (SecurityException e) {

e.printStackTrace();

} catch (NoSuchMethodException e) {

e.printStackTrace();

}

}

Demo03 通过反射动态操作构造器,方法,属性

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import com.lorinda.bean.User;

/**

* 通过反射动态操作构造器,方法,属性

* @auther Matrix42

*/

public class ReflectionDemo03 {

public static void main(String[] args) {

String path = “com.lorinda.bean.User”;

try {

Class clazz = Class.forName(path);

//动态操作构造器

User u = (User) clazz.newInstance(); //调用了User的无参构造方法

Constructor c = clazz.getConstructor(int.class,int.class,String.class);

User u2 = c.newInstance(1000,20,”Matrix42″);

System.out.println(u2.getUname());

//通过反射调用普通方法

//好处:方法名,参数都可以是变量,可以从数据库读取

User u3 = (User) clazz.newInstance();

Method method = clazz.getDeclaredMethod(“setUname”, String.class);

method.invoke(u3, “Matrix42”);

System.out.println(u3.getUname());

//通过反射操作属性

User u4 = (User) clazz.newInstance();

Field f = clazz.getDeclaredField(“uname”);

f.setAccessible(true);

f.set(u4, “24xirtaM”);

//默认会报错,添加f.setAccessible(true);关闭安全检查

//can not access a member of class com.lorinda.bean.User with modifiers “private”

System.out.println(u4.getUname()); //正常调用

System.out.println(f.get(u4)); //通过反射调用

} catch (Exception e) {

e.printStackTrace();

}

}

}

7.反射机制性能问题

当你获得灵活性的时候也会牺牲你的性能

setAccessible

启用和禁用安全检查的开关,值为true则表示反射的对象在使用时应取消Java语言访问检查.值为fals则表示反射的对象应该实施Java语言访问检查.并不是为true就能访问,为false就不能访问

禁止安全检查,可以提高反射的运行速度

可以考虑使用:cglib/javasssist字节码操作

反射性能测试:

import java.lang.reflect.Method;

import com.lorinda.bean.User;

public class ReflectionDemo04 {

public static void test01(){

User user = new User();

long startTime = System.currentTimeMillis();

for(int i=0;i<1000000000L;i++){

user.getUname();

}

long endTime = System.currentTimeMillis();

//421ms

System.out.println(“普通方法调用,执行10亿次,耗时:”+(endTime-startTime)+”ms”);

}

public static void test02() throws Exception{

User user = new User();

Class clazz = user.getClass();

Method m = clazz.getDeclaredMethod(“getUname”, null);

long startTime = System.currentTimeMillis();

for(int i=0;i<1000000000L;i++){

m.invoke(user, null);

}

long endTime = System.currentTimeMillis();

//1650ms

System.out.println(“反射动态调用,执行10亿次,耗时:”+(endTime-startTime)+”ms”);

}

public static void test03() throws Exception{

User user = new User();

Class clazz = user.getClass();

Method m = clazz.getDeclaredMethod(“getUname”, null);

m.setAccessible(true);

long startTime = System.currentTimeMillis();

for(int i=0;i<1000000000L;i++){

m.invoke(user, null);

}

long endTime = System.currentTimeMillis();

//1153ms

System.out.println(“反射动态调用,跳过安全检查,执行10亿次,耗时:”+(endTime-startTime)+”ms”);

}

public static void main(String[] args) throws Exception {

test01();

test02();

test03();

}

}

可以看出在java8中使用安全检查的反射耗时大约是普通调用的4倍,不使用安全检查是普通调用的2.5倍

8.反射操作泛型(Generic)

Java采用泛型擦除机制来引入泛型.Java中泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦.但是,一旦编译完成,所有和泛型有关的类型全部擦除.

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.

ParameterizedType:表示一种参数化类型,比如Collection

GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型

TypeVariable:是各种类型变量的公共父接口

WildcardType:表示一种通配符类型表达式,比如?,? extends Number,? super Integer [wildcard就是通配符的意思]

Demo05 通过反射读取泛型

import java.lang.reflect.Method;

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

import java.util.List;

import java.util.Map;

import com.lorinda.bean.User;

/**

* 通过反射读取泛型

* @author Matrix42

*

*/

public class ReflectionDemo05 {

public void test01(Map map,List list){

System.out.println(“ReflectionDemo05.test02”);

}

public Maptest02(){

System.out.println(“ReflectionDemo05.test2”);

return null;

}

public static void main(String[] args) {

try {

//获取指定方法参数泛型信息

Method m = ReflectionDemo05.class.getMethod(“test01”, Map.class,List.class);

Type[] t = m.getGenericParameterTypes();

for(Type paramType:t){

System.out.println(“#”+paramType);

if(paramType instanceof ParameterizedType){

Type[] genericTypes = ((ParameterizedType)paramType).getActualTypeArguments();

for(Type genericType:genericTypes){

System.out.println(“泛型类型: “+genericType);

}

}

}

/*

#java.util.Map

泛型类型: class java.lang.String

泛型类型: class com.lorinda.bean.User

#java.util.List

泛型类型: class com.lorinda.bean.User

*/

//获得指定方法返回值泛型信息

Method m2 = ReflectionDemo05.class.getMethod(“test02”, null);

Type returnType = m2.getGenericReturnType();

if(returnType instanceof ParameterizedType){

Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();

for(Type genericType:genericTypes){

System.out.println(“返回值,泛型类型: “+genericType);

}

}

/*

返回值,泛型类型: class java.lang.Integer

返回值,泛型类型: class com.lorinda.bean.User

*/

} catch (Exception e) {

e.printStackTrace();

}

}

}

9.反射操作注解

Student类:

package com.lorinda.bean;

import com.demo.util.MField;

import com.demo.util.MTable;

@MTable(“tb_student”)

public class MStudent {

@MField(columnName=”id”,type=”int”,length=10)

private int id;

@MField(columnName=”sname”,type=”varchar”,length=10)

private String studentName;

@MField(columnName=”age”,type=”int”,length=3)

private int age;

public MStudent(int id, String studentName, int age) {

super();

this.id = id;

this.studentName = studentName;

this.age = age;

}

public MStudent() {

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getStudentName() {

return studentName;

}

public void setStudentName(String studentName) {

this.studentName = studentName;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

Table注解:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(value={ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface MTable {

String value();

}

Field注解:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(value={ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

public @interface MField {

String columnName();

String type();

int length();

}

Demo06 通过反射读取注解

import java.lang.annotation.Annotation;

import java.lang.reflect.Field;

public class ReflectionDemo06 {

public static void main(String[] args) {

try {

Class clazz = Class.forName(“com.lorinda.bean.MStudent”);

//获得类的所有有效注解

Annotation[] annotations = clazz.getAnnotations();

for(Annotation a:annotations){

System.out.println(a);

}

//获得类的指定注解

MTable table = (MTable) clazz.getAnnotation(MTable.class);

System.out.println(table.value());

//获得类的属性的注解

Field f = clazz.getDeclaredField(“studentName”);

MField field = f.getAnnotation(MField.class);

System.out.println(field.columnName()+”–“+field.type()+”–“+field.length());

//可以根据获得的表名,字段的信息,拼出DDL语句,然后使用JDBC执行这个SQL,在数据库中生成相关的表

} catch (Exception e) {

e.printStackTrace();

}

}

}



版权声明:本文为weixin_42680139原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。