java学习总结一、反射

一、反射定义&为什么要学习反射

1、反射定义

反射(Reflection)指 在编译的时候不需要确定数据的类型,而是延迟到运行时获取类的信息(如类名、方法名、字段名)、调用类的方法、访问或修改类的字段, 无需在编译时 确定这些信息,反射使Java这种静态编译型的语言具有了动态性。
反射具有看透类的能力,类的信息在反射面前都是透明的(包括private的属性和方法都是可以调用)
Java 的反射机制主要由 java.lang.reflect 包提供支持,包括 Class 类、Method 类、Field 类等。通过这些类,可以动态地加载类、创建类实例、调用类的方法、访问和修改类的字段等操作。

2、为什么要学习反射

框架:反射、泛型、注解
1、扩展性和灵活性:通过反射机制,可以实现动态加载类、调用方法、访问字段等操作,从而使程序具有更强的扩展性和灵活性。这在开发框架、插件系统或通用库时非常有用。
2、框架设计:许多 Java 框架(如Spring框架)都广泛使用了反射机制,通过反射可以实现依赖注入、AOP编程等功能。
3、框架源码阅读:许多开源框架和库中广泛使用了反射,学习反射可以帮助更好地理解和阅读这些框架的源码。
4、编写通用工具类:通过反射可以编写一些通用的工具类,比如序列化/反序列化工具、注解处理器、配置文件解析器等。
虽然反射机制有很多优点,但也需要注意反射可能带来的性能开销和安全隐患。因此,在使用反射时需要谨慎考虑,并避免滥用。总的来说,学习反射机制有助于提高 Java 程序员的编程技能和应用开发能力。

3、反射的缺点

尽管反射机制提供了很大的灵活性和扩展性,但也存在一些缺点和不足之处,包括:
1、性能开销:使用反射会导致性能开销较大,因为在运行时需要动态地解析类结构、查找方法或字段等,比直接调用方法要慢很多。这对性能要求高的应用可能不太适用。
2、 编译时检查失效:由于反射是在运行时进行的,因此编译器无法对反射代码进行类型检查,容易引入类型错误,造成运行时异常。
3、安全性问题: 通过反射可以访问和修改类的私有字段和方法,这可能会绕过类的封装性,造成安全隐患,特别是在安全敏感的应用中需要格外小心。
4、可读性和维护性差:使用反射的代码通常比直接调用更难理解和维护,因为它隐藏了类的结构和行为,使得代码更加晦涩难懂。
5、性能优化困难:由于反射是在运行时动态进行的,因此很难进行静态分析和优化,使得程序的性能优化变得更加困难。
6、不利于代码重构:使用反射的代码通常依赖于类的具体实现细节,一旦类结构发生变化,可能导致反射代码出错,不利于代码的重构和演化。
综上所述,虽然反射机制为 Java 程序员提供了很多便利,但在实际应用中需要谨慎使用,权衡利弊。在性能要求高、安全性要求严格或代码可维护性重要的情况下,建议尽量避免过度依赖反射机制,寻找其他更合适的解决方案。

二、反射整体理解

类比学习一下:面向对象抽象过程
  1. 众多的人 ----> Person类
  2. 众多学生 -----> Stundent类
  3. 众多的类 -----> Class类:任何一个类里面都包含这些东西:Field[]、Constructor[]、Method[]
  4. 众多的属性 ------> Field类
  5. 众多构造方法 ------> Constructor类
  6. 众多的普通方法 ------> Method类
private String name;
public Integer age;
class Field{
修饰符; //private
类型; //String
变量名字; // name
}
既然Class是描述类的类的类型,那类结构里面包含哪些东西呢: Field、Constructor、Method,同样这些众多的属性、构造函数、方法也有对应的类类型表示他们。
  1. Java.lang.Class;
  2. Java.lang.reflect.Field;
  3. Java.lang.reflect.Method;
  4. Java.lang.reflect.Constructor;
对于类型的学习我们可以参考做 月饼的模子,什么样模子就可以做出什么大小,图案的月饼。对于Java里面的int类型是四个字节,那么这个模子就是一个只能存放四个字节的模子,用这个模子做出来的就是int类型。

同理Class、Field、Method、Constructor就分别是类、属性、方法、构造函数的模子。

通过Class可以获得类的所有属性Field[]、方法Method[]、构造方法Constructor[]信息。

通过Field可以获得属性的名字、类型、修饰符

通过Method可以获得方法的名字、参数、返回值。

三、反射常见类

1、Class 反射的核心类

每个类加载到内存后,系统都会有唯一的一份字节码对象(Person.class/Student.class字节码对象都是Class这个类的实例)

