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
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);//类型的限制作用 } }



