1.反射
一、什么是反射?
Java反射是Java被视为动态语言的很重要的一个特性。
Java通过反射的Api可以做到获取运行时类的任何信息。包括其类、属性、方法、注解、父类等任何信息。并且可以直接操作任意对象的内部属性及方法。
二、反射能用来做什么?
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时获取泛型信息。
- 在运行时调用任意一个对象成员变量和方法。
- 在运行时处理注解。
- 生成动态代理。
三、获取class实例的方式。
获取Class实例的方式
- 方式一:调用运行时类的属性。 .class
Class<Person> clazz1 = Person.class;
- 方式二:通过运行时类的对象 调用getClass这个方法
Class<? extends Person> aClass = person.getClass();
- 方式三:调用Class的静态方法
Class.forName(String classPath)
- 方式四:使用类的加载器:ClassLoader
Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类。
四、通过反射创建运行时类的对象。
newInstance():调用此方法创建对应的运行时类的对象。内部调用了运行时类的空参构造器。
要想此方法能够正常的创建运行时类的对象,要求:
- 运行时类必须提供空参构造器。
- 空参构造器的访问权限得够(通常设置为public的)
五、类加载器
系统类加载器:加载自己创建的一些类。
扩展类加载器:加载Java的lib下得扩展类。
引导类加载器:主要负责加载java的核心类库,无法加载自定义类的。
六、随机创建运行时类对象,体验动态性
@Test
public void testNewInstance1() throws Exception {
int num = new Random().nextInt(3);
String classPath = "";
switch (num) {
case 0: classPath = "java.util.Date"; break;
case 1: classPath = "java.lang.Object"; break;
case 2: classPath = "com.starcpdk.Person"; break;
}
Object obj = getInstance(classPath);
System.out.println(obj);
}
/**
* 创建一个指定类的对象
* @param classPath 指定类的全类名
* @return
* @throws Exception
*/
public Object getInstance(String classPath) throws Exception {
Class<?> clazz = Class.forName(classPath);
return clazz.newInstance();
}
七、创建对象的几种方式
- new + 构造器
- 调用方法获取实例。XXX、XXXs、XXXFactory、XXXBuilder等。
- clone方法克隆对象。
- 反射方式创建对象。调用newInstance()使用类或者构造器调用。
八、clazz获取一些结构。
- 获取构造器。
// 获取当前运行时类中声明为public的构造器
Constructor<?>[] constructors = personClass.getConstructors();
// 获取当前运行时类的所有构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
- 获取运行时类的父类
// 获取运行时类的父类
Class<? super Person> superclass = personClass.getSuperclass();
// 获取运行时类的带泛型的父类
Type genericSuperclass = personClass.getGenericSuperclass();
// 获取运行时类的带泛型的父类
Type genericSuperclass = personClass.getGenericSuperclass();
// 获取父类的泛型类型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(((Class)actualTypeArguments[0]).getName());
- 获取运行时类实现的接口
// 获取运行时类实现的接口
Class<?>[] interfaces = personClass.getInterfaces();
// 获取运行时类的父类实现的接口。
Class<?>[] interfaces1 = personClass.getSuperclass().getInterfaces();
- 获取当前运行时类 所在的包
Package aPackage = personClass.getPackage();
- 获取运行时类 声明的注解
Annotation[] annotations = personClass.getAnnotations();
- 获取运行时类的方法
// 获取当前运行时类及其所以父类中声明为public的所有方法
Method[] methods = personClass.getMethods();
// 获取当前运行时类中的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = personClass.getDeclaredMethods();
@Test
public void test2() throws Exception {
Class<Person> personClass = Person.class;
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// 4. 获取方法名
System.out.print("方法名是:" + declaredMethod.getName() + ":\t");
// 只能获取Runtime的生命周期的注解
// 1. 获取方法声明的注解
Annotation[] annotations = declaredMethod.getAnnotations();
for (Annotation annotation : annotations) {
System.out.print("注解是:" + annotation + "\t");
}
// 2、获取权限修饰符
int modifiers = declaredMethod.getModifiers();
System.out.print("权限修饰符是:" + Modifier.toString(modifiers) + "\t");
// 3.获取返回值类型
Class<?> returnType = declaredMethod.getReturnType();
System.out.print("返回值类型是:" + returnType.getName() + "\t");
// 5. 获取形参列表
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (!(parameterTypes == null || parameterTypes.length == 0)) {
for (Class<?> parameterType : parameterTypes) {
System.out.print("形参类型:" + parameterType.getName() + " args_" + " ");
}
}
// 6. 抛出的异常
Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.print("异常:" + exceptionType.getName());
}
System.out.println();
}
}
- 获取运行时类的属性结构
// getFields() :获取当前运行时类及其父类声明为public访问权限的属性
Field[] fields = personClass.getFields();
// getDeclaredFields():获取当前运行时类中所有的属性。(不包含父类的属性)
Field[] declaredFields = personClass.getDeclaredFields();
// 权限修饰符 数据类型 变量名
@Test
public void test1() {
Class<Person> personClass = Person.class;
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 权限修饰符
int modifiers = declaredField.getModifiers();
// System.out.println(modifiers);
System.out.print(Modifier.toString(modifiers) + "\t");
// 数据类型
Class<?> type = declaredField.getType();
System.out.print(type.getName() + "\t");
// 变量名
String name = declaredField.getName();
System.out.println(name);
}
}
- 使用运行时类中的一些属性或方法
package com.starcpdk.java2;
/**
* @Author 姚云峰
* @Email
* @Date 2022/10/27 14:02
* @Version 1.0
*/
import com.starcpdk.test.Person;
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 testField() throws Exception {
// 获取指定的属性
Class<Person> personClass = Person.class;
// 创建运行时类的对象。
Person person = personClass.newInstance();
// 获取指定属性 , 要求运行时类中的属性声明为public的。
// 通常不采用此方式
Field id = personClass.getField("id");
// 设置当前属性的值
// 参数1:指明设置哪儿个对象的属性, 参数2:指明将此属性值设置为多少
id.set(person , 1234);
// 获取当前属性的值
// 参数1:获取哪儿个对象的当前属性的值
int pId = (int) id.get(person);
System.out.println(pId);
}
/**
* 操作运行时类中的属性。
* @throws Exception
*/
@Test
public void testField1() throws Exception {
Class<Person> personClass = Person.class;
// 创建运行时类的对象。
Person person = personClass.newInstance();
// 获取指定变量名的属性
Field age = personClass.getDeclaredField("age");
// 保证当前属性是可访问的。
age.setAccessible(true);
// 获取、设置指定对象的此属性的值。
age.set(person , 12);
Object o = age.get(person);
System.out.println(o);
}
/**
* 操作运行时类中的方法
* 参数1:指明获取方法的名称。
* 参数2:指明获取方法的形参列表。
*/
@Test
public void testMethod() throws Exception {
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
Method show = personClass.getDeclaredMethod("show", String.class);
/**
* invoke()
* 参数1:方法的调用者。
* 参数2:给方法形参赋值的实参。
* invoke()的返回值就是对应类中调用的方法的返回值。
*/
show.setAccessible(true);
Object hahah = show.invoke(person, "哈哈哈哈");
System.out.println(hahah);
Method showDesc = personClass.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
// 如果调用的方法没有=返回值 , 则返回null
Object invoke = showDesc.invoke(Person.class);
System.out.println(invoke);
}
/**
* 调用运行时类中指定的构造器。
*
* 参数:指明构造器的参数列表
*
* @throws Exception
*/
@Test
public void testConstructor() throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
Person tom = declaredConstructor.newInstance("Tom");
System.out.println(tom);
}
}
2. 枚举
一、什么是枚举
类中对象的个数是确定的,有限个。
枚举类中的属性的修饰符是 private final 的。
枚举类中的对象的修饰符是 public static final 的。
二、枚举类常用方法
- values(): 返回一个数组,即枚举类中的所有对象
- valueOf(String str) 找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
- toString() 默认返回枚举类对象名称
三、例子
如何自定义枚举
/**
* @Author 姚云峰
* @Email
* @Date 2022/8/20 16:02
* @Version 1.0
* <p>
* <p>
* 枚举类的使用
*/
public class SeasonTest {
}
/**
* 自定义枚举实现
*/
class Season {
private final String seasonName;
private final String seasonDescription;
private Season(String seasonName, String seasonDescription) {
this.seasonName = seasonName;
this.seasonDescription = seasonDescription;
}
public static final Season SPRING = new Season("春天" , "春暖花开");
public static final Season SUMMER = new Season("夏天" , "夏日炎炎");
public static final Season AUTUMN = new Season("秋天" , "秋高气爽");
public static final Season WINTER = new Season("冬天" , "冰天雪地");
}
/**
* @Author 姚云峰
* @Email
* @Date 2022/8/20 16:16
* @Version 1.0
*
* 使用enum关键字创建枚举类
*
* 常用方法:
* values(): 返回一个数组,即枚举类中的所有对象
* valueOf(String str) 找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
* toString() 默认返回枚举类对象名称
*
*
*/
public class SeasonTestEnum {
public static void main(String[] args) {
System.out.println(Season1.SPRING);
}
}
interface Info{
void show();
}
enum枚举类。
// 枚举类实现接口。
// 可以统一重写接口方法
// 也可以每个枚举值都重写接口方法
enum Season1 implements Info{
// 提供当前类枚举对象
SPRING("春天","春暖花开"){
@Override
public void show() {
System.out.println("春天在哪儿里");
}
},
SUMMER("夏天","夏日炎炎") {
@Override
public void show() {
System.out.println("夏宁");
}
},
AUTUMN("秋天","秋高气爽") {
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天","冰天雪地") {
@Override
public void show() {
System.out.println("大约在冬季");
}
};
private final String seasonName;
private final String seasonDescription;
private Season1(String seasonName, String seasonDescription) {
this.seasonName = seasonName;
this.seasonDescription = seasonDescription;
}
// @Override
// public void show() {
// System.out.println("这是一个季节。。。。。。");
// }
}
注解
一、什么是注解,怎么定义
使用@interface关键字声明。
内部定义成员,通常使用value表示
可以指定成员的默认值,使用default定义默认值。
如果自定义的注解没有成员,表明是一个标识作用。
如果注解有成员,使用注解时需要指明成员的值。
自定义注解必须配上注解的信息处理流程(反射的方式)才有意义。
自定义注解通常都会指明两个元注解,即:@Retention \ @Target。
@Repeatable(MyAnnotations.class)
//@Inherited // 可继承注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
public @interface MyAnnotation {
String value() default "world";
}
二、什么是元注解
元注解:对现有注解进行解释说明的注解。
@Retention:指定所修饰的Annotation的声明周期: SOURCE\CLASS(默认行为)\RUNTIME
只有声明为RUNTIME声明的注解,才能通过反射获取。
@Target:用于指定被修饰的Annotation能用于修饰哪儿些程序元素。
@Documented:表示所修饰的注解在被javadoc解析时,保留下来。
@Inherited:具有继承性,如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。
通过反射获取注解信息:
Class<Student> studentClass = Student.class;
Annotation[] annotations = studentClass.getAnnotations();
-
可重复注解、类型注解
- 可重复注解:
- MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
- MyAnnotation的Target和Retention和MyAnnotations相同。
- 可重复注解:
-
类型注解:
- ElementType.TYPE_PARAMETER : 表示该注解能写在类型变量的声明语句中(如:泛型声明)
- ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。
三、注解使用场景
- 生成文档相关注解
- 在编译时进行格式检查
@Override:限定重写父类方法,该注解只能用于方法
@Deprecate:用于标识锁修饰的元素(类、方法)已过时。通常是因为锁修饰的结构危险或存在更好的选择。
@SuppressWarnings:抑制编译器警告。
四、例子
@Repeatable(MyAnnotations.class)
//@Inherited // 可继承注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
public @interface MyAnnotation {
String value() default "world";
}
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;
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {
MyAnnotation[] value();
}
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
/**
*
*/
public class AnnotationTest {
@Test
public void testAnnotation() {
Class<Student> studentClass = Student.class;
Annotation[] annotations = studentClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
@MyAnnotation(value = "hello")
@MyAnnotation(value = "abc")
// jdk8之前使用重复注解的方式
//@MyAnnotations({@MyAnnotation(value = "hello") , @MyAnnotation(value = "abc")})
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void walk(){
System.out.println("人走路");
}
}
class Student extends Person {
@Override
public void walk() {
super.walk();
}
}
class Generic<@MyAnnotation T>{
public void show() throws @MyAnnotation RuntimeException{
ArrayList<@MyAnnotation String> objects = new ArrayList<>();
int num = (@MyAnnotation int)10L;
}
}