this.age = age;
}
@MyAnnotation
private String show(String nation){
System.out.println(“我的国际是:”+nation);
return nation;
}
public String display(String interests) throws Exception,NullPointerException{
return interests;
}
@Override
public void info() {
System.out.println(“我是一个人”);
}
@Override
public int compareTo(String o) {
return 0;
}
private static void showDesc(){
System.out.println(“hello ,world.”);
}
@Override
public String toString() {
return “Person{” +
“name='” + name + ‘’’ +
“, age=” + age +
“, id=” + id +
‘}’;
}
}
Creature类:
package exam.demo;
import java.io.Serializable;
public class Creature implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println(“生物呼吸”);
}
public void eat(){
System.out.println(“生物吃东西”);
}
}
MyAnnotation注解:
package exam.demo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME) //注解必须用RUNTIME的生命周期
public @interface MyAnnotation {
String value() default “hello”;
}
MyInterface接口:
package exam.demo;
public interface MyInterface {
void info();
}
整体上一个反射效果案例(了解,看后面):
package filefanxingTest;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
public class ReflectionTest {
// 反射之前对于Person类的操作
@Test
public void test1() {
// 1.创建Person类的对象
Person p1 = new Person(“Tom”, 12);
// 2.通过对象,调用其内部的属性,方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
// 在Person外部,不可以通过Person类的对象调用其内部私有结构
// 比如,我们定义的name,showNation()方法以及私有的构造器。
}
// 反射之后,对于Person的操作
@Test
public void test2() throws Exception {
// 1.通过反射创建Person类的对象
Class clazz = Person.class;
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance(“Tom”, 12);
Person p = (Person) obj;
System.out.println(p.toString());
// 2.通过反射 ,调用对象指定的属性和方法
Field age = clazz.getDeclaredField(“age”);
age.set(p, 10);
System.out.println(p.toString());
Method show = clazz.getDeclaredMethod(“show”);
show.invoke§;
System.out.println(“*******************”);
// 通过反射,是可以调用Person类的私有结构的,比如:私有的构造器,私有的方法,私有的属性。
Constructor cons2 = clazz.getDeclaredConstructor(String.class);
cons2.setAccessible(true);
Person p2 = (Person) cons2.newInstance(“Jerry”);
System.out.println(p2);
// 调用私有属性
Field name = clazz.getDeclaredField(“name”);
name.setAccessible(true);
name.set(p2, “zhangsan”);
// 调用私有的方法
Method showNation = clazz.getDeclaredMethod(“showNation”, String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p2, “中国”);// 相当于String nation = p1.showNation(“中国”);
System.out.println(nation);
}
}
反射可以给私有属性,私有方法,私有构造器来设置值。正常的new创建对象是不可以的!
============================================================================
通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底使用哪个呢?
建议:直接new的方式。
什么时候会使用:反射的方式? 反射的特征:动态性。
反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾,封装性提示的是你哪个方法能调用哪个方法不能调用(private),整体上算是一个限制提示建议的效果。
=====================================================================================
类的加载过程:
程序经过javac.exe 以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,这个过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
**提示小结:
Idea项目下的out目录是用来存放.java文件编译后的字节码文件的。**
万事万物皆对象!
平常我们是对象.xxx属性 , File对象, URL对象等等。反射同样也是,就是通过Class的类对象,来实现的。
并且Class的实例就对应着一个运行时类,因此我们不能new一个Class,因为他是运行时类本身就存在的。
=================================================================================
首先,第一点Class运行时类并不是我们自己创建出来的,而是它编译运行后自己有的类。
加载到内存中的运行时类,会缓存一定的时间。在此事件之内,我们可以通过4中不同的方式来获取此运行时类。(也就是只有一个运行时类,只不过获取方式不同。)
package com.test;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//也是可以加泛型的
//Class clazz1 = Person.class;
//方式二:通过运行时类的对象,调用getClass()来获取
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName(“com.test.Person”);
System.out.println(clazz3);
//我们比较一下他们的地址值,返回都是true,说明他们都是相同的运行时类,只不过获取的方式不同而已。
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1 == clazz3);//true
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass(“com.test.Person”);
System.out.println(clazz4);
System.out.println(clazz1 == clazz4);//true
}
}
================================================================================
package com.test;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
@Test
public void test4(){
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
//只要元素类型与维度一样,就同一个Class
System.out.println(c10 == c11);//true
}
}
======================================================================================
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
这三个步骤必须牢记!
加载(Load):就是讲class文件字节码内容加载到内存中,转换为运行时数据结构,生成一个代码这个类的java.lang.Class对象。
链接(Link):正式为类变量(static类型)分配内存并设置类变量默认初始值,例如在static int a 在链接这个环节它就会被赋值默认初始值0,String b在链接就被默认赋值为null。
初始化:这个阶段有一个类构造器()方法,非常重要,它是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
类加载器的作用和类缓存:
类加载器作用就是用来把类(class)装载进内存的。
我们平时定义的类,都是通过系统类加载器来架子啊的,因此也算是最常用的加载器了。
package com.test;
import org.junit.jupiter.api.Test;
public class ClassLoadingTest {
@Test
public void test1(){
//对于定义类,使用系统类加载器进行加载
ClassLoader classLoader = ClassLoadingTest.class.getClassLoader();
System.out.println(classLoader);//这个就是AppClassLoader系统加载器
//调用系统类加载的getParent():获取扩展类加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);//这个就是ExtClassLoader拓展加载器
//调用扩展类加载器的getParent(): 无法直接获取引导类加载器的。
//引导类加载器主要负责加载java的核心类库,它是无法加载自定义类。
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);//这里返回null,并不是没有,而是引导类加载器不能直接获取。
//引导类加载器加载java的核心类库,例如String类型
ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println(classLoader3);//这里也是返回null,说明String的加载器也是引导类加载器。
}
}
7.3 类加载器(ClassLoader) 加载配置文件Properties案例
我们在jdbc场景下,就有两种加载Properties文件的方式:
-
读取配置文件方式一:使用FileInputStream方法,此路径默认是当前module工程模块下。
-
读取配置文件方式二:使用ClassLoader , 此路径默认是当前module的src下。
package com.test;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
public class ClassLoadingTest {
/*
Properties:用来读取配置文件。
*/
@Test
public void test2() throws Exception {
Properties pros = new Properties();
//读取配置文件方式一:使用FileInputStream方法,此路径默认是当前module工程模块下。
FileInputStream fis = new FileInputStream(“jdbc.properties”);
//如果想用这种方式访问jdbc1.properties也是可以的,就是需要配置路径如下
//FileInputStream fis2 = new FileInputStream(“src\jdbc1.properties”);
pros.load(fis);
String user = pros.getProperty(“user”);
String password = pros.getProperty(“password”);
System.out.println(“姓名:”+user + “, 密码:” +password);
}
@Test
public void test3() throws Exception {
Properties pros = new Properties();
//读取配置文件方式二:使用ClassLoader , 此路径默认是当前module的src下。
ClassLoader classLoader = ClassLoadingTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream(“jdbc1.properties”);
pros.load(is);
String user = pros.getProperty(“user”);
String password = pros.getProperty(“password”);
System.out.println(“姓名:”+user + “, 密码:” +password);
}
}
8. 反射 newInstance()方法 创建对应的运行时类的对象
================================================================================================
clazz.newInstance()方法:调用此方法,创建对应的运行时类的对象。该方法默认调用的就是类的空参构造器。
因此要想此方法能正常创建运行时类的对象,要求:
-
1.运行时类必须提供空参的构造器。
-
2.空参的构造器的访问权限得够!通常设置为public。
一个public的空参构造器,在javabean中很重要,原因如下:
-
1.便于反射,创建运行时类的对象。
-
2.便于子类继承此运行时类 时,默认调用super()时,保证父类有此构造器。
package com.Reflection;
import com.test.Person;
import org.junit.jupiter.api.Test;
public class NewInstanceTest {
@Test
public void test1() throws IllegalAccessException, InstantiationException {
Class clazz = Person.class;
/*
clazz.newInstance()方法:调用此方法,创建对应的运行时类的对象。
这里默认调用的就是类的空参构造器。
因此要想此方法能正常创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器。
2.空参的构造器的访问权限得够!通常设置为public。
一个public的空参构造器,在javabean中很重要,原因如下:
1.便于反射,创建运行时类的对象。
2.便于子类继承此运行时类 时,默认调用super()时,保证父类有此构造器。
*/
Person p1 = (Person)clazz.newInstance();
System.out.println(p1);
}
}
通过newInstance()方法,来体现反射的一个动态效果:
package com.Reflection;
import com.test.Person;
import org.junit.jupiter.api.Test;
import java.util.Random;
public class NewInstanceTest {
/*
下面这种方式就体现了,反射的动态性效果。
*/
@Test
public void test2(){
//要一个随机值,nextInt(3)定义了以3为边界。
int num = new Random().nextInt(3);//0,1,2
String classPath = “”;
switch (num){
case 0:
classPath = “java.util.Date”;
break;
case 1:
classPath = “java.lang.String”;
break;
case 2:
classPath = “com.test.Person”;
break;
}
try {
Object obj = getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
该方法创建一个指定类的对象。
classPath:指定类的全类名
*/
public Object getInstance(String classPath) throws Exception {
Class<?> clazz = Class.forName(classPath);
return clazz.newInstance();
}
}
=====================================================================================
获取属性结构
-
getFields():获取当前运行时类及其父类中声明为public访问权限的属性.
-
getDeclaredFields():获取当前运行时类中声明的所有属性,并且不包含父类中声明的属性。
package exam.demo2;
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
/*
获取当前运行时类的属性结构
*/
public class FieldTest {
@Test
public void test() throws Exception {
/*
Person是我自定义的一个类,
定义了int,String类型属性,创建一些方法,
设定注解,实现接口等等操作,这里就不添加person源码了,随便设定就可以。
*/
Class clazz = Person.class;
Class clazz2 = Class.forName(“exam.demo.Person”);
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz2.getFields();
for (Field f : fields){
System.out.println(f);
}
System.out.println(“*******************”);
//getDeclaredFields():获取当前运行时类中声明的所有属性,并且不包含父类中声明的属性。
Field[] declaredFields = clazz2.getDeclaredFields();
for (Field f : declaredFields){
System.out.println(f);
}
}
}
获取属性结构的权限修饰符,数据类型,变量名:
package exam.demo2;
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
获取当前运行时类的属性结构
*/
public class FieldTest {
/*
属性的权限修饰符,数据类型,变量名的获取
*/
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
//1.f.getModifiers()获取当前属性的权限修饰符
int modifiers = f.getModifiers();
System.out.println(modifiers);//返回的是int类0,1,2,4
System.out.println(Modifier.toString(modifiers));
//2.f.getType()获取当前属性的数据类型
Class type = f.getType();
System.out.println(type + “,”);
System.out.println(type.getName());
//3.f.getName()获取当前属性的变量名
String name = f.getName();
System.out.println(name);
System.out.println(“--------------”);
}
}
}
get.getModifiers()返回值对应类型:
(default类型的值为: 0)
获取当前运行时类的属性结构的权限修饰符,注解,返回值类型,方法名和参数名 ,抛出异常。
注意如果反射需要接受注解啥的,那么注解的生命周期必须是runtime的!!
原因也很简单,反射是基于.class文件后的,因此注解也必须保留到那个时候。
package exam.demo2;
import org.junit.jupiter.api.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/*
获取当前运行时类的属性结构的权限修饰符,返回值类型,方法名和参数名 ,抛出异常。
*/
public class FieldTest {
@Test
public void test() throws Exception {
Class clazz = Class.forName(“exam.demo.Person”);
Method[] declaredMethod = clazz.getDeclaredMethods();
for (Method m : declaredMethod){
//1. m.getAnnotations()方法:获取方法声明的注释
Annotation[] annotation = m.getAnnotations();
for (Annotation a : annotation){
System.out.println(a);
}
//2. m.getModifiers()方法:获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers())+“\t”);
//3. m.getReturnType()方法:返回值类型
Class<?> returnType = m.getReturnType();
System.out.print(returnType+“\t”);
//4. m.getName()方法:方法名
String name = m.getName();
System.out.print(name+“\t”);
System.out.print(“(”);
//5. 形参列表
Class[] parameterTypes = m.getParameterTypes();
//没有参数类型的情况和有参数的情况区分
if (!(parameterTypes == null && parameterTypes.length == 0)){
for (Class c:parameterTypes){
//形参名
System.out.print(“参数名:”+c.getName());
}
}
System.out.print(“)”+“\t”);
//6. 获取抛出异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
if (!(exceptionTypes == null && exceptionTypes.length == 0)){
for (Class c : exceptionTypes){
System.out.print(“异常:”+c.getName()+“\t”);
}
}
System.out.println();
}
}
}
=================================================================================
获取构造器结构的方法:
package exam.demo2;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
public class OtherTest {
/*
获取构造器结构
*/
@Test
public void test() throws ClassNotFoundException {
Class clazz = Class.forName(“exam.demo.Person”);
//clazz.getConstructors()方法:获取当前运行时类当中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
for (Constructor c : constructors){
System.out.print(c+“\t”);
System.out.println(c.getName());
}
System.out.println(“--------------”);
//clazz.getDeclaredConstructors()方法:获取当前运行时类中声明的所有构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c : declaredConstructors){
System.out.print(c+“\t”);
System.out.println(c.getName());
}
}
}
================================================================================
获取运行时类的父类及其父类泛型。
package exam.demo2;
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class OtherTest {
/*
获取运行时类的父类
*/
@Test
public void test2(){
Class clazz = Person.class;
//clazz.getSuperclass()方法:获取运行时的父类。
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
System.out.println(superclass.getName());
}
@Test
public void test3(){
Class clazz = Person.class;
//clazz.getGenericSuperclass()方法:获取带泛型的父类。
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
@Test
public void test4(){
//获取运行时类的带泛型的父类的泛型
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getTypeName());
}
}
=======================================================================================
package exam.demo2;
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.annotation.Annotation;
public class OtherTest {
/*
获取运行时类实现的接口结构
*/
@Test
public void test(){
Class clazz = Person.class;
//clazz.getInterfaces()方法:获取运行时类实现的接口。
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces){
System.out.println©;
}
//clazz.getSuperclass().getInterfaces()方法:获取运行时类的父类的接口信息。
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c : interfaces1){
System.out.println©;
}
}
/*
获取运行时类所在的包
*/
@Test
public void test2(){
Class clazz = Person.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
}
/*
获取运行时类声明的注解
*/
@Test
public void test3(){
Class personClass = Person.class;
Annotation[] annotations = personClass.getAnnotations();
for (Annotation a : annotations){
System.out.println(a);
}
}
}
================================================================================
调用运行时类中指定的结构:属性,方法,构造器:
package exam.demo2;
/*
调用运行时类中指定的结构:属性,方法,构造器
*/
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
public class ReflectionTest {
@Test
public void testField() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Class clazz = Person.class;
/*
clazz.getField(“id”)方法:获取指定的属性,这里也是只能获取public类型的属性。通常不采用该方式!
*/
Field id = clazz.getField(“id”);
System.out.println(id);
//创建运行时类对象
Person p = (Person)clazz.newInstance();
//id.set(p,xxx)方法:设置参数
id.set(p,100);
//id.get§方法:获取p的id的属性值
int i = (int)id.get§;//可以强转一下
System.out.println(i);
/*
clazz.getDeclaredField(“name”)方法比较推荐,因为它可以修改所有权限。
*/
Field name = clazz.getDeclaredField(“name”);
//name.setAccessible(true)方法:对于private和默认类型等等,我们必须设置它的Accessible为true才能进行设置修改!
name.setAccessible(true);
name.set(p,“zhangsan”);
String str = (String)name.get§;
System.out.println(str);
System.out.println(“=================”);
//
Field[] field = clazz.getFields();
for (Field f: field){
System.out.println(f);
}
System.out.println(“===================”);
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f:declaredFields){
System.out.println(f);
}
}
}
=================================================================================
调用运行时类的方法,并设置参数:
package exam.demo2;
import exam.demo.Person;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
/*
调用运行时类的方法,并设置参数
*/
@Test
public void testMethod() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class aClass = Class.forName(“exam.demo.Person”);
Person p = (Person)aClass.newInstance();
//1. getDeclaredMethod(“xxx”,参数类型.class)方法:获取指定的某个方法 , 参数1:方法名,参数2:指明获取方法的形参列表。
Method declaredMethod = aClass.getDeclaredMethod(“show”, String.class);
//注意这里也要设置setAccessible(true)的方法!不然遇到private等其他类型可能就无法执行。
declaredMethod.setAccessible(true);
//declaredMethod.invoke()方法: 参数1:方法调用者的对象,参数2:给方法传递的形参。
//此外,invoke()方法的返回值也是调用方法的返回值。
Object returnvalue = declaredMethod.invoke(p, “zhangsan”);
System.out.println(returnvalue);
System.out.println(“方法有静态方法,那么如何调用静态方法呢?? 见下:”);