《别看了,你学不会的》——反射与泛型

前言

再介绍反射之前,要了解什么是动态语言什么是静态语言

动态语言
动态语言是一类在运行时可以改变其数据结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。
通俗点说就是在运行时代码可以根据某些条件改变自身结构
举个例子
function test() {
 var x = "var a=3;var b=5;alert(a+b)"; 
 eval(x); 
 }
这是一个叫做test的函数,函数里面定义了一个变量x,它是一个字符串,eval()是接受一个字符串并执行,所以这个函数就是定义了一个字符串然后执行
在函数初始化的时候,x的数据类型是字符串,然后在运行时,eval()方法把x的数据类型由字符串类型改成了整型,也就是说前后x的数据类型改变了,这是动态语言的特点,所以说javascript是一门动态语言

主要的动态语言:object-C、C#、JavaScript、PHP、Python

静态语言
与动态语言相对应的,运行时数据结构不可变的语言就是静态语言。如Java、C、C++

Java不是动态语言,但Java可以称之为“准动态语言”。
Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活

反射

通常在创建对象的时候,在类加载区就会识别对象的数据类型
而反射是在运行期动态地获取对象的数据类型
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在运行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法
在这里插入图片描述

正常方式: 引入需要对象的“包类”名称————通过new实例化————取得实例化对象
反射方式: 实例化对象————getClass()方法————得到完整的“包类”名称

Class clazz = person.getClass();

JVM类的加载流程和内存结构
在这里插入图片描述
class文件包含的内容
在这里插入图片描述
反射关键类图
在这里插入图片描述
生成对象的步骤对比
在这里插入图片描述
通过反射创建对象的3种方式

public class Person {

    public String name = "com/reflect";

    protected Integer age = 1;

    private Byte sex = (byte) 1;

    Boolean isMarriage = true;

    // 无参数
    public Person() {
    }

    // 有参数
    public Person(String name, Integer age, Byte sex, Boolean isMarriage) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.isMarriage = isMarriage;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Byte getSex() {
        return sex;
    }

    public void setSex(Byte sex) {
        this.sex = sex;
    }

    public Boolean getMarriage() {
        return isMarriage;
    }

    public void setMarriage(Boolean marriage) {
        isMarriage = marriage;
    }

    private String privateMethod() {
        return "This is private method!";
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", isMarriage=" + isMarriage +
                '}';
    }
}
public class Person1 {

    public String name;

    protected Integer age;

    private Byte sex;

    Boolean isMarriage1;

    public Person1() {
    }

    public Person1(String name, Integer age, Byte sex, Boolean isMarriage1) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.isMarriage1 = isMarriage1;
    }

    public Boolean getMarriage1() {
        return isMarriage1;
    }

    public void setMarriage1(Boolean marriage1) {
        isMarriage1 = marriage1;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Byte getSex() {
        return sex;
    }

    public void setSex(Byte sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", isMarriage1=" + isMarriage1 +
                '}';
    }
}

单例类

public class SingletonPerson {

    private static final SingletonPerson instance = new SingletonPerson();

    private SingletonPerson(){
    }

    public static SingletonPerson getInstance(){
        return instance;
    }
}

测试类

  public void test() throws Throwable{
        // 方式一  类.class
        Class personClazz = Person.class;

        // 方式二  实例.getClass()
        Person person = new Person();
        Class personClazz1 = person.getClass();

        // 方式三  Class.forName("类的全路径")
        Class personClazz2 = Class.forName("com.reflect.Person");

        System.out.println(personClazz == personClazz1);
        System.out.println(personClazz == personClazz2);
    }

通过Class创建实例对象

 // 无参数
 <bean id="person" class="com.reflect.Person" />
      
 // 有参数
 <bean id="person" class="com.reflect.Person" >
       <constructor-arg index="0" type="java.lang.String" value="yang"/>
 </bean>

 public void test2() throws Throwable{
        /** 首先:获得Person的字节码 */
        Class personClazz = Class.forName("com.reflect.Person");

        /** 其次:通过Class对象,创建构造方法对象 */
        Constructor constructor1 = personClazz.getConstructor(); // 初始化无参构造方法
        Constructor constructor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class, Boolean.class); // 初始化有参构造方法对象

        /** 最后:通过构造方法创建对象 */
        // 调用无参数构造方法创建Person对象
        Person person1 = (Person) constructor1.newInstance();
        person1.setName("yang1");
        System.out.println("person1=" + person1);

        // 调用有参数构造方法创建Person对象
        Person person2 = (Person) constructor2.newInstance("yang2", 10, (byte) 1, true);
        System.out.println("person2=" + person2);

        /** 补充内容:反射通过私有构造方法创建对象,破坏单例模式 */
        Class singletonPersonClazz = SingletonPerson.class;
        // Constructor constructor3 = singletonPersonClazz.getConstructor();
        Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
        constructor3.setAccessible(true);
        SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
        SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
        SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
    }

通过反射获取public属性的Field

