概述
- 之前的学习主要是先类,然后通过类创建的对象;所谓的反射,通过对象反过来对类进行相关的操作
- 类的组成部分
- 类的名称 -> Class(类的类)
- 属性(成员变量)-> Field(属性类)
- 一般方法 -> Method(方法类)
- 构造方法-> Constructor(构造方法类)
基本使用
//1,获取类的类对象Class clazz = Student.class;System.out.println("类的名称:"+clazz.getName());//2,获取类的属性Field[] fields = clazz.getDeclaredFields();System.out.println("属性有:");for (Field field :fields) {System.out.print(field.getName()+",");}System.out.println("-----");//3,获取类的方法System.out.println("方法有:");Method[] methods = clazz.getDeclaredMethods();for (Method method:methods) {System.out.print(method.getName()+",");}System.out.println("-----");//4,获取构造方法System.out.println("构造方法有:");Constructor[] constructors = clazz.getDeclaredConstructors();for (Constructor constructor:constructors) {System.out.print(constructor.getName()+",");}
类对象
将所有的类,都是通过Class进行定义,每个不同的类的信息都是存储再Class创建的类的对象中的
Class类的对象获取几种方式
第一种,直接通过类.class获取
Class teacherClass = Teacher.class;Class studentClass = Student.class;
第二种,通过对象获取(重点)
Student student = new Student();Class studentClass1 = student.getClass();System.out.println(studentClass1.getName());
第三种,通过类的名字来获取
Class teacherClass2 = Class.forName("com.java2022.entity.Teacher");System.out.println(teacherClass2.getName());
第四种,通过类加载器获取
ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class teacherClass3 = classLoader.loadClass("com.java2022.entity.Teacher");
常用的方法 | 方法名 | 描述 | | —- | —- | | public String getName() | 获取类的完全名称 | | public Package getPackage() | 获取包信息 | | public Class<? super T> getSuperclass() | 获取父类 | | public Class<?>[] getInterfaces() | 获取实现父接口 | | public Field[] getFields() | 获取字段信息 | | public Method[] getMethods() | 获取方法信息 | | public Constructor<?>[] getConstructors() | 获取构造方法 | | public T newInstance() | 反射创建对象 |
实现一个方法,传入类的名称,可以获取类创建的对象
创建一个接口
public interface IPerson {void say();}
创建实现接口的类
public class Teacher implements IPerson {@Overridepublic void say() {System.out.println("我是个老师");}}
实现创建对象方法
//实现一个方法可以根据类的名称创建对象public static IPerson createInstance(String className){try {//获取className对应类的类对象Class<IPerson> clazz = (Class<IPerson>) Class.forName(className);//通过类的类对象创建对象return clazz.newInstance();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
测试对象的创建
public static void main(String[] args) {IPerson iPerson = createInstance("com.java2022.entity.Worker");iPerson.say();}
属性对象
Field是属性的类,由其创建的对象就是属性的对象
- 属性对象主要用于存储类的属性的相关的信息
通过类的类对象(Class)获取对应的类的属性的对象
clazz.getDeclaredFields();//获取所有的属性//获取指定的属性Field field = clazz.getDeclaredField("age");
通过属性对象(Feild)获取属性的相关的信息
String name = field.getName();//获取属性的名称Class type = field.getType();//获取属性的类型类对象System.out.println("属性的名称:"+name);System.out.println("属性的类型:"+type.getName());
通过属性对象,获取类创建对象的属性的值
//通过一个对象,获取对象的所有的属性和对应值public static void get(Object obj){//获取对象的类对象Class clazz = obj.getClass();//获取对象的所有的属性对象Field[] fields = clazz.getDeclaredFields();for (Field field :fields) {field.setAccessible(true);//设置强制获取try {//获取属性对象对应的值Object value = field.get(obj);System.out.println(field.getName() + ":"+value);} catch (IllegalAccessException e) {e.printStackTrace();}}}
设置属性的值
Worker worker = new Worker(13,"开挖掘机的");//篡改编号Class clazz = worker.getClass();Field field = clazz.getDeclaredField("id");field.setAccessible(true);field.set(worker,2);//支持各种基本类型和包装类型,只要对象类型一致即可field.setInt(worker,4);//指定类型,必须是int类型,如果是是Integer请属性set
方法对象
- 通过方法对象可以对类创建的对象的方法进行调用
获取一个类的方法对象
//2获取所有的方法对象Method[] methods = clazz.getDeclaredMethods();//获取所有的方法for (Method method:methods) {System.out.println(method.getName());}//3获取一个方法try {Method setName1 = clazz.getMethod("setName");//获取无参的方法Method setName2 = clazz.getMethod("setName",String.class);Method setAge = clazz.getMethod("setAge", int.class);} catch (NoSuchMethodException e) {e.printStackTrace();}
方法对象的一般方法
Class[] types = setName2.getParameterTypes();//获取所有参数的类型System.out.println("方法的名称:"+setName2.getName());for (Class t:types){System.out.println("参数类型:"+t.getName());}Class returnType = setName2.getReturnType();//获取返回对象的类型System.out.println("返回参数类型:"+returnType.getName());
通过反射调用对象的私有方法 ```java public static Object callPrivateMethod(Object obj,String methodName,Class[] paramsType,Object[] values){
//1,通过对象获取类的类对象Class clazz = obj.getClass();//2,获取方法对象try {Method method = clazz.getDeclaredMethod(methodName,paramsType);method.setAccessible(true);//3通过方法对象,调用对象的方法Object returnValue = method.invoke(obj,values);return returnValue;} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;
} //方法的调用 public static void main(String[] args) {
Student s1 = new Student();s1.setName("小黑");;callPrivateMethod(s1,"setName",new Class[]{String.class},//方法参数的类型new Object[]{"小黄"});//方法的实参
Object value = callPrivateMethod(s1,"getName",null,null);System.out.println("结果是:"+value);}
<a name="wlcXu"></a>## 构造方法对象- 构造方法对象主要是用于创建对象的- 构造方法对象的获取方式```java//2,获取构造方法对象Constructor[] constructors = clazz.getDeclaredConstructors();for (Constructor c : constructors){System.out.println(c.getName());}//获取指定的构造方法Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
通过构造方法对象创建对象
//3,通过构造方法对象创建对象Object obj = constructor.newInstance("小明",15);System.out.println(obj.toString());
注解编程
注解概述
注解是JDK1.5之后出现的新特性,本质上是对反射的补充
注解的作用
定义一个注解,直接通过@interface进行定义
public @interface MyAnnotation {}
设置方法,是没有方法体的
public @interface MyAnnotation {String name();int age();}
可以在类(接口,注解),属性,方法,参数使用注解(重点)
@MyAnnotation(name="xx",age=15)public class TestAnnotation002 {@MyAnnotation(age = 12)String dd;@MyAnnotation(age=13)public static void add(@MyAnnotation(age=15) String[] arg){}}
通过反射获取注解上方法的值
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1,获取类对象Class clazz = UserEntity.class;//2,通过类对象获取类上的注解Annotation[] annotations = clazz.getDeclaredAnnotations();for (Annotation annotation:annotations) {// System.out.println(annotation.toString());}//获取指定的注解Annotation a = clazz.getDeclaredAnnotation(MyTable.class);System.out.println(a.toString());//1,获取对象的类的类对象Class myTableClazz = a.annotationType();//2,通过类的类对象获取对应的方法Method tableNameMethod = myTableClazz.getDeclaredMethod("tableName");//3,通过方法对象和对象进行调用获取返回结果String tableName = (String) tableNameMethod.invoke(a);System.out.println("表名:"+tableName);}
元注解的使用
元注解:就是用于定义注解的注解
四个元注解
@Target:限制注解在那些位置定义 :::info 1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明 :::@Retention: 定义了注解在什么情况下生效 :::info 1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)[一般的选项] :::@Documented: 生成文档
- @Inherited:在使用这个注解的类的子类,注解也效果
枚举
枚举的概述
也是可以类,接口,注解同级别的定义
主要可以用于一组常量 和 单例模式的创建
传统的常量写法
定义常量
public interface ColorContants {int GREEN =1;int RED = 2;int YELLOW = 3;}
在代码中通过switch进行判断
//传统写法int color = ColorContants.RED;switch (color){case ColorContants.GREEN:break;case ColorContants.RED:break;case ColorContants.YELLOW:break;}
enum的写法
枚举中可以定义枚举类型成员,成员变量,构造方法,一般方法
- 枚举类型成员,就是使用枚举直接创建的对象,
- 一般都写在枚举的头部
- 多个枚举之间使用,隔开
- 最后一个枚举类型成员需要使用;进行分割
枚举的构造方法,是在枚举对象被创建的时候调用
- 默认调用的是无参的构造方法
- 构造方法被重载之后,默认的无参构造方法就会失效
构造方法不能是public,只能在枚举内部调用 ```java public enum ColorEnum { GREEN, //创建了一个枚举对象 RED(2,”红色”),//调用有参数的构造方法 YELLOW; String name; int index; //构造 ColorEnum(){
} ColorEnum(int index,String name){ this.name = name; this.index = index; } //一般方法 public String getName(){ return this.name; }
}
- 枚举的基本使用- 枚举不能被new出来,只能通过枚举类型来获取对象ColorEnum colorEnum = ColorEnum.GREEN;- 两种方法用法- 直接通过枚举类调用方法(调用的方法都是来自于Enum父类)```java//获取所有的枚举ColorEnum[] colorEnums = ColorEnum.values();//根据名字获取指定的枚举对象ColorEnum redEnum = ColorEnum.valueOf("RED");
- 通过枚举对象调用方法
//调用自定义的方法redEnum.getName();//获取枚举对象的默认索引(父类提供了,从0开始)e.ordinal()//比较两个枚举的大小,相同0,小于-1,大于1redEnum.compareTo(greenEnum);
设计模式
概述
- 设计就是前人提供一些经验,可以复用代码结构
- 设计模式即可复用面向对象软件的基础
设计模式的分类
学习设计模式,一般都是需要使用UML进行描述
在static变量的时候,直接创建对象
/*饿汉式*/public class MyHungrySingleton {static MyHungrySingleton instance = new MyHungrySingleton();private MyHungrySingleton(){}public static MyHungrySingleton getInstance(){return instance;}}
天生不会出现线程并发的问题
-
懒汉式
为了解决饿汉式浪费静态内存的问题,所以出现了懒汉式
public class MyLazySingleton {static MyLazySingleton instance;private MyLazySingleton(){}public static MyLazySingleton getInstance(){if(instance==null){instance = new MyLazySingleton();}return instance;}}
但是一般的懒汉式会出现线程并发的问题,使用DCL + volatie解决
public class MyLazySingleton {static volatile MyLazySingleton instance;private MyLazySingleton(){}public static MyLazySingleton getInstance(){if(instance==null){synchronized(MyLazySingleton.class){if(instance ==null){instance = new MyLazySingleton();}}}return instance;}}
枚举式
直接利用枚举的私有构造方法的特征实现
public enum EnumSingleton {INSTANCE;public static EnumSingleton getInstance(){return INSTANCE;}}
-
容器式
之前所有的单例模式都是针对某一个类进行实现,当出现多个类需要使用单例模式的时候,就会很麻烦而且不灵活
所谓的容器式,就是创建一个容器,由其进行对象的创建和维护,使用者直接通过类的名称获取单例的类对象即可
public class MySingleContainer {//定义一个容器Map<String,Object> container = new ConcurrentHashMap<>();public Object getInstance(Class clazz){//使用类的名称作为key值String key = clazz.getName();Object obj = container.get(clazz.getName());if(obj == null){synchronized (MySingleContainer.class){if(obj ==null){//创建对象try {obj = clazz.newInstance();//将对象放入到容器中container.put(key,obj);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}}return obj;}}
Threadlocal的方式实现
- 不能保证整个程序唯一;
- 可以保证线程唯一;
public class ThreadLocalSingleton {private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstanceThreadLocal = new ThreadLocal<ThreadLocalSingleton>() {@Overrideprotected ThreadLocalSingleton initialValue() {return new ThreadLocalSingleton();}};//构造方法私有化private ThreadLocalSingleton() {}//获取线程public static ThreadLocalSingleton getInstance() {return threadLocalInstanceThreadLocal.get();}}
- 可以保证线程唯一;








