第十七天

单例设计模式
饿汉式实现方式
package cn.tedu.single;
/*本类用于测试单例设计模式1     饿汉式*/
public class Singleton1 {
    public static void main(String[] args) {
        MySingle single1=MySingle.getSingle();
        MySingle single2=MySingle.getSingle();
        System.out.println(single1==single2);//比较的是地址值,说明是同一个对象
    }
}
class MySingle{
    /*1.构造方法私有化的目的:为了不让外界随意实例化\new本类对象*/
    private MySingle(){ }

    /*3.构造方法和对象私有化后,通过公共的访问点来获取对象,那外界如何调用这个方法呢?
    * 之前我们都是在外部创建本类对象并进行方法调用,但是现在单例程序中外部无法直接创建本类对象
    * 解决方案;我们可以利用之前学习的静态的概念,将方法修饰成静态的,就可以通过类名直接调用啦
    * 注意事项:静态只能调用静态,所以静态方法中返回的对象也需用静态修饰*/
    private static MySingle single=new MySingle();

    /*2.也就是以公共的方式向外界提供获取本类私有对象的方法*/
    public static MySingle getSingle(){
        return single;
    }
}
懒汉式实现方式
public class Singleleton2 {
    public static void main(String[] args) {
        MySingle2 s1=MySingle2.getMySingle2();
        MySingle2 s2=MySingle2.getMySingle2();
        System.out.println(s1==s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}
class MySingle2{
    private MySingle2(){}
    private static MySingle2 single2;
    static Object o=new Object();
    /*问题:程序中有共享资源single2,并且有多条语句操作了共享数据
    * 此时single2共享资源在多线程环境下一定会存在多线程数据安全隐患
    * 解决方案1:同步代码块(加锁)
    * 解决方案2:同步方法[如果方法中的所有代码都需要被同步,那么这个方法可以修饰成同步方法]
    * 注意事项:锁对象在静态方法中,不可以使用this,因为静态资源优先于对象加载
    * 锁对象可以使用外部创建好的唯一的锁对象o,但请注意,需要是静态的,静态只能调用静态*/

    synchronized public static MySingle2 getMySingle2(){
        /*注意:这需要增加一个判断
        * 如果调用方法时single2的值为null,说明之间没有new过,保存的是默认值
        * 这时才需要new对象,如果single2的值不为null,直接return single2即可*/
        synchronized (o){
            if(single2==null){
                single2=new MySingle2();
            }
            return single2;
        }


    }
}
关于单例模式的两种实现方式

1.饿汉式 : 不管你用不用这个类的对象,都会直接先创建一个
2.懒汉式 : 先不给你创建这个类的对象,等你需要的时候再帮你创建 利用了延迟加载的思想
延迟加载的思想:是指不会再第一时间就把对象创建好来占用内存,而是什么时候用到,什么时候再去创建对象
3.线程安全问题 : 时候共享资源有线程并发的数据安全隐患,可以通过加锁的方式[同步代码块/同步方法]

反射

获取字节码对象
  • Class.forName(“包名.类名”);
  • 类名.class
  • 对象.getClass();
单元测试方法:

是java测试的最小范围,使用灵活,非常推荐
语法要求: @Test + void + 没有参数 + public
注意:使用时需要导包

测试反射
先准备物料类,然后测试反射

单元测试1:用来测试获取指定类的字节码对象

@Test
    public void getClazz() throws ClassNotFoundException {
        Class<?> student1=Class.forName("cn.tedu.reflection.Student");
        Class<?> student2=Student.class;
        Class<?> student3=new Student().getClass();
        System.out.println(student1);//打印通过反射获取到的字节码对象
        System.out.println(student2.getName());//获取全路径名
        System.out.println(student3.getSimpleName());//获取类名
        System.out.println(student3.getPackage().getName());//获取包名
    }

单元测试2:获取构造方法

@Test
    public  void getConstruct(){
        Class<?> clazz=Student.class;
        Constructor<?>[] cs=clazz.getConstructors();
        for(Constructor c:cs){
            System.out.println(c.getName());
            Class[] cp=c.getParameterTypes();
            System.out.println(Arrays.toString(cp));
        }
    }

单元测试3:获取普通方法

@Test
    public void getFunction() throws ClassNotFoundException {
        //获取字节码对象
        Class<?> zj = Class.forName("cn.tedu.reflection.Student");
        //获取指定类中的所有普通方法
        Method[] ms=zj.getMethods();
        //遍历存放所有方法的数组
        for(Method m:ms){
            //通过当前遍历到的方法对象获取当前方法的名字
            System.out.println(m.getName());
            //通过当前遍历到的方法对象获取当前方法的参数类型
            Class<?>[] pt=m.getParameterTypes();
            System.out.println(Arrays.toString(pt));
        }
    }

单元测试办法4:获取成员变量

@Test
    public void getFields(){
        /**注意:    目前所有成员变量的修饰符必须是public才能获取到
         * 如果属性采用的是默认的修饰符,是反射不到的*/
        /*Class<?>中的"?"是泛型约束的通配符,类似于"*"*/
        Class<?> clazz=Student.class;
        Field[] fs=clazz.getFields();
        for(Field f:fs){
            System.out.println(f.getName());
            System.out.println(f.getType().getName());
        }
    }

单元测试5:通过单元测试来反射创建对象
* 方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance,触发时对应类型的构造函数来创建对象

@Test
    public void makeObject() throws Exception {
        Class<?> clazz=Student.class;//触发的是无参构造
        Object obj=clazz.newInstance();
        System.out.println(obj);
        /**注意参数类型的指定,是对应类型的字节码对象,不能直接光写类型*/
        Constructor<?> c=clazz.getConstructor(String.class,int.class);
        Object obj2=c.newInstance("小灰灰",5);/**此处是多态的向上造型*/
        System.out.println(obj2);
        /**查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类对象
         * 为什么要把Object转成子类类型呢?因为想要使用子类的属性或者是特有功能
         * 父类是无法使用子类的特有属性和功能的,上面的obj2就是Object类型
         * Object类中没有Student中的属性和特有功能
         * 这种把之前看做是父类类型的子对象,重新转回子类对象的现象,叫做向下造型
         * */
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值