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<String> 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<User> 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<String>
- 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<String, User> map,List<User> list){
System.out.println("ReflectionDemo05.test02");
}
public Map<Integer, User>test02(){
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<java.lang.String, com.lorinda.bean.User>
泛型类型: class java.lang.String
泛型类型: class com.lorinda.bean.User
#java.util.List<com.lorinda.bean.User>
泛型类型: 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();
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。