class文件包含的内容
反射
获得Class文件的三种方式 以com.snowf.Person为例
com.snowf.Person
public class Person {
public String name = "muse";
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", isMarriage=" + isMarriage +
'}';
}
}
方式一:Class personClazz = Person.class
方式二:Person person = new Person();Class personClazz = person.getClass();
方式三:Class personClazz = Class.forName("com.snowf.Person");
利用反射去创建对象 以com.snowf.Person为例
/** 首先:获得Person的字节码 */
Class personClazz = Class.forName("com.snowf.Person");
/** 其次:通过Class对象,创建构造方法对象 */
Constructor constructor1 = personClazz.getConstructor(String.class, Integer.class, Byte.class,
Boolean.class); // 初始化有参构造方法对象
Constructor constructor = personClazz.getConstructor(); // 初始化无参构造方法
/** 最后:通过构造方法创建对象 */
// 调用无参数构造方法创建Person对象
Person person = (Person) constructor.newInstance();
person.setName("muse1");
System.out.println("person=" + person);
// 调用有参数构造方法创建Person对象
Person person1 = (Person) constructor1.newInstance("muse1", 10, (byte) 1, true);
System.out.println("person1=" + person1);
反射有关的方法
- 获取属性的两种方法
- getField 只能获取public的,包括从父类继承来的字段。
- getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
- 获取属性值的步骤
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象
(由于非静态非private的属性,使用(对象.属性)方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
Field nameField = personClazz.getField("name");
- 属性有关方法
System.out.println("获取字段的类型:" + nameField.getType());
System.out.println("获取字段的名字:" + nameField.getName());
System.out.println("获取字段的访问修饰符:" + Modifier.toString(nameField.getModifiers()));
System.out.println("获取字段所在类的全路径:" + nameField.getDeclaringClass().getName());
反射的应用
我们采用反射机制来实现一个工具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));
}
}
}
}
泛型
- 泛型的本质是指类型参数化
- 允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型
- 所有使用该泛型参数的地方都被统一化,保证类型已知,如果未指定具体类型,默认是Object类型。集合体系的所有类都增加了泛型,泛型也主要用在集合
泛型类
static class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
泛型方法
public <T> void show(T t) {
System.out.println(t);
}
泛型的上下限
- 上限
- 格式上限: 类型名称<? extends 类> 对象名称
- 格式意义: 只能接受该类型及其子类
/** * 【读取】 * 如果要从集合中读取类型T的数据,并且不能写入, * 可以使用 ? extends 通配符;(Producer Extends) */ public void testPECSextends() { List<Dog> dogs = Lists.newArrayList(); dogs.add(new Dog()); List<? extends Animal> animals = dogs; /** * animals是一个Animal的子类的List,由于Dog是Animal的子类, * 因此将dogs赋给animals是合法的,但是编译器会阻止将new Cat()加入animals。 * 因为编译器只知道animals是Animal的某个子类的List,但并不知道究竟是哪个子类, * 为了类型安全,只好阻止向其中加入任何子类。 * 事实上,不能够往一个使用了? extends的数据结构里写入任何的值。 */ // animals.add(new Cat()); // 编译失败 // animals.add(new Animal()); // 编译失败 // animals.add(new Dog()); // 编译失败 /** * 由于编译器知道它总是Animal的子类型,因此我们总可以从中读取出Animal对象: */ Animal animal = animals.get(0); // Dog dog = animals.get(0); // 编译失败 } - 下限
- 格式下限: 类型名称<? super 类> 对象名称
- 格式意义: 只能接受该类型及其父类
/** * 【写入】 * 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super) * <p> * 如果既要存又要取,那么就不要使用任何通配符。 */ public void testPECSsuper() { List<Animal> animals = Lists.newArrayList(); List<? super Dog> dogs = animals; /** * 这里的animals是一个Animal的超类(父类,superclass)的List。 * 同样地,出于对类型安全的考虑,我们可以加入Dog对象或者其任何子类(如WhiteDog)对象, * 但由于编译器并不知道List的内容究竟是Dog的哪个超类,因此不允许加入特定的任何超类型。 */ dogs.add(new Dog()); dogs.add(new WhiteDog()); // dogs.add(new Animal()); // 编译失败 // dogs.add(new Cat()); // 编译失败 // dogs.add(new Object()); // 编译失败 /** * 而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象, * 因为Object是任何Java类的最终祖先类。 */ Object obj = dogs.get(0); // Dog dog = dogs.get(0); // 编译失败 // Animal animal = dogs.get(0); // 编译失败 }类型擦除和桥接方法
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法。