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);

            }

        }

    }

}

Teacher [name=android, age=18, address=qingdao]

bndroid

qingdbo

Teacher [name=bndroid, age=18, address=qingdbo]

java.lang.IllegalAccessException: Class com.situ.day14.ReflactDemo can not access a member of class com.situ.day14.Teacher with modifiers "private"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值