反射、设计模式、枚举、注解的简单介绍
一、反射
1.1反射的概念
反射:反射的机制指的是程序在运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类。也可以了解任意一个类的成员遍历及方法,也可以调用对象的属性和方法,这种动态获取程序的信息以及动态调用对象的功能,我们称之为java语言反射机制
反射:
-
以前得到对象的属性和方法 创建对象的实例new(耦合)写框架的时候根本就不知道有哪些对象的产生
- 类的对象(多个)
-
在代码运行期间获取对象中的属性及方法等等(解耦合)(不是在编译期间进行创建对象)
-
类对象(一个) 属性 方法 构造方法父类 接口 包…
-
反射涉及的类
-
java.lang.Class
:代表一个类 -
java.lang.reflect.Method
:代表类的方法 -
java.lang.reflect.Field
:代表类的成员变量 -
java.lang.reflect.Constructor
:代表类的构造器
-
二、Class
类及反射的原理
Class
本身也是一个类,一个加载的类在JVM中只会有一个Class
对象,一个Class
对象对应的是一个加载到JVM中的一个.class
文件- 通过
Class
可以完整地得到一个类中的所有被加载的结构。Class
类是反射的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class
对象
三、反射相关操作
3.1、获取Class
类对象
类名.class
效率最高对象.getClass
Class.forName("全类名")
可能出现异常
/*
* 类对象常用方法
*@throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* */
public static void Test02() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1.获取类对象
Class<?> c = Class.forName("com.deu.reflect.User");
//2.类对象的常用方法
//获取类名
String simpleName = c.getSimpleName();
System.out.println(simpleName);
//获取全限定名
String name = c.getName();
System.out.println(name);
//获取父类对象
Class<?> superclass = c.getSuperclass();
System.out.println(superclass);
//获取实现的接口
Class<?>[] cInterfaces = c.getInterfaces();
System.out.println(Arrays.toString(cInterfaces));
//获取所在的包对象
Package p1 = c.getPackage();
System.out.println(p1);
//通过类获取类的对象 1.必须有无参构造 2.构造方法需要访问权限
Object newInstance = c.newInstance();
System.out.println(newInstance);
}
哪些类型可以有Class
对象:
class
类interface
接口[]
数组enum
枚举annotation
注解- 基本数据类型
void
3.2获取属性
/*
* 通过类获取类对象中的属性
*@throw NoSuchFieldException
* */
public static void Test03() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//1.获取到类对象
Class<?> c = Class.forName("com.edu.reflect.User");
//2.通过类对象获取类的属性
// //获取类中所有公开属性 (包括父类)
// Field[] fields = c.getFields();
// for (Field f : fields) {
// System.out.println(f);
// }
// System.out.println("=======================================");
// //获取类中指定的公开属性 (包括父类)
// Field field = c.getField("age");
Field field1 = c.getField("c");
// System.out.println(field);
System.out.println(field1);
//
// System.out.println("=======================================");
//
// //获取到本类中所有的属性 (不包括父类)
// Field[] declaredFields = c.getDeclaredFields();
// for (Field f1:declaredFields) {
// System.out.println(f1);
// }
// System.out.println("=======================================");
//
// //获取本类中的指定属性(包括非公开 不包括父类)
// Field declaredField = c.getDeclaredField("a");
// System.out.println(declaredField);
//
// System.out.println("=========++========================");
//
// //Field干嘛用? 存值取值
// Field c1 = c.getField("c");
//
// //存值 参数1 类的对象 参数:属性值
// Object obj = c.newInstance();
// c1.set(obj,20.2);
// //取值
// System.out.println(c1.get(obj));
//默认修饰符修饰的属性
Field field = c.getDeclaredField("b");
Object obj = c.newInstance();
field.set(obj,10);
//取值
System.out.println(field.get(obj));
Field field1 = c.getDeclaredField("a");
Object obj2 = c.newInstance();
//暴力反射 不推荐使用
field1.setAccessible(true);
field1.set(obj2,"hello");
System.out.println(field1.get(obj2));
}
}
3.3获取构造方法
/*
* 通过类对象获取构造方法
*
*
* */
public static void Test04() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.获取类对象
Class<?> c = Class.forName("com.edu.reflect.User");
//通过类对象获取构造方法
// //获取本类中公开的构造方法
// Constructor<?>[] constructors = c.getConstructors();
// for (Constructor constructor:constructors) {
// System.out.println(constructor);
// }
//
// //获取本类中指定的构造方法
// Constructor<?> constructor = c.getConstructor(String.class);
// System.out.println(constructor);
//获取本类中所有的构造方法
// Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
// for ( Constructor<?> constructor: declaredConstructors) {
// System.out.println(constructor);
// }
//获取本类指定的构造方法
// Constructor<?> dr = c.getDeclaredConstructor(int.class);
// System.out.println(dr);
//构造方法是用来干嘛的? 创建类的对象
// Constructor<?> constructor = c.getConstructor(String.class);
Object newInstance = constructor.newInstance("CXK");
System.out.println(newInstance);
//暴力反射 不建议用
Constructor<?> declaredConstructor = c.getDeclaredConstructor(int.class);
declaredConstructor.setAccessible(true);
Object newInstance = declaredConstructor.newInstance(10);
System.out.println(newInstance);
}
3.4Method
类
/*
* 获取对象的方法
*
* */
public static void Test05() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1.获取类对象
Class<?> c = Class.forName("com.edu.reflect.User");
//2.通过类对象获取方法
//获取所有的公开方法 (包括父类)
// Method[] methods = c.getMethods();
// for (Method m:methods) {
// System.out.println(m);
//
// }
//获取指定的公开方法 (包括父类)
// Method method = c.getMethod("mm");
// System.out.println(method);
// Method method1 = c.getMethod("mm", String.class, int.class);
// System.out.println(method1);
//
// //获取本类中所有的方法(不包括父类)
// Method[] declaredMethods = c.getDeclaredMethods();
// for (Method ms:declaredMethods) {
// System.out.println(ms);
// }
// Method nn = c.getDeclaredMethod("nn");
// System.out.println(nn);
//方法 是用来干嘛的? 调用
//调用无参
// Method mm = c.getMethod("mm");
//
// mm.invoke(c.newInstance());
//
// //调用有参
// Method method = c.getMethod("mm", String.class, int.class);
// method.invoke(c.newInstance(),"cxk",30);
//
// //掉用有参的方法
// Method method1 = c.getMethod("show");
// //invoke方法的返回值就是反射调用方法的返回值,如果调用的方法有void 那么就是返回null
// Object invoke = method1.invoke(c.newInstance());
// System.out.println("方法的返回值"+invoke);
//调用私有的方法
Method method2 = c.getDeclaredMethod("nn");
//暴力反射
method2.setAccessible(true);
method2.invoke(c.newInstance());
}
四、设计模式
4.1概念
- 一套被反复使用、多数人知晓、经过分类 编目、代码设计经验的总结
- 可以简单理解为为特定问题固定的一个解决方式
- 在Gof的《设计模式》书中描述了23种设计模式
4.2好处
- 开发当中有一个非常重要的原则”开闭原则“ ,对拓展开放、对修改关闭
- 工厂模式主要负责对象的创建问题
- 可通过反射进行工厂模式的设计,完成动态创建对象
//接口
public interface Phone {
void call();
void send();
}
//手机类
public class HuaWeiPhone implements Phone {
@Override
public void call() {
System.out.println("华为手机打电话");
}
@Override
public void send() {
System.out.println("华为手机发短信");
}
}
public class XiaoMiPhone implements Phone {
@Override
public void call() {
System.out.println("小米手机打电话");
}
@Override
public void send() {
System.out.println("小米手机发短信");
}
}
//工厂类
public class PhoneFactory {
// public static Phone getPhone(String type){
// if (type.equals("小米")){
// return new XiaoMiPhone();
// }else if (type.equals("华为")){
// return new HuaWeiPhone();
// }else {
// return null;
// }
// }
public static Phone getPhone(String className){
try {
Class<?> c = Class.forName(className);
return (Phone) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
BeanFactory
import jdk.nashorn.internal.ir.CallNode;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
//所有的对象创建好 用的时候直接拿
public class BeanFactory {
static Map<String,Object> map= new HashMap<String,Object>();
static {
try {
//1.读取文件 properties
Properties properties = new Properties();
//将指定流中的内容映射到properties中
// properties.load(new FileInputStream("src\\bean.properties"));
//getResourceAsStream 类对象可以获取到类加载器 像流一样获取到资源
properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"));
//2.创建类的对象
Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
for (Map.Entry<Object, Object> entry: entrySet) {
//3.存储到Map集合当中
Class<?> c = Class.forName((String) entry.getValue());
map.put((String) entry.getKey(),c.newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String name){
return map.get(name);
}
}
Bean.Properties
配置文件,设置类和要运行的方法,通过反射的方式运行配置文件中的方法
#key=value
xiaomi=com.qf.factory.XiaoMiPhone
huawei=com.qf.factory.HuaWeiPhone
测试类
public class Test {
public static void main(String[] args) {
//需要一个对象
// XiaoMiPhone xiaoMiPhone = new XiaoMiPhone();
// xiaoMiPhone.call();
// xiaoMiPhone.send();
//以上代码 调用者和对象之间产生耦合 解决:将创建对象的任务交给工厂
// Phone phone = PhoneFactory.getPhone("华为");
// phone.call();
// phone.send();
//以上代码:工厂跟手机对象产生了耦合 解决利用反射创建对象
// Phone phone = PhoneFactory.getPhone("com.qf.factory.XiaoMiPhone");
// phone.call();
// phone.send();
//以上代码 有硬编码 写死了 解决:提前创建好对象(配置文件)
Phone xiaomi = (Phone) BeanFactory.getBean("xiaomi");
xiaomi.send();
xiaomi.call();
Phone huawei = (Phone) BeanFactory.getBean("huawei");
huawei.call();
huawei.send();
}
}
2.3单例模式
- 让类的对象只能有唯一的一个实例
- 如何实现单例:
- 私有化构造方法
- 提供一个方法 返回这个类的实例
懒汉模式
public class Singleton01 {
private static volatile Singleton01 instance;
//解决两个问题 线程可见 和 防止指令循环
private Singleton01(){ }
// public static Singleton01 getInstance(){
// if (instance==null){//当这个值为null的时候 那么就可以创建实例
// instance = new Singleton01();
// }
// //不为null的时候 就还是那个实例
// return instance;
// }
// public static synchronized Singleton01 getInstance(){
// //大量的逻辑代码 。。没必要上锁的 锁的力度没必要那么大
//
//
// if (instance==null){//当这个值为null的时候 那么就可以创建实例
// instance = new Singleton01();
// }
// //不为null的时候 就还是那个实例
// return instance;
// }
public static synchronized Singleton01 getInstance(){
//大量的逻辑代码 。。
//DCL double check lock
if (instance==null){
synchronized (Singleton01.class){
if (instance==null){
instance = new Singleton01();//jvm底层中先后顺序多线程情况下会导致赋值的情况不一致
}
}
}
//不为null的时候 就还是那个实例
return instance;
}
}
饿汉模式
//饿汉模式 没有线程安全的问题
public class Singleton02 {
private static Singleton02 instance = new Singleton02();//唯一就一次
//有的时候不想用对象 只是加载一下或者做别的事情 就new 了一个对象
private Singleton02(){
}
public static Singleton02 getInstance(){
return instance;
}
}
测试
public class Test {
public static void main(String[] args) {
Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
反射破坏单例
private static volatile Singleton02 singleton02;
private static Object obj = new Object();
private Singleton02(){
if(singleton02 != null){
throw new IllegalArgumentException("不要试图使用反射破坏单例");
}
}
测试
public class Test {
/**
* 单例模式:类的对象在整个应用程序中只能有唯一的一个实例
* 单例实现:
* 1、私有化构造方法(没有意义)
* 2、对外提供公共的方法返回对象的实例
* 单例分类:
* 懒汉模式
* 优点:什么时候用,什么时候创建,节约资源
* 缺点:线程不安全
* 解决:1、同步方法(粒度太大)
* 2、双重检验锁(a、指令重排 b、线程可见性)
* 3、双重检验锁+volatile
* 饿汉模式
* 优点:天生线程安全
* 缺点:浪费资源
* 反射会破坏单例模式
*/
public static void main(String[] args) {
Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
总结
单例模式:
- 让类的对象只能有唯一的一个实例
单例分类:
- 懒汉式
- 好处:避免资源浪费(需要对象的时候就创建对象)
- 缺点 :线程不安全
- 解决:
- DCL(双重检测锁)
- DCL的问题:1、指令重排 2、多线程可见性
- 解决:volatile关键字修饰
- 使用静态内部类(什么时候使用静态内部类,什么时候就加载)
- DCL(双重检测锁)
- 饿汉式
- 好处:线程安全
- 缺点:浪费资源。只要类被加载,那么这个类实例就会被创建
如何实现单例
- 1、私有化构造方法
- 2、提供一个方法,返回这个类的实例
反射破坏单例的问题:
- 解决:在构造方法中加上判断,如果对象不为空则直接抛出异常
五、枚举
枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。 表示状态 用常量不方便 jdk之后出现了枚举
一般已经确定类的对象个数,可以使用枚举定义
-
枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性
-
定义枚举使用
enum
关键字 -
枚举的本质:
- 枚举是一个终止类,并继承Enum抽象类
- 枚举中常量是当前类型的静态常量
案例
public enum Color {
RED,GREEN,YELLOW;
private Color() {}
private Color(String name) {}
//定义属性
String name;
//定义方法
public void show() {
}
}
测试
public class Test {
public static void main(String[] args) {
Color color = Color.RED;
switch (color) {
case RED:
System.out.println("红");
break;
case GREEN:
System.out.println("绿");
break;
case YELLOW:
System.out.println("黄");
break;
default:
break;
}
}
}
注意:
- 枚举中必须要包含枚举常量,也可以包含属性、方法、私有构造方法
- 枚举常量必须在前面,多个常量之间使用逗号隔开,如果没有定义其他内容,最后分java号可写可不写
六、注解
6.1 概念
注解(Annotation):是代码里的特殊标记, 程序可以读取注解,一般用于替代配置文件
开发人员可以通过注解告诉类如何运行
- 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类
public class Demo01 {
public static void main(String[] args) {
@SuppressWarnings("unused") //压制警告 不让警告这个东西没有用过
int a = 10;
}
}
@Deprecated //过时的
class Person{
@Deprecated
public void show() {}
}
class User extends Person{
@Override
public void show() {
}
}
6.2 定义注解
定义注解使用@interface关键字,注解中只能包含属性
常见注解:@Override
案例演示
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Demo02 {
}
/**
* 1、注解定义使用@interface关键字
* 2、注解中只能有属性
* 3、如果注解中的属性是value,且只有一个必填属性的时候value=可以省略
* 4、如果注解中的属性有默认值使用的时候可以不用设置
* 5、注解中的属性类型可以是:基本类型、String类型、注解类型、数组
* 6、如果注解中的属性是数组类型,且属性值只有一个的时候{}可以省略不写
*
*
* 元注解:注解上的注解
* @Target:表示注解的作用范围(默认是所有地方都可以用)
* TYPE 接口,FIELD 属性, METHOD方法, PARAMETER形参, CONSTRUCTOR构造方法, LOCAL_VARIABLE 局部变量, ANNOTATION_TYPE 注解,
* PACKAGE 包, TYPE_PARAMETER 泛型, TYPE_USE
*
* @Retention:表示注解的生命周期(默认只在编译期有效)
* SOURCE(编译期),CLASS(编译期~运行期), RUNTIME(运行期)
*/
@MyAnnotation(value="aaa",name="bbb",hobby= {"aaa","bbb","ccc"})
class Student{
//@MyAnnotation(name="bbb") 都可以用
String aaa;
//@MyAnnotation(name="ccc")
public void show(/*@MyAnnotation(name="bbb")*/ String name) {
}
}
@Target(ElementType.TYPE) //表示注解的作用范围(默认是所有地方都可以用)
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期(默认只在编译期有效)
@interface MyAnnotation{
String value();
String name() default "xxx"; //如果注解中的属性有默认值使用的时候可以不用设置
String[] hobby();
int age() default 11; //如果注解中的属性有默认值使用的时候可以不用设置
}
6.3 使用注解
@MyAnnotation(value="aaa",name="bbb",hobby= {"aaa","bbb","ccc"})
class Student{
String aaa;
public void show(String name) {
}
}
6.4 使用注解注意事项
- 注解中只能是属性
- 如果注解中属性只有一个,且是value。那么使用的时候,value=可以省略
- 如果属性有默认值,那么在使用的时候可以不填
- 如果属性是一个数组,那么在使用的时候如果只有一个参数,大括号可以省略
6.5 元注解
加在注解上的注解
@Retention:定义注解的使用范围
- Class (默认的) class源文件期间保留
- Source 编译期间保留
- Runtime 在运行期保留
@Target: 定义注解的使用范围
- TYPE, 类上使用
- FIELD, 属性上使用
- METHOD, 方法上使用
- PARAMETER, 参数上使用