泛型 ※
反射 ※※
容器 ※※※
J V M 类 加 载 流 程 和 内 存 结 构:
反射的类在运行期才会确认。
1. 创建class对象的三种方式①
(1)类.class 前提:知道要操作的类是什么
(2)实例.getClass() 前提:已经有实例
(3)Class.forName(“类的全路径”) 常用
2. class文件包含的内容
几乎包含了所有类相关的信息,如:类名、包名、属性、方法等。class对象其实就是我们获得的某一个类型字节码的引用对象。
3. 创建实例对象的步骤对比
常规方式 Person person = new Person(); |
通过反射创建Person实例对象 | 反射创建对象举例 |
编译期加载Class文件 Person.class |
运行期加载Class文件 Person.class |
Class personClazz = Class.forName(com.xx.Person) |
寻找入参匹配的构造函数 public Person(…) {} |
寻找入参匹配的构造函数 public Person(…) {} |
Constructor constructor = personClazz.getConstructor(); 或者 = personClazz.getConstructor(String.class); |
通过构造函数创建对象 person |
通过构造函数创建对象 person |
Person person = (Person) constructor.newInstance(); 相应地: (Person) constructor.newInstance("xxx"); |
4. 反射通过私有构造方法创建对象,破坏单例模式
getDeclared… 可以获得任意访问权限的对象
Class singletonPersonClazz = SingletonPerson.class;
// 返回public的
// Constructor constructor3 = singletonPersonClazz.getConstructor();
// 返回任何修饰符的构造器,但只是获得构造器,并没有权限去操作。
Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
// 需要操作的话,要将构造器对象进行以下设置
constructor3.setAccessible(true);
SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
System.out.println("singletonPerson==singletonPerson1 is " + (singletonPerson == singletonPerson1));
System.out.println("singletonPerson==singletonPerson2 is " + (singletonPerson == singletonPerson2));
System.out.println("singletonPerson1==singletonPerson2 is " + (singletonPerson1 == singletonPerson2));
运行结果:
5. 通过反射获得类的public属性值
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
// Field nameField = personClazz.getField("name"); // public java.lang.String com.muse.reflect.Person.name
System.out.println(nameField);
/** 最后:获取字段的类型 .get(person)意为获得某个对象的name属性值*/
String name = String.valueOf(nameField.get(person));
System.out.println(name);
6. getDeclaredField无法获得父类中的变量值
// 前提:Student 类继承了 Person类,但Student是一个空类,没有任何属性,
System.out.println("---------getDeclaredField无法获得父类中的变量---------");
Class sutdentClazz = Student.class;
Student student = (Student) sutdentClazz.getConstructor().newInstance();
Field field = sutdentClazz.getField("name"); // 可以获得父类的name值
// Field field = sutdentClazz.getDeclaredField("name"); // 报错,没有name属性
name = String.valueOf(field.get(student));
System.out.println(name);
7. 获得非public属性和方法:.getDeclared 和 .setAccessible(true)
public void getPrivateField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
// Field sexField = personClazz.getField("sex"); // 不能使用getField,否则报错:java.lang.NoSuchFieldException: sex
Field sexField = personClazz.getDeclaredField("sex");
sexField.setAccessible(true); // 必须设置为true
/** 最后:获取字段的类型 */
Byte sex = (Byte) sexField.get(person);
System.out.println("private属性:sex=" + sex);
/** 补充内容:获取private类型的方法 */
// Method method = personClazz.getMethod("privateMethod");
// 不能使用getMethod,否则报错:java.lang.NoSuchMethodException: com.muse.reflect.Person.privateMethod()
Method method = personClazz.getDeclaredMethod("privateMethod");
method.setAccessible(true); // 必须设置为true
System.out.println("private方法:privateMethod()=" + method.invoke(person));
}
总结:
getDeclared 只能获得本类的属性或方法;不加declared,直接用get可以获得父类的属性或方法;
getdeclared用于获得本类的非public属性或方法,获得后进行进一步的操作需要.setAccessible(true)
8. 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);
}
9. 双亲委派机制 SPI
spi是增强扩展能力。
作用、类型擦除、桥接方法
早期Java是使用object来代表任意类型的,但是向下转型有强转的问题,这样程序并不安全。
针对list、set、map等集合类型,他们对存储的元素类型是没有任何限制的。假如向list中存储Dog类型的对象,但是有人把Cat对象也存储到这个list中了,那么在编译上是没有任何语法错误的。
所有使用该泛型参数的地方都被统一化,保证类型一致。如果未制定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。
把泛型定义在类上,用户使用该类的时候,才把类型明确下来。这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题和运行时转换异常的问题了。
public static void main(String[] args) {
/** 创建ObjectTool对象并指定元素类型为String */
ObjectTool stringTool = new ObjectTool<>();
stringTool.setObj("muse");
System.out.println(stringTool.getObj());
/** 创建ObjectTool对象并指定元素类型为Integer */
ObjectTool integerTool = new ObjectTool<>();
// integerTool.setObj("muse"); // 编译报错
integerTool.setObj(10);
System.out.println(integerTool.getObj());
}
/**
* 构建可以存储任何类型对象的工具类
*/
static class ObjectTool {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
运行结果:
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 void show(T t) {
System.out.println(t);
}
}
运行结果:
前边定义了泛型类,泛型类是拥有泛型这个特性的类,本质上还是一个Java类,即可以被继承或实现。分为两种情况:
(1)子类明确泛型类的类型参数变量
(2)子类不明确泛型类的类型参数变量
public static void main(String[] args) {
// 测试第一种情况
Inter i = new InterImpl1();
i.show("hello");
// 编译错误
// Inter ii = new InterImpl1();
// ii.show(1);
// 第二种情况测试
Inter iii = new InterImpl2();
iii.show("100");
Inter ii = new InterImpl2();
ii.show(1);
}
}
/**
* 把泛型定义在接口上
*/
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);
}
运行结果:
List>表示元素类型未知的list,它可以匹配任何类型的元素。声明List> list后,不能向集合中添加元素,因为无法确定集合的元素类型,唯一例外的是null。
(1)泛型的上限
格式:类型名称 extends 类> 对象名称
意义:只能接收该类型及其子类
(2)泛型的下限
格式:类型名称 super 类> 对象名称
意义:只能接收该类型及其父类型
泛型是提供给javac编译器使用的,它可以作为类型的限制,让编译器在源代码级别上,挡住非法类型的数据。但是在JDK1.5之前没有泛型的概念,为了能够与之前版本代码兼容,编译器编译完带有泛型的Java程序后,生成的class字节码文件中将不再带有泛型信息,这个过程为“擦除”。
jdk1.5引入泛型后,为使Java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的。可用method.isBridge()判断method是否是桥接方法。