反射、设计模式、枚举、注解的简单介绍

反射、设计模式、枚举、注解的简单介绍

一、反射

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单例模式

  • 让类的对象只能有唯一的一个实例
  • 如何实现单例:
    1. 私有化构造方法
    2. 提供一个方法 返回这个类的实例
懒汉模式
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);
	}
}
总结

单例模式:

  • 让类的对象只能有唯一的一个实例

单例分类:

  • 懒汉式
    • 好处:避免资源浪费(需要对象的时候就创建对象)
    • 缺点 :线程不安全
    • 解决:
      1. DCL(双重检测锁)
        • DCL的问题:1、指令重排 2、多线程可见性
        • 解决:volatile关键字修饰
      2. 使用静态内部类(什么时候使用静态内部类,什么时候就加载)
  • 饿汉式
    • 好处:线程安全
    • 缺点:浪费资源。只要类被加载,那么这个类实例就会被创建

如何实现单例

  • 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 使用注解注意事项

  1. 注解中只能是属性
  2. 如果注解中属性只有一个,且是value。那么使用的时候,value=可以省略
  3. 如果属性有默认值,那么在使用的时候可以不填
  4. 如果属性是一个数组,那么在使用的时候如果只有一个参数,大括号可以省略

6.5 元注解

加在注解上的注解

@Retention:定义注解的使用范围

  • Class (默认的) class源文件期间保留
  • Source 编译期间保留
  • Runtime 在运行期保留

@Target: 定义注解的使用范围

  • TYPE, 类上使用
  • FIELD, 属性上使用
  • METHOD, 方法上使用
  • PARAMETER, 参数上使用
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值