public void testClass() throws ClassNotFoundException {
   // 1.Class.forName(类路径)
   Class clazz1 = Class.forName("com.situ.day15.Student");
   // 2.类型.class
   Class clazz2 = Student.class;
   // 3.对象.getClass()
   Student student = new Student();
   Class clazz3 = student.getClass();
   
   System.out.println(clazz1 == clazz2);//true
   System.out.println(clazz1 == clazz3);//true
   
}

2、Constructor、Method

public void testConstructor() {
   Class clazz = Student.class;
   Constructor[] constructors = clazz.getDeclaredConstructors();
   for (Constructor constructor : constructors) {
     System.out.println(constructor);
     System.out.println(constructor.getName());//构造方法的名字
     System.out.println(constructor.getModifiers());//修饰符
   }
}

public  com.situ.day15.Student()
com.situ.day15.Student
1
private  com.situ.day15.Student(java.lang.String)
com.situ.day15.Student
2

@Test
public void testConstructor2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class clazz = Student.class;
    //private Student(Integer id, String name, Integer age, String gender)
    Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class, Integer.class, String.class);
    //在反射面前,私有的都可以访问
    constructor.setAccessible(true);
    //Student student = new Student(1, "zhansgan", 23, "男");
    Student student = (Student) constructor.newInstance(1, "zhangsan", 23, "男");
    System.out.println(student);
}
Student{id=1, name='zhangsan', age=23, gender='男'}

public void testMethod() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   //Student student = new Student();
   //student.setName("lisi");
   
   Class clazz = Class.forName("com.situ.day15.Student");
   //得到无参构造方法
   Constructor constructor = clazz.getConstructor();
   //调用newInstance实例化对象
   Student student = (Student) constructor.newInstance();
   //student.setName("lisi");
   Method method = clazz.getMethod("setName", String.class);
   method.invoke(student, "lisi");
   System.out.println(student);
}

四、反射综合案例

练习1

利用反射加配置文件加载指定数据库

public interface IDB {
   public abstract void getConnection();
}
public class MySql implements IDB{
   @Override
   public void getConnection() {
      System.out.println("MySql.getConnection()");
   }
}
public class Oracle implements IDB{
   @Override
   public void getConnection() {
      System.out.println("Oracle.getConnection()");
   }
}
public class SqlServer implements IDB{
   @Override
   public void getConnection() {
      System.out.println("SqlServer.getConnection()");
   }
}

public class DBTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //MySql mySql = new MySql();
        //IDB db = new MySql();
        //db.getConnection();
        //db = new SqlServer();
        //db.getConnection();
        //只出现接口,不出现具体实现类,面向接口编程
        // 反射+配置文件

        //1.通过当前的类获取类加载器
        ClassLoader classLoader = DBTest.class.getClassLoader();
        //2.通过类加载器的方法获取输入流
        InputStream inputStream = classLoader.getResourceAsStream("db1.properties");
        Properties properties = new Properties();
        properties.load(inputStream);
        String className = properties.getProperty("className");
        Class clazz = Class.forName(className);
        //Constructor constructor = clazz.getConstructor();
        //IDB db = (IDB) constructor.newInstance();

        //如果是new无参构造方法,可以直接clazz.newInstance()
        IDB db = (IDB) clazz.newInstance();
        db.getConnection();
    }

    public void db(IDB db) {
        db.getConnection();
    }
}

练习2

写一个函数,传入任意的对象,可以将对象里面String类型的属性中的a改成b。

public class Teacher {
    private String name = "android";
    private int age = 18;
    private String address = "qingdao";
    @Override
    public String toString() {
       return "Teacher [name=" + name + ", age=" + age + ", address="
               + address + "]";
    }
}

@Test
public void test1() throws IllegalAccessException {
    Teacher teacher = new Teacher();
    System.out.println(teacher);
    changeField(teacher);
    System.out.println(teacher);

    Student student = new Student();
    student.setName("lisia");
    System.out.println(student);
    changeField(student);
    System.out.println(student);
}

//动态性体现在传递任意一个对象使用反射都可以完成这个功能。
private void changeField(Object object) throws IllegalAccessException {
    //1.获得对应的Class
    Class clazz = object.getClass();
    //2.获取所有的Field[] fields
    Field[] fields = clazz.getDeclaredFields();
    //3.遍历所有的Field,如果是String类型,将包含的a改成b
    for (Field field : fields) {
        //3.1 如果是String类型才修改
        if (field.getType() == String.class) {
            //属性是私有的
            field.setAccessible(true);
            //student.getName(); 并不是调用getName,因为调用getName()要拿到对应method
            //student.name
            //3.2或者Field原来的值  "qingdao"
            String oldValue = (String) field.get(object);
            if (oldValue != null && !"".equals(oldValue)) {
                String newValue = oldValue.replace("a", "b");
                //3.3将修改后的值设置到Field
                field.set(object, newValue);
            }
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喃寻1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值