栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

反射、泛型详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

反射、泛型详解

反射

 Class文件所包含的内容都有其对应的方法可以获得。        

 创建Class对象的3种方式

//方式一  类.class
Class personClass=Person.class;
//方式二  对象.getClass()
Person person=new Person();
Class personClass1=person.getClass();
//方式三  Class.froname("包+类名")
Class personClass2=Class.forName("类的全路径");

三种方式都是对同一个.class文件进行操作

 

  • 想通过反射操作类中的普通方法:使用Method的对象。
  • 想通过反射操作类中的构造方法:使用Constructor的对象。
  • 想通过反射操作类中的属性:使用Field的对象。

 

RTTI在编译时已经知道类的信息;而反射提供了编译时无法知道,却能够在运行时知道类型信息的能力。 

 

使用常用方法创建对象时:
    类名 对象=new 类(参数列表);
    eg:
    Person person=new Person();
  • 此时,该类的构造函数已经存在了,而通过反射来生成对象,需要手动创建构造函数
通过反射来生成对象:
    //1、获得Person类的字节码文件(即.class文件)
    Class personClazz=Class.forName("Person类的全路径");//全路径即:包名+类名
    //2、通过Class对象,创建构造方法对象
    Constructor constructor1=personClazz.getConstructor();//初始化无参构造方法
    Constructor constructor2=personClazz.getConstructor(类型.class,类型.calss,...);//初始化有参构造方法
    //3、通过构造方法创建对象
    Person person1=(person)contructor.newInstance();//调用无参构造方法创建对象
    Person person2=(person)contructor.newInstance (参数1,参数2,...);//调用有参构造方法创建对象
  • 当反射通过私有构造方法创建对象,会破坏单例模式
Class singletonPersonClazz=SingletonPerson.class;
Constructor constructor3=singletonPersonClazz.getDeclareConstructor();//只是可以获得被private修饰的构造方法,但并未拥有其操作权
constructor3.setAccessible(true);//将其设为true,获得被private修饰的方法的操作权。
SingletonPerson songletonPerson=(SingletonPerson)constructor3.newInstence();//通过反射方式获得对象
SingletonPerson songletonPerson1=SingletonPerson.getInstance();//通过单例模式创建对象
SingletonPerson songletonPerson2=SingletonPerson.getInstance();

System.out.println(singletonPerson==singletonPerson1);
System.out.println(singletonPerson==singletonPerson2);
System.out.println(singletonPerson1==singletonPerson2);

输出结果:
    false
    false
    true

SingletonPerson.java

//单例类
public class SingletonPerson{
    private static final SingletonPerson instance=new SingletonPerson();
    
    private SingletonPerson(){
    }
    
    public static SingletonPerson getInstence(){
        return instance;
    }
}
例:采用反射机制来实现一个BeanUtils,可以将一个对象属性相同的值赋给另一个值

BeanUtils.java

package murphy.mianxiangduixiang.fanshe;

import java.lang.reflect.Field;


public class BeanUtils {
    public static void main(String[] args) throws Throwable {
        Person person = new Person("Murphy", 10);
        Person1 person1 = new Person1();
         person1.setName("Murphy");
        BeanUtils.convertor(person, person1);
        System.out.println(person);
        System.out.println(person1);
    }

    public static void convertor(Object originObj, Object targetObj) throws Throwable {
        //第一步:获取Class对象
        Class originClass = originObj.getClass();
        Class targetClass = targetObj.getClass();
        //第二步:获取Field
        Field[] originFields = originClass.getDeclaredFields();
        Field[] targetFields = targetClass.getDeclaredFields();
        //第三步:赋值
        for (Field originField : originFields) {
            for (Field targetField : targetFields) {
                //当两个getName()获取到的属性相同时,将setAccessible方法设置为true,代表使其可以获取到私有的属性
                if (originField.getName().equals(targetField.getName())) {
                    originField.setAccessible(true);
                    targetField.setAccessible(true);
                    //相当于给targetField赋值
                    targetField.set(targetObj, originField.get(originObj));
                }
            }
        }
    }
}

Person.java

//要通过反射获得包名使用Package。
package 包名
//要通过反射获得类名使用ClassName。
public class Person {
    
    private String name;
    private int age;
    
