反射:
在程序运行时动态的获取类中的属性方法等内容的机制,叫反射。
反射中要用到Class类
获取Class类对象的三种方式
1、类名.class
User.class
2、对象.getClass()
3、Class.forName("全类名");
获取类的内容
1、包 class.getPackage();
2、类名 class.getSimpleName();
3、全类名 class.getName();
4、获取属性
公共属性(本类及父类) class.getFields(); Fields[]
所有属性 (本类) class.getDecalaredFields();
修饰符:field.getModifiers(); //返回int值,Modifier.toString(field.getModifiers());
类型: field.getType();
属性名:field.getName();
获取指定的公共属性:field.getField("属性名");
5、获取方法
公共方法 (本类及父类) class.getMethods();
所有方法 (本类) class.getDecalaredMethods();
修饰符:method.getModifiers();
返回值类型:method.getReturnType();
方法名:method.getName();
方法的参数类型: method.getParameterType();
获取指定的公共方法对象:class.getMethod("eat",可变参数类型);
获取指定的私有方法对象:class.getDecalaredMethod("run",可变参数类型);
Method method = class.getMethod("eat",可变参数类型);
调用执行:method.invoke("被执行方法所属对象",具体参数);
---------------------------------------------------------------------------------------------------------------------------------
反射
1. 反射定义
反射即反向探知,有点像考古学家发掘的物品来探知以前的事情。
程序中的反射指程序运行状态中 :
1. 对于给定的一个类(class)对象,可以获得这个类(class)对象所有属性和方法;
2. 对于给定的一个对象(new xxxClassName<? extends Object>), 都能够拥有它的任意一个属性和方法。
这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做 Java反射机制
2. 反射初始
public class User {
public void eat(){
System.out.println("---eat---");
}
}
public class TestReflect{
public static void main(String[] args) throws Exception {
Class<User> clazz = User.class;
//获取类对象对应的属性和方法
System.out.println(clazz.getName());
System.out.println(clazz.getPackage());
System.out.println(clazz.getSuperclass());
System.out.println(clazz.getClassLoader());
//获取一个对象实例
User user = clazz.newInstance();
Method method = clazz.getDeclaredMethod("eat");
method.invoke(user);
}
3. 为何用反射
直接 new User()不就可以了,为什么要用这种反射写法?
实例化一个User()对象,不使用反射,如果想再实例化其他对象比如 new Person(),那么就需要修改源代码,并且重新编译,使用反射后就不需要修改源代码只需要灵活改动类描述就可以了,不需要重新再编译。如下代码所示:
//类描述,可根据需要实例化的描述而改动
String className = "com.tledu.pojo.User";
//使用反射中API创建对象
Class.forName(className).newInstance();
4. 反射的优缺点
优点:
增加程序的灵活性,避免固有逻辑写死的程序中
代码相对简洁,可以提高程序的复用性
缺点:
相比于直接调用反射有比较大的性能销毁
内部暴露和安全隐患
4.1 灵活性测试
public interface Ball {
void playBall();
}
public class BasketBall implements Ball{
@Override
public void playBall() {
System.out.println("打篮球");
}
}
public class FootBall implements Ball{
@Override
public void playBall() {
System.out.println("踢足球");
}
}
//----------------------------
public static void main(String[] args) {
//System.out.println(getInstanceByKey("basket"));
System.out.println(getInstanceReflectByKey("FootBall"));
}
public static Ball getInstanceByKey(String key) {
if("foot".equals(key)){
return new FootBall();
}
if("basket".equals(key)){
return new BasketBall();
}
return null;
}
如果我们想在添加一个乒乓球那么除了创建一个乒乓球类,还需要在方法中加入获取乒乓球对应的判断和乒乓球创建对象的返回,很显然不够灵活出现了写死代码的情况。
public static Ball getInstanceReflectByKey(String key) {
String basePackage = "com.tledu.mjw";
Ball ball = null;
try {
Class clazz = Class.forName(basePackage + "." + key);
ball = (Ball)clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return ball;
}
使用反射的方式获取对象,可以看出代码灵活了很多而且不需要改动创建返回对象的源码。
4.2 性能测试
public static void main(String[] args) {
//System.out.println(getInstanceByKey("basket"));
//System.out.println(getInstanceReflectByKey("FootBall"));
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++){
getInstanceByKey("basket");
//getInstanceReflectByKey("FootBall");
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
}
为什么这么慢呢?(分别查看forName()方法和newIntance()方法源码分析)
1、调用了native方法
2、每次调用newInstance()方法都会做安全检查,比较耗时
5. 反射的操作
5.1 获取类对象的四种方式
//获取类对象的四种方式
Class<User> clazz1 = User.class;
Class<?> clazz2 = Class.forName("com.tledu.mjw.User");
Class<? extends User> clazz3 = new User().getClass();
Class<?> clazz4 = Demo3.class.getClassLoader().loadClass("com.tledu.mjw.User");
5.2 类中基本信息的获取
System.out.println(clazz1.getClassLoader());
System.out.println(clazz1.getSuperclass());
System.out.println(clazz1.getPackage());
System.out.println(clazz1.getModifiers()); //获取类的修饰符
System.out.println(clazz1.getName());
System.out.println(clazz1.getSimpleName());
System.out.println(clazz1.getInterfaces().length);//获取类实现的所有接口
System.out.println(clazz1.getAnnotations().length);
5.3 类中字段的操作
创建User类继承Person类添加相应属性
public class Person {
public String idCard;
private String userName;
}
public class User extends Person{
private String name;
public String sex;
public static String address;
public void eat(){
System.out.println("---eat---");
}
}
测试方法调用
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
//getFields()获取的是本类及父类中的公共属性
Field[] fields = userClass.getFields();
for (Field field : fields) {
System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("---------------");
//getDeclaredFields()获取本类中的所有属性
Field[] fields2 = userClass.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("---------------");
//获取name字段对应的field
Field nameField = userClass.getDeclaredField("name");
//如果需要修改私有属性信息那么我们要放开权限
nameField.setAccessible(true);
nameField.set(user,"天亮教育");
System.out.println(nameField.get(user));
//System.out.println(user.getName());
System.out.println("---------------");
//如何对静态的属性赋值
Field addressField = userClass.getDeclaredField("address");
addressField.set(null,"Jack");
System.out.println(addressField.get(null));
//System.out.println(User.address);
}
5.4 类中方法的操作
在User类中将eat方法改为私有的,并添加静态方法 say
private void eat(){
System.out.println("---eat---");
}
public static void say(String msg){
System.out.println(msg);
}
在Person类中添加共有的fn1()方法和私有的fn2()方法
public void fn1(){}
private void fn2(){}
测试方法调用
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
//获取本类及父类中的公共方法
Method[] methods1 = userClass.getMethods();
for (Method method : methods1) {
System.out.println(method.getModifiers() + " " + method.getName());
}
System.out.println("--------------");
//获取本类中所有的方法包括私有
Method[] methods2 = userClass.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method.getModifiers() + " " + method.getName());
}
System.out.println("--------------");
//调用方法执行
Method eatMethod = userClass.getDeclaredMethod("eat");
//调用私有方法时要先开放权限
eatMethod.setAccessible(true);
eatMethod.invoke(user);
System.out.println("--------------");
//调用静态的方法
Method sayMethod = userClass.getDeclaredMethod("say",String.class);
sayMethod.invoke(null,"你好");
}
5.5 类中构造器的操作
在User中添加如下构造方法
public User() {
}
public User(String name) {
this.name = name;
}
private User(String name,String sex) {
this.name = name;
this.sex = sex;
}
通过反射操作User中的构造方法
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
//获取公共的构造器
Constructor<?>[] constructors = userClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getModifiers() + " " + constructor.getName());
}
System.out.println("------------------");
//获取所有的构造器
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getModifiers() + " " + declaredConstructor.getName());
}
//1、直接通过newInstance创建对象
User user = userClass.newInstance();
//2、获取对应的Constructor对象获取实例
Constructor<User> constructor = userClass.getDeclaredConstructor(String.class, String.class);
//操作私有的构造器要先打开权限
constructor.setAccessible(true);
User user1 = constructor.newInstance("乔森", "男");
}
6. 反射破局单例
单例模式
public class PersonSingle {
private static PersonSingle instance = null;
private PersonSingle(){}
public static PersonSingle getInstance(){
if(instance == null){
instance = new PersonSingle();
}
return instance;
}
}
测试单例、通过反射可以调用私有构造
public static void main(String[] args) throws Exception {
PersonSingle instance1 = PersonSingle.getInstance();
PersonSingle instance2 = PersonSingle.getInstance();
PersonSingle instance3 = PersonSingle.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
System.out.println("---------------------");
Class<PersonSingle> personSingleClass = PersonSingle.class;
Constructor<PersonSingle> declaredConstructor = personSingleClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance());
}
改进单例如下
private PersonSingle(){
if(instance != null){
throw new RuntimeException("实例已经存在不允许再创建")
}
7. 反射使用场景
1、jdbc封装
2、SpringIOC
3、JdbcTemplate
4、Mybatis
...................