类型信息
java提供了两种在运行时识别类型信息的方式:
- RTTI
- 反射
RTTI
RTTI:Run-time type information
面向对象编程的基本目的:
让代码只操作对基类的引用
package com.company;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args){
List<Test1> list = new ArrayList<>();
Collections.addAll(list,new Test2(),new Test3());
Iterator<Test1> iterator = list.iterator();
while (iterator.hasNext()){
iterator.next().print();
}
}
}
interface Test1{
void print();
}
class Test2 implements Test1{
public void print(){
System.out.println("this is Test2");
}
}
class Test3 implements Test1{
@Override
public void print() {
System.out.println("this is Test3");
}
}
把Test2和Test3对象放进类型为Test1的容器中,Test2和Test3向上转型为Test1
当调用容器中的Test1的引用的时候又根据实现不同输出结果不同
使用RTTI可以知道接口引用所指的对象的具体类型,然后就可以对不同的实现进行不同的操作
Class对象
- 要知道RTTI的工作原理,就需要先知道类型信息是怎么表现出来的
- 每个类在编译的时候都会产生一个Class对象在.Class文件中
- 为了生成这个对象,JVM将使用被称为“类加载器”的子系统
- java采用动态加载的方式,new一个对象这个对象就会被挂在类加载器上
- java不是程序启动的时候生成所有的对象并挂在类加载器上
而是在运行过程中new一个对象的时候再把这个对象挂在类加载器上 - java会检查摸个类的Class对象是否加载到类加载器上,如果没有加载,就查找.Class文件找到Class对象
- 加载之后,Class对象在内存中,然后就用这个Class对象创建这个类的所有的对象
Class.forName方法:参数:类名,返回:类的引用
类字面常量
package com.company;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) throws ClassNotFoundException {
System.out.println(Class.forName("com.company.Test2"));
System.out.println(Test2.class);
System.out.println(Test1.class);
System.out.println(String.class);
}
}
interface Test1{
void print();
}
class Test2 implements Test1{
public void print(){
System.out.println("this is Test2");
}
}
class Test3 implements Test1{
@Override
public void print() {
System.out.println("this is Test3");
}
}
使用.class比Class.forName更简单,而且不需要检查异常
同时.class也可用于接口,基本类型,数组
package com.company;
/**
* @ClassName Test
* @Description
* @Auther liuxiansen
* @Date 2020/3/29 8:55 上午
**/
public class Test{
public static void main(String [] args) throws ClassNotFoundException {
System.out.println(Class.forName("com.company.Test2"));
System.out.println(Test2.class.getSimpleName());
System.out.println(Test2.class.getAnnotatedInterfaces());
System.out.println(Test1.class);
System.out.println(String.class);
System.out.println(int.class);
System.out.println(char.class);
System.out.println(Boolean.TYPE);
System.out.println(Character.TYPE);
System.out.println(Integer.TYPE);
}
}
interface Test1{
void print();
}
class Test2 implements Test1{
public void print(){
System.out.println("this is Test2");
}
}
class Test3 implements Test1{
@Override
public void print() {
System.out.println("this is Test3");
}
}
基本类型的.TYPE字段 = 对应包装类的.class
拿到对象的引用之后可以得到对象的父类,接口,类名等一些信息
使用.class创建对象的引用时不会初始化而是:
- 加载:类加载器查找字节码
- 链接:验证字节码,分配存储空间,解析这个类的其他所有引用
- 初始化:如果有超类,对超类初始化
instanceof:X instanceof Y , 判断X是否是Y的子类
泛化Class引用
public static void main(String [] args) {
Class<?> c = int.class;
}
?表示任何事物
即使Class< ?>与Class是等价的,但Class< ?>是更好的,他表示你是选择了非具体的版本
public static void main(String [] args) {
Class<? extends Number> c = int.class;
c = double.class;
c = float.class;
}
结合extends关键字,表示一个范围,Number类的所以子类
- 加入泛型语法是为了可以在编译器检查出错误
反射
用来检查可用的方法,返回方法名
RTTI:编译时检查.class文件查找类
反射:运行时打开和检查.class文件
类方法提取器
反射支持特性:对象序列化,JavaBean
public static void main(String [] args) {
for(Method method : Test2.class.getMethods()){
Matcher matcher = Pattern.compile("(.*)throws").matcher(method.toString());
if(matcher.find()){
System.out.println(matcher.group(1));
} else {
System.out.println(method.toString());
}
}
for(Constructor constructor : Test2.class.getConstructors()){
System.out.println(constructor.toString());
}
}
getMethods:返回类的所有方法的数组
getConstructors:返回类的构造器数组
使用正则表达式删除throws后面的内容