问题一:类和对象的关系?
答曰:类是抽象的概念,它是具有相同属性和方法的一组对象集合,它代表着事物的模板;而对象是能够真正“感觉的到、看得见,摸得着的”具体的实体。对对象的抽象便是类,而类的实例化结果便是对象。
问题二:有个可能不恰当的问法:对象的抽象是类,那类的抽象用什么表示?
java.lang.Class
,该类是用来描述类的类(比较拗口),为了帮助理解,直接上图:Dog
类、Shape
类;Dog
类,正方形、三角形的类型是Shape
类;Dog
类和Shape
类的类型是什么类?答案便是Class
类,它用来描述类的类型。以上图Dog
类入手,创建一个Dog
类以及一个测试类。
//Dog类,是用来描述各个品种的狗的的类,
public class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat() {
System.out.println("我的名字是:" + getName() + ",我开始吃饭了!");
}
}
class DogTest{
public static void main(String[] args) {
//huskie、teddy是Dog类的具体实例。
Dog huskie = new Dog();
huskie.setName("哈士奇");
huskie.eat();
Dog teddy = new Dog();
teddy.setName("泰迪");
teddy.eat();
}
}
过程解读:
Dog
类经过javac.exe命令以后,会生成Dog.class
字节码文件;Dog.class
字节码文件进行解释执行,即加载到内存中,该过程称为类的加载;Dog.class
类时,JVM为其创建一个Class类型的实例,并将其关联起来。也就是说:当Dog.class
加载到内存中时,JVM为其创建了一个Class实例,这个Class实例包含了该class类型的所有完整信息。那么只要能得到某个类的Class实例,那么便可以拿到该类的类名、包名、父类、实现的接口、所有方法、字段等等。注意:
Class
实例在JVM中是唯一的,因此无论通过何种方式获取到的Class实例都是同一个实例。class
文件会产生不同的Class
实例,加载Dog.class
时,JVM会为Dog.class
创建一个Class
类型的实例, 加载Shape.class
时,JVM会为Shape.class
创建一个Class
类型的实例。方便个人理解,直接上图。
我们对Dog.class
字节码文件进行解释执行,加载到内存中,并且将这些静态数据转换成方法区的运行时的数据结构,之后会在堆中生成一个代表这个类的java.lang.Class
对象,用来封装类在方法区内的数据结构,可以作为方法区中类数据的访问入口(好像JDK版本不同,Class对象所在的位置也不同)。
Class
实例,Class
实例上保存了这个类的所有完整信息,那么,通过Class
实例获取class
信息的方法便称为反射(Reflection);Dog.class
有Dog
类的Class
类型的实例,Shape.class
有Shape
类Class
类型的实例。这些Class类型的实例跟踪着每个对象所属的类。这些Class类型的实例保存这些类的完整信息。class
类的关系:class是描述类的一个关键字。Class却是保存着运行时信息的类。Class
与反射配套使用,因为Class类能够帮助我们在程序运行时分析类,获取运行时类中的值。Class
实例后,我们在程序运行时便可以做到:
Class
对象的类型有:
Dog
类为例。//方式一:直接调用运行时类的属性:.class;
Class clazzOne = Dog.class;
System.out.println("方式一获取到的Class:" + clazzOne);
//方式二:使用运行时类的对象,调用getClass()方法;
Dog dog1 = new Dog();
Class clazzTwo = dog1.getClass();
System.out.println("方式二获取到的Class:" + clazzTwo);
//方式三:直接使用Class的静态方法:forName(String classPath);
Class clazzThree = Class.forName("com.practice.reflect.Dog");
System.out.println("方式三获取到的Class:" + clazzThree);
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = DogTest.class.getClassLoader();
Class clazzFour = classLoader.loadClass("com.practice.reflect.Dog");
System.out.println("方式四获取到的Class:" + clazzFour);
//比较获取到的是否是同一个Class
Boolean bool = (clazzOne == clazzTwo) && (clazzTwo == clazzThree) && (clazzThree == clazzFour);
System.out.println(bool);
public static void main(String[] args) {
//拿到DogTest类的类加载器,自定义的类是系统类加载器进行加载
ClassLoader classLoaderOne = DogTest.class.getClassLoader();
System.out.println("系统类加载器classLoaderOne:" + classLoaderOne);
//获取一个系统类加载器
ClassLoader classLoaderOne1 = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器classLoaderOne1:" + classLoaderOne1);
System.out.println(classLoaderOne1 == classLoaderOne);
//系统类加载器可以以流的方式创建一个资源 文件目录在当前src下
InputStream is = classLoaderOne1.getResourceAsStream("jdbc.properties");
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoaderTwo = classLoaderOne.getParent();
System.out.println("扩展类加载器classLoaderTwo:" + classLoaderTwo);
//调用扩展类加载器的getParent():无法获取引导类加载器
//引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
ClassLoader classLoaderThree = classLoaderTwo.getParent();
System.out.println("引导类加载器classLoaderThree:" + classLoaderThree);
}
获取到Class类的实例后,我们可以创建运行时类的对象;比如通过Dog.class获取到对应的Class实例后,创建一个Dog对象名为Labrador拉布拉多犬。
//通过Dog.class拿到Class对象后
Class clazz = Dog.class;
//使用Class的newInstance()方法创建对象,它实际上是调用了运行时类的空参的构造器
//使用条件: 1.运行时类必须提供空参的构造器 2.空参的构造器的访问权限得够,通常为public。
Dog Labrador = (Dog)clazz.newInstance();
//也可以通过构造器创建 有这样的构造器:Dog(String name)
Constructor constructor = clazz.getConstructor(String.class);
Dog d = (Dog)constructor.newInstance("Labrador");
首先拿到Class
实例。
Class clazz = Dog.class;
通过Class拿到类的属性
//获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
//获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
通过上面拿到的属性进而拿到这个属性的其他信息
getModifiers()
:返回属性的修饰符,是一个int
;getType()
:返回字段类型,也是一个Class
实例;getName()
:返回属性名称; Field[] fields1 = clazz.getDeclaredFields();
for (Field field : fields1) {
//以 private String name; 为例
//返回权限修饰符 private
int m = field.getModifiers();
System.out.print(Modifier.toString(m) + "\t");
//返回该字段的类型 String
Class s = field.getType();
System.out.print(s.getName() + "\t");
//返回该字段的名字 name
String name = field.getName();
System.out.print(name + "\t");
}
拿到指定属性的值,并对指定属性的值进行操作
Dog teddy = new Dog();
teddy.setName("泰迪");
Class c = teddy.getClass();
Field field = c.getDeclaredField("name");
//如果不设置 会报错:IllegalAccessException 因为name的属性是private的。
field.setAccessible(true);
Object value = field.get(teddy);
System.out.println("name属性的值为" + value);
//设置name的值 第一个参数是哪一个类的实例,第二个参数是要设置的值。
field.set(teddy, "哈士奇");
System.out.println("修改后的值为" + teddy.getName());
方法总结
Field[] getFields()
:获取所有public的field(包括父类);Field[] getDeclaredFields()
:获取当前类的所有field(不包括父类);Field getField(name)
:根据字段名获取某个public的field(包括父类);Field getDeclaredField(name)
:根据字段名获取当前类的某个field(不包括父类)。通过Class
实例可以获取到方法Method
信息
//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] methods1 = clazz.getDeclaredMethods();
//获取某个public的Method(包括父类)
Method method1 = clazz.getMethod("play");
//获取当前类的某个Method(不包括父类) 无参
Method method = clazz.getDeclaredMethod("sleep");
通过上面拿到的方法,可以进一步拿到该方法的其他信息。
getAnnotations()
:获取方法声明的注解;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义;getReturnType()
:返回方法返回值类型;getName()
:返回方法名称;getParameterTypes()
:返回方法的参数类型,是一个Class数组;getExceptionTypes()
:抛出的异常。//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] methods1 = clazz.getDeclaredMethods();
for (Method method : methods1) {
//一、获取该方法声明的注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("方法的注解为:" + annotation);
}
//二、获取权限修饰符
System.out.print(Modifier.toString(method.getModifiers()) + "\t");
//三、获取返回值类型
System.out.print(method.getReturnType().getName() + "\t");
//四、获取方法名
System.out.printf(method.getName());
//五、获取参数列表
Class[] parameterTypes = method.getParameterTypes();
//六、获取抛出的异常
Class[] exceptionTypes = method.getExceptionTypes();
}
调用方法
Object invoke(Object instance, Object... parameters)
调用某个对象的方法,第一个参数是对象实例,即在哪一个对象上调用该方法;第二个参数是参数列表。Class clazz = Dog.class;
//创建运行时类的对象
Dog dog1 = (Dog)clazz.newInstance();
//以该方法为例:public String play(String toy),获取指定的方法
Method method = clazz.getDeclaredMethod("play",String.class);
//确保当前方法可以访问 可以调用private修饰的方法,
//不加的话,private方法会报错:IllegalAccessException
method.setAccessible(true);
//调用方法:调用方法的invoke,Object invoke(Object instance, Object... parameters)
// 参数1:对象实例-->dog1 参数2:形参列表; 调用静态方法时,第一个参数传null。
//方法的返回值为调用方法的返回值
Object returnObj = method.invoke(dog1, "篮球");
System.out.println(returnObj);
getConstructor(Class...)
:获取当前运行类中某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取当前运行类中某个Constructor
;getConstructors()
:获取当前运行类中所有public
的Constructor
;getDeclaredConstructors()
:获取当前运行类中所有Constructor
。//Dog类的实例化
//1、使用new关键字
Dog teddy = new Dog();
teddy.setName("teddy");
//2、使用反射的方式
//2.1:使用的是无参的构造方法;2.2:权限修饰符为public
Dog dog1 = Dog.class.newInstance();
dog1.setName("田园犬");
//3、使用反射调用任意的构造方法 例:public Dog(String name);
Class clazz = Dog.class;
//3.1、取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
//3.2、获取当前运行类中声明的所有构造器的方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//3.3、获取当前运行类中的String类型参数的构造方法
Constructor constructor = clazz.getConstructor(String.class);
//3.4、保证此构造器是可访问的
constructor.setAccessible(true);
//3.5、调用构造方法创建对象
Dog dog2 = (Dog)constructor.newInstance("哈士奇");
Class getSuperclass()
获取运行时的父类;Type getGenericSuperclass()
获取带泛型父类;
Type[] getActualTypeArguments()
获取带泛型的父类的泛型;Class[] getInterfaces()
:获取当前类实现的所有接口;Package getPackage()
:获取当前类所在包;Annotation[] getAnnotations()
:获取运行时类声明的注解。原创不易,欢迎转载,转载时请注明出处,谢谢!
作者:潇~萧下
原文链接:https://www.cnblogs.com/manongxiao/p/13429889.html
原文:https://www.cnblogs.com/manongxiao/p/13429889.html