概述
- 之前的学习主要是先类,然后通过类创建的对象;所谓的反射,通过对象反过来对类进行相关的操作
- 类的组成部分
- 类的名称 -> 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 {
@Override
public 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,大于1
redEnum.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>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
//构造方法私有化
private ThreadLocalSingleton() {}
//获取线程
public static ThreadLocalSingleton getInstance() {
return threadLocalInstanceThreadLocal.get();
}
}
- 可以保证线程唯一;