    //要通过反射获得构造方法使用Constructor。获取私有构造方法同上
    public Person() {

    }
    public Person(String name) {
        this.name = name;
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    //要通过反射获得普通方法使用Method。获取私有方法同上
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void eat() {
        System.out.println("吃饭");
    }
    public void walk() {
        System.out.println("走路");
    }
    @Override
    public String toString() {
        return "Person [age=" + age + ", name=" + name + "]";
    }

}

Person1.java

package murphy.mianxiangduixiang.fanshe;

public class Person1 {
    public String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person1 [age=" + age + ", name=" + name + "]";
    }

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

 

泛型类

//作用域为整个类
public class 类名{
    属性;
    构造方法;
    普通方法;
}

泛型方法

public class 类名{
    属性;
    构造方法;
    普通方法;
    //作用域仅为该方法
    public  方法名(T t){
        
    }
}

泛型类派生出的子类

//把泛型定义在接口上
interface Inter{
    void show(T t);
}
//类型一:子类明确泛型类的类型参数变量
class InterImpl1 implements Inter{
    @Override
    public void show(String s){System.out.println(s);}
}
//类型二:子类明确泛型类的类型参数变量,实现类也要定义出T的类型
class InterImpl2 implements Inter{
    @Override
    public void show(T t){System.out.println(t);}
}

类型通配符(不是很重要,但对阅读源码有帮助)

import java.util.ArrayList;
import java.util.List;

public class TypeWildcard {
    private final static List INTEGER_LIST = Lists.newArrayList(1, 2, 3, 4);
    private final static List OBJECT_LIST = Lists.newArrayList("a", 1, 'c', 6.0F, 100L, true);
    private final static List STRING_LIST = Lists.newArrayList("x", "y", "z");

    public static void main(String[] args) {
        TypeWildcard typeWildcard = new TypeWildcard();
        System.out.println("------------test1-------------");
        typeWildcard.test1(INTEGER_LIST);

        System.out.println("n------------test2-------------");
        //注:Object类可以存任何类型数据,但List类,只能接收List!!!!!
        // typeWildcard.test2(STRING_LIST);//会出错
        typeWildcard.test2(OBJECT_LIST);

        System.out.println("n------------test3-------------");
        typeWildcard.test3(INTEGER_LIST);
        typeWildcard.test3(OBJECT_LIST);
        typeWildcard.test3(STRING_LIST);

        System.out.println("n------------test4 end-------------");
        typeWildcard.test4(INTEGER_LIST);
        typeWildcard.test4(OBJECT_LIST);
        typeWildcard.test4(STRING_LIST);

        
        System.out.println("n------------test5 end-------------");
        typeWildcard.test5(INTEGER_LIST);


        
        System.out.println("n------------test6 end-------------");
        // typeWildcard.test6(INTEGER_LIST); //编译错误
    }

    public void test1(List list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }

    public void test2(List list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
    //使用泛型,读写都可以,无限制
    public  void test3(List list) {
        list.add(list.get(0));
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
    //使用类型通配符(?),有读写限制,具体解释在后面
    public void test4(List list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
    //泛型上限
    //只能接收该类型及其子类,
    //不能往里存,只能往外取(只读)
    public void test5(List list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
    //泛型下限
    //只能接收该类型及其父类
    //不影响往里存,但往外取出的数据只能放在Object对象中
    public void test6(List list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
    }
} 

PECS(Producer Extends Consumer Super)

 

  • 当角色为Producer(生产者)时,用Extends,使用场景为读取时,对其写入时会报错。
  • 当角色为Consumer时(消费者),用Super,使用场景为写入时,获取时只能获取Object类型。

类型擦除与桥接方法

  • 泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在编译期就挡住向集合中插入非法数据(即放错误类型的数据代码会有报错)。但编译器编译完带泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受影响,这个过程称之为“擦除”。
  • 由于类型被擦除了,为了维持多态性,所以编译期就自动生成了桥接方法。
  • 为什么要进行类型擦除:为了向下兼容(有些版本没有引入泛型的概念)
//类型擦除前:
public interface Animal{
    void eat(T t);
}
public class Cat implements Animal{
    @Override
    public void eat(String s){
        System.out.println("cat eat"+s);
    }
}

//类型擦除后:
public interface Animal{
    void eat(Object t);
}
public class Cat implements Animal{
    public void eat(String s){
        System.out.println("cat eat"+s);
    }
    //桥接方法,由编译器自动完成
    @Override
    public void eat(Object s){
        eat((String)s);//类型的限制作用
    }
}

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号