public void test3() throws Throwable{
        // 第一步:获得Class
        Class personClazz = Person.class;

        // 第二步:获得构造方法
        Constructor<Person> constructor = personClazz.getConstructor();
        Person person = constructor.newInstance(); // 初始化生成Person对象

        // 第三步:通过Class对象,获得Field对象  Person[类]的name属性。
        Field nameField = personClazz.getField("name");

        // 第四步:操作Field,获得属性值
        String name = String.valueOf(nameField.get(person));

        System.out.println(name);
    }

通过反射获取private属性的Field

 public void test4() throws Throwable{
        // 第一步:获得Class
        Class personClazz = Person.class;

        // 第二步:获得构造方法
        Constructor<Person> constructor = personClazz.getConstructor();
        Person person = constructor.newInstance();

        // 第三步:通过Class对象,获得Field对象
        Field sexField = personClazz.getDeclaredField("sex");
        //setField为true时才能访问到private属性
        sexField.setAccessible(true);

        // 第四步:操作Field,获得属性值
        System.out.println(sexField.get(person));
    }

通过反射获取protect属性

    public void getProtectedField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field ageField = personClazz.getField("age"); // java.lang.NoSuchFieldException: age
        Field ageField = personClazz.getDeclaredField("age");
        ageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest
        // can not access a member of class com.muse.Person with modifiers "protected"

        /** 最后:获取字段的类型 */
        Integer age = (Integer) ageField.get(person);
        System.out.println(age);
    }

通过反射获得default属性

    public void getDefaultField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field isMarriageField = personClazz.getField("isMarriage"); // java.lang.NoSuchFieldException: isMarriage
        Field isMarriageField = personClazz.getDeclaredField("isMarriage");
        isMarriageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers ""

        /** 最后:获取字段的类型 */
        Boolean isMarriage = (Boolean) isMarriageField.get(person);
        System.out.println(isMarriage);
    }


}

采用反射机制来实现一个工具BeanUtils, 可以将一个对象属性相同的值赋值给另一个对象

public class BeanUtils {

    public static void convertor(Object originObj, Object targetObj) throws Throwable {
        // 第一步,获得class对象
        Class orginClazz = originObj.getClass();
        Class targetClazz = targetObj.getClass();

        // 第二步,获得Field
        Field[] orginFields = orginClazz.getDeclaredFields();
        Field[] targetFields = targetClazz.getDeclaredFields();

        // 第三步:赋值
        for (Field originField : orginFields) {
            for (Field targetField : targetFields) {
                if (originField.getName().equals(targetField.getName())) {
                    originField.setAccessible(true);
                    targetField.setAccessible(true);
                    targetField.set(targetObj, originField.get(originObj));
                }
            }
        }
    }

    public static void main(String[] args) throws Throwable{
        // Service层返回的
        Person person = new Person("com/muse", 10, (byte)1, true);

        // 需要返回实体对象
        Person1 person1 = new Person1();

        BeanUtils.convertor(person, person1);

        System.out.println("person" + person);

        System.out.println("person1" + person1);

    }
}

泛型

泛型的本质是指数据类型参数化
允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型
所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认时object类型
集合体系中所有类都增加了泛型,泛型也主要用在集合

泛型类

public class ClassGenericity {
    public static void main(String[] args) {
        /** 创建ObjectTool对象并指定元素类型为String */
        ObjectTool<String> stringTool = new ObjectTool<>();
        stringTool.setObj("yang");
        System.out.println(stringTool.getObj());

        /** 创建ObjectTool对象并指定元素类型为Integer */
        ObjectTool<Integer> integerTool = new ObjectTool<>();
        // integerTool.setObj("muse"); // 编译报错
        integerTool.setObj(10);
        System.out.println(integerTool.getObj());
    }

    /**
     * 构建可以存储任何类型对象的工具类
     */
    static class ObjectTool<T> {
        private T obj;
        public T getObj() {
            return obj;
        }
        public void setObj(T obj) {
            this.obj = obj;
        }
    }
}

泛型方法

public class MethodGenericity<T> {
    public static void main(String[] args) {
        //创建对象
        ObjectTool tool = new ObjectTool();

        /** 调用方法,传入的参数是什么类型,T就是什么类型 */
        tool.show("hello");
        tool.show(12);
        tool.show(12.5f);
    }

    static class ObjectTool {
        //定义泛型方法
        public <T> void show(T t) {
            System.out.println(t);
        }
    }
}

泛型测试

    public void test(List<Object> list) { // List<Object> 和 Object
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }

泛型定义的是List< Object>类型,那么传递的参数数据类型也要是OBJECT_LIST类型

通过通配符限制泛型的类型
泛型的上限:只能接收Number或者Number的子类
格式:类型名称<? extends 类>对象名

    public void test(List<? extends Number> list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }

泛型的下限:只能接收该类型及其父类型

  public void test(List<? super Number> list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
泛型的擦除与桥接方法

泛型是提供给Javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受到影响,这个过程称之为==“擦除”==

由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法

泛型接口

public interface Animal<T> {
    void eat(T t);
}

泛型擦除

public class Cat implements Animal<String> {
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

桥接方法

public class BridgeMethodTest {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.eat(new Object());
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值