第三周复习总结

2 篇文章 0 订阅

一、泛型

1. 为什么使用泛型

泛型,即“参数化类型”,早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2.什么是泛型

泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,而这种参数类型可以用在类、方法和接口中,分别被称为泛型类泛型方法泛型接口

注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

3.使用泛型的好处

  • 避免了类型强转的麻烦。
  • 它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。

4. 泛型的使用

泛型虽然通常会被大量的使用在集合当中,但是我们也可以完整的学习泛型只是。泛型有三种使用方式,分别为:泛型类、泛型方法、泛型接口。将数据类型作为参数进行传递。

二、集合

1.什么是集合

    1、集合类存放于java.util包中。

    2、集合类型主要有3种:set(集)、list(列表)和map(映射)。

    3、集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。

    简单来讲:集合就是一个放数据的容器,准确的说是放数据对象引用的容器。

2.集合的关系

在这里插入图片描述

 

3.集合的继承结构

Collection接口
List 接口【数据有下标,有序,可重复】
ArrayList子类
LinkedList子类
Set 接口【数据无下标,无序,不可重复】
HashSet子类
Map 接口【键值对的方式存数据】
HashMap子类

4.Collection接口介绍

Collection c = new ArrayList();
		Iterator it = c.iterator();
		while(it.hasNext()){
			Object ob = it.next();
		}
public class TestCollection {
    public static void main(String[] args) {
        //1.创建Collection对象
        Collection<Integer> collection = new ArrayList<>();
        //2.向集合添加元素
        collection.add(123);
        collection.add(234);
        collection.add(345);
        //3.打印集合中的内容
        System.out.println(collection);
        //4集合的常用方法测试
        //4.1清空集合
        collection.clear();
        //4.2获取对象的哈希码值
        System.out.println(collection.hashCode());
        //4.3打印集合的具体元素
        System.out.println(collection.toString());
        //4.4判断集合对象中是否有指定元素
        System.out.println(collection.equals(200));
        //4.5判断集合是否为空
        System.out.println(collection.isEmpty());
        //4.6移除集合中的指定元素
        System.out.println(collection.remove(200));
        //4.7返回元素的个数
        System.out.println(collection.size());
        //4.8将指定的集合转为数组object
        Object[] objects = collection.toArray();
        System.out.println(Arrays.toString(objects));
        //5.操作集合与集合之间的常用方法
        //5.1创建Collection对象
        Collection<Integer> collection2 = new ArrayList<>();
        //5.2向集合添加元素
        collection2.add(111);
        collection2.add(222);
        collection2.add(333);
        //5.3把集合2 添加到 集合1 中
        System.out.println(collection.containsAll(collection2));
        //5.4查看是否包含指定元素
        System.out.println(collection.contains(123));
        //5.5删除集合1中的集合2的所有元素
        System.out.println(collection.removeAll(collection2));
        //5.6取集合的公共元素
        System.out.println(collection.retainAll(collection2));
        //6.遍历集合
        //6.1增强for变量
        for (Integer integer : collection) {
            System.out.println(integer);
        }
        //6.2迭代器变量
        Iterator<Integer> iterator = collection.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

5.List集合

1.概述

有序的colletion(也称为序列).此接口的用户可以对列表中的每个元素的插入位置进行精确的控制,用户可以根据元素的整数索引(在列表中的位置)来访问元素,并搜索列表中的元素.

2.特点

  1. 元素都有下标
  2. 数据是有序的
  3. 允许存放重复的元素

3.使用方式同上 Collection一样

在这里插入图片描述

4.List的遍历  

public class TsetList {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(123);
        list.add(234);
        list.add(345);
        //第一种高效遍历
        for (Integer integer : list) {
            System.out.println(integer);
        }
        //第二种遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //第三种 使用iterator()遍历
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //第四种 使用listIterator() 遍历
        ListIterator<Integer> integerListIterator = list.listIterator();
        while (integerListIterator.hasNext()) {
            System.out.println(integerListIterator.next());
        }
        //反向遍历
        while (integerListIterator.hasPrevious()) {
            System.out.println(integerListIterator.previous());
        }
    }
}

6.ArrayList集合

1.概述

1.List接口的实现类
2.通常可以根据下标进行操作
3.元素有下标,有序,可重复
4.底层结构是数组,内存空间是连续的
5.增删操作比较慢,查询操作比较快【数据量比较大时】
6.初始化量为10,扩容以1.5倍扩容

2.List的遍历  

//方式1:for循环
        System.out.println("方式一:for循环迭代");
        for(int i = 0;i <= list.size()-1 ; i++){
            System.out.println(list.get(i));
        }
        //方式2:高效for循环
        System.out.println("方式二:高效for循环迭代");
        //for(本轮遍历到元素的类型 元素名 : 要遍历的集合名){循环体}
        for(Integer t : list){
            System.out.println(t);
        }
        //方式3:iterator迭代器
        System.out.println("方式三:iterator迭代器:");
        //获取迭代器对象
        Iterator<Integer> it = list.iterator();
        //循环迭代集合中的所有元素
        while(it.hasNext()){//判断是否有下一个元素可迭代,如果有,继续循环
            System.out.println(it.next());//打印本轮迭代到的元素
        }
        //方式4:listIterator
        System.out.println("方式四:list独有的迭代器listIterator");
        //获取迭代器对象
        ListIterator<Integer> it2 = list.listIterator();
        //循环迭代集合中的所有元素
        while(it2.hasNext()){//判断是否有下个元素可迭代
            System.out.println(it2.next());//打印本轮迭代到的元素
        }

7.LinkedList

1.概述

链表,两端效率高,底层就是链表实现的

1.List接口的实现类
2.元素有下标,有序,可重复
3.底层的数据结构是链表,内存空间是不连续的
4.通过进行首尾节点的操作比较多
5.增删操作比较快,查询操作比较慢【数据量比较大时】
注意:LinkedList的查询操作也不是都慢,首尾操作还是比较快的

在这里插入图片描述

请添加图片描述 在这里插入图片描述

 2.使用方法

public class TestLinkedList {
    public static void main(String[] args) {
        //LinkedList常用方法
        //1.创建LinkedList对象
        LinkedList<Integer> list = new LinkedList<>();
        //2.添加数据
        list.add(123);
        list.add(234);
        list.add(345);
        //3.方法测试练习
        //3.1添加首元素
        list.addFirst(123);
        //3.2添加尾元素
        list.addLast(234);
        //3.3获取首元素
        System.out.println(list.getFirst());
        //3.4添加尾元素
        System.out.println(list.getLast());
        //3.5移除首元素,成功移除会返回移除的数据
        System.out.println(list.removeFirst());
        //3.6移除尾元素,成功移除会返回移除的数据
        System.out.println(list.removeLast());
        //3.7获取但不移除此列表的首元素(第一个元素)
        System.out.println(list.element());
        //3.8获取但不移除此列表的首元素(第一个元素)
        System.out.println(list.peek());
        //3.9获取但不移除此列表的首元素(第一个元素)
        System.out.println(list.peekFirst());
        //4.0获取但不移除此列表的尾元素(最后一个元素)
        System.out.println(list.peekLast());
        //4.1将指定元素添加到列表末尾
        System.out.println(list.offer(123));
        //4.2将指定元素插入列表开头
        System.out.println(list.offerFirst(345));
        //4.3将指定元素插入列表末尾
        System.out.println(list.offerLast(455));
        //4.4获取并且移除此列表的首元素(第一个元素),成功移除,返回移除元素
        System.out.println(list.poll());
        //4.5获取并且移除此列表的首元素(第一个元素),成功移除,返回移除元素,如果此列表为空,则返回null
        System.out.println(list.pollFirst());
        //4.6获取并且移除此列表的尾元素(最后一个元素),成功移除,返回移除元素,如果此列表为空,则返回null
        System.out.println(list.pollLast());
    }
}

 8.Map接口

1.1 概述

Java.util接口Map<K,V>
类型参数 : K - 表示此映射所维护的键 V – 表示此映射所维护的对应的值
也叫做哈希表、散列表. 常用于键值对结构的数据.其中键不能重复,值可以重复

1.2 特点

  1. Map可以根据键来提取对应的值
  2. Map的键不允许重复,如果重复,对应的值会被覆盖
  3. Map存放的都是无序的数据
  4. Map的初始容量是16,默认的加载因子是0.75

1.3使用方式

public class TestMap {
    public static void main(String[] args) {
        //1.创建对象
        Map<Integer,String> map = new HashMap<>();
        //2.向map集合存入数据
        map.put(1,"巢云飞");
        map.put(2,"亿万富翁");
        /**1.map中存放着的都是无序的数据
         * 2.map中的value可以重复-比如我们可以存两个黑熊精
         * 3.map中的key不允许重复,如果重复,后面的value会把前面的value覆盖掉
         **/
        //3.方法测试
        //3.1清空集合
        map.clear();
        //3.2获取集合的哈希码
        System.out.println(map.hashCode());
        //3.3判断“黄毛怪”是否与集合对象相等
        System.out.println(map.equals("我"));
        //3.4判断集合是否为空
        System.out.println(map.isEmpty());
        //3.5获取集合中的元素个数
        System.out.println(map.size());
        //3.6判断当前map集合中是否包含指定key键
        System.out.println(map.containsKey(1));
        //3.7判断当前map集合中是否包含指定的Value
        System.out.println(map.containsValue("巢云飞"));
        //3.8根据key值获取到对应的value值
        System.out.println(map.get(1));
        //3.9根据此key值对应的键值对,K与V都删了
        System.out.println(map.remove(1));
        //4.0将map集合中的所有value取出,放入Collection集合中
        //Collection<Type>中Type的类型,取决于map中value的类型
        Collection<String> values = map.values();
        System.out.println(values);

        //4.遍历迭代器
        /**方式一:
         * 遍历map中的数据,但是map本身没有迭代器,所以需要先转换成set集合
         * Set<Key>:把map中的所有key值存入到set集合当中--keySet()*/
        //4.1将map集合中的key值取出存入set集合中,集合的泛型就是key的类型Integer
        Set<Integer> set = map.keySet();
        System.out.println(set);
        //4.2想要遍历集合就需要获取集合的迭代器
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()){
            Integer key = iterator.next();
            String value = map.get(set);
            System.out.println();
        }
        /**方式二:
         * 遍历map集合,需要把map集合先转成set集合
         * 是把map中的一对键值对key&value作为一个Entry<K,V>整体放入set
         * 一对K,V就是一个Entry*/
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
        //获取迭代器
        Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();
        while(it2.hasNext()){//判断是否有下一个元素可迭代
            //本轮遍历到的一个Entry对象
            Map.Entry<Integer, String> entry = it2.next();
            Integer key = entry.getKey();//获取Entry中的key
            String value = entry.getValue();//获取Entry中的value
            System.out.println("{"+key+","+value+"}");
        }
    }
}

9.HashMap接口

1 前言


HashMap的键要同时重写hashCode()和equlas()
hashCode()用来判定二者的hash值是否相同,重写后根据属性生成
equlas()用来判断属性的值是否相同,重写后,根据属性判断
–equlas()判断数据如果相等,hashCode()必须相同
–equlas()判断数据如果不等,hashCode()尽量不同
 

2.HashMap的存储过程:

  1. HashMap的结构是数组+链表 或者 数组+红黑树 的形式
  2. HashMap底层的Entry[ ]数组,初始容量为16,加载因子是0.75f,扩容按约为2倍扩容
  3. 当存放数据时,会根据hash(key)%n算法来计算数据的存放位置,n就是数组的长度,其实也就是集合的容量
  4. 当计算到的位置之前没有存过数据的时候,会直接存放数据
  5. 当计算的位置,有数据时,会发生hash冲突/hash碰撞,解决的办法就是采用链表的结构,在数组中指定位置处以后元素之后插入新的元素,也就是说数组中的元素都是最早加入的节点
  6. 如果链表的长度>8且数组长度>64时,链表会转为红黑树,当链表的长度<6时,红黑树会重新恢复成链表
     
  7. 线程不安全的

在这里插入图片描述

达到容量的加载因子后,就会重新开辟空间,重新计算所有对象的存储位置,也叫做rehash * 设置初始容量与加载因子要讲求相对平衡,如果加载因子过低,则rehash过于频繁,影响性能 * 如果初始容量设置太高或者加载因子设置太高,影响查询效率 

10.Set集合

 在这里插入图片描述

1.概述

  1. Set是一个不包含重复数据的Collection
  2. Set集合中的数据是无序的(因为Set集合没有下标)
  3. Set集合中的元素不可以重复 – 常用来给数据去重

2.Set集合的特点

  1. 数据无序且数据不允许重复
  2. HashSet : 底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
  3. TreeSet : 底层是TreeMap,也是红黑树的形式,便于查找数据

3.使用方法同上Collection

11.HashSet

1.概述:

底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K存入内部的HashMap中,其中K不允许重复,允许使用null.

1.线程不完全

2.数据没有下标

3.没有重复数据

package cn.tedu.collection;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/*本类用于测试Set*/
public class TestSet {
    public static void main(String[] args) {
        //1.创建对应的集合对象
        Set<String> set = new HashSet<>();
        //2.存入数据
        set.add("紫霞仙子");
        set.add("至尊宝");
        set.add("蜘蛛精");
        set.add("紫霞仙子");
        set.add(null);
        /*1.set集合中的元素都是没有顺序的
        * 2.set集合中的元素不能重复
        * 3.set集合可以存null值,但是最多只有一个*/
        System.out.println(set);//[蜘蛛精, null, 至尊宝, 紫霞仙子]

        //3.常用方法测试
        System.out.println(set.contains("唐僧"));//false,判断是否包含指定元素
        System.out.println(set.isEmpty());//false,判断是否为空
        System.out.println(set.remove(null));//true,移除指定的元素
        System.out.println(set);//[蜘蛛精, 至尊宝, 紫霞仙子]
        System.out.println(set.size());//3,获取集合中元素的个数
        System.out.println(Arrays.toString(set.toArray()));//[蜘蛛精, 至尊宝, 紫霞仙子],将集合转为数组

        //4.1创建set2集合,并向集合中存入数据
        Set<String> set2 = new HashSet<>();
        set2.add("小兔纸");
        set2.add("小脑斧");
        set2.add("小海疼");
        set2.add("小牛犊");
        System.out.println(set2);//[小兔纸, 小海疼, 小牛犊, 小脑斧]
        System.out.println(set.addAll(set2));//将set2集合的所有元素添加到set集合中
        System.out.println(set);//[蜘蛛精, 小兔纸, 小海疼, 至尊宝, 小牛犊, 小脑斧, 紫霞仙子]
        System.out.println(set.containsAll(set2));//判断set2集合的所有元素是否都在set集合中
        System.out.println(set.removeAll(set2));//删除set集合中属于set2集合的所有元素
        System.out.println(set);//[蜘蛛精, 至尊宝, 紫霞仙子]
        System.out.println(set.retainAll(set2));//只保留set集合中属于set和set2集合的公共元素
        System.out.println(set);//[]

		//5.集合的迭代
		Iterator<String> it = set2.iterator();//5.1获取集合的迭代器
		while(it.hasNext()) {//5.2判断集合是否有下个元素
			String s = it.next();//5.3如果有,进循环获取当前遍历到的元素
			System.out.println(s);
		}
    }
}

四、多线程

1.进程 线程 多线程

        核心概念:

  1. 线程就是独立的执行路径
  2. 在程序运行时,即使没有自己创建线程,后台也会有多个线程
  3. main()称之为主线程,为系统的入口,用于执行整个程序
  4. 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度也就是cpu,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
  5. 对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制
  6. 线程会带来额外的开销,如cpu调度时间,并发控制开销
  7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2.三种创建方式

1.继承Thread类(重点)

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行方法
  • 创建线程对象,调用start()方法启动线程
  • 启动线程不是立即执行,而是调度器进行分配后执行(分配时间片和执行顺序)
public class TestThread1 {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();
        //启动线程
        myThread1.start();
        myThread2.start();
        myThread3.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("这是主线程:"+i);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i+"----"+getName());
        }
    }
}

2.实现Runnable接口 (重点)

  • 定义MyRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
  • 推荐使用Runnable对象,因为java单继承的局限性
public class TestRunnable1 implements Runnable{
        @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程:"+i+"   "+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
            //创建接口的实现类对象
            TestRunnable1 t1 = new TestRunnable1();
            TestRunnable1 t2 = new TestRunnable1();
            TestRunnable1 t3 = new TestRunnable1();
            //创建线程的对象,通过线程对象来开启我们的线程,代理
            new Thread(t1).start();
            new Thread(t2).start();
            new Thread(t3).start();
            //另一个参数可以设置线程的名字
            new Thread(t1,"小名").start();
            new Thread(t2,"小黄").start();
            new Thread(t3,"小红").start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程:"+i);
        }
    }
}

买票练习:

public class TestRunnable2 implements Runnable{
    private int mp = 10;

    @Override
    public void run() {
        while(true){
            if (mp <= 0)break;

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+mp--+"票");
        }
    }

    public static void main(String[] args) {
        TestRunnable2 testRunnable21 = new TestRunnable2();
        //出现并发问题
        //问题1:产生了重卖的现象:同一张票卖了多个人
        //问题2:产生了超卖的现象:超出了规定的票数100,出现了0 -1 -2这样的票
        new Thread(testRunnable21,"xiaoming----").start();
        new Thread(testRunnable21,"xiaozhang----").start();
        new Thread(testRunnable21,"xiaomi-----").start();

    }
}

3.实现Callable接口(了解)

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Excutors.newFixedThreadPool(1);
  5. 提交执行:Future<Boolean> result1 = ser.submit(1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();
public class TestCallable implements Callable<Integer> {
    static int i = 10;
    String ck;

    public TestCallable() {}

    public TestCallable(String ck) {
        this.ck = ck;
    }

    @Override
    public Integer call() throws Exception {
        while (true){
            if (i==0)break;
            System.out.println(ck +"卖了"+i--+"了");
        }
        return i;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testCallable1 = new TestCallable("窗口1");
        TestCallable testCallable2 = new TestCallable("窗口2");
        TestCallable testCallable3 = new TestCallable("窗口3");
        TestCallable testCallable4 = new TestCallable("窗口4");

        ExecutorService service = Executors.newFixedThreadPool(4);

        Future<Integer> submit1 = service.submit(testCallable1);
        Future<Integer> submit2 = service.submit(testCallable2);
        Future<Integer> submit3 = service.submit(testCallable3);
        Future<Integer> submit4 = service.submit(testCallable4);

        Integer integer1 = submit1.get();
        Integer integer2 = submit2.get();
        Integer integer3 = submit3.get();
        Integer integer4 = submit4.get();

        service.shutdownNow();
    }

3.线程的方法

1.停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法。
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量
package cn.tedu.review;

public class TestStop implements Runnable{
    static boolean flag = true;
    public void stop(){
        this.flag = false;
    }

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println(Thread.currentThread().getName()+"---"+i++);
        }
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop,"小张").start();
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
            if (i == 9) {
                testStop.stop();
                System.out.println("线程结束了");
            }
        }
    }
}

2.线程休眠

  • sleep指定当前线程
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延迟时,倒计时等
  • 每个对象都有一个锁,sleep不会释放锁

3.线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功看cpu心情!!
  • yield()

4.Join

  • Join合并线程,待此线程执行完成后,再执行其他线程,其它的线程阻塞
  • 可以想象成插队
  • 不建议使用,防止线程阻塞

5.线程状态观测

        Thread.State

6.线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程执行

线程的优先级用数字表示  1-10

getPriority()  获取优先级

setPriority(int i)改变优先级

7.守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • setDaemon(boolean true);

4.线程的状态

 

 5.线程同步

在多线程程序中 + 有共享数据 + 多条语句操作共享数据

同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权力的线程排队。

坏处就是效率低,不过保证了安全

异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢夺资源,坏处就是安全隐患,效率要高一些。

十、单例设计模式

1.什么是设计模式?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。(百度解释)

其实设计模式是人们实践的产物,在初期的开发过程中好多人发现再进行重复的代码书写,那些开发大牛们就不断总结、抽取最终得到了大家的认可于是就产生了设计模式,其实设计模式的种类可以分为23种左右,今天主要和大家一起学习一下单例设计模式,因为这种设计模式是使用的最多的设计模式。

2.为什么会有单例设计模式?

可以保证一个类在内存中的对象的唯一性,在一些常用的工具类、线程池、缓存,数据库,账户登录系统、配置文件等程序中可能只允许我们创建一个对象,一方面如果创建多个对象可能引起程序的错误,另一方面创建多个对象也造成资源的浪费。在这种基础之上单例设计模式就产生了因为使用单例能够保证整个应用中有且只有一个实例,


3.单例模式的设计思想

(1)不允许其他程序用new对象。

    因为new就是开辟新的空间,在这里更改数据只是更改的所创建的对象的数据,如果可以new的话,每一次new都产生一个对象,这样肯定保证不了对象的唯一性。

(2)在该类中创建对象
   因为不允许其他程序new对象,所以这里的对象需要在本类中new出来

(3)对外提供一个可以让其他程序获取该对象的方法

   因为对象是在本类中创建的,所以需要提供一个方法让其它的类获取这个对象。

那么这三步怎么用代码实现呢?将上述三步转换成代码描述是这样的

(1)私有化该类的构造函数
(2)通过new在本类中创建一个本类对象
(3)定义一个公有的方法,将在该类中所创建的对象返回


单例模式的写法  请查看:Java设计模式—单例设计模式(Singleton Pattern)完全解析_dmk877的专栏-CSDN博客_单例设计模式

    

十一、反射

1.反射的概念

        JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

反射就是把java类中的各种成分映射成一个个的Java对象

Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

2.为什么要是有反射

类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象

3.反射的使用

1.获取Class对象的三种方式

//第一种   【常用】
Class<Student> studentClass = Student.class;
//第二种
Class<?> aClass = Class.forName("cn.tedu.cn.Student");
//第三种
Class<? extends Student> aClass1 = new Student().getClass();

2.获取类对象

System.out.println("==============获取类对象==============");
System.out.println(studentClass);
System.out.println(aClass);
System.out.println(aClass1);
System.out.println("==============获取当前字节码对象的名字==============");
System.out.println(studentClass.getName());
System.out.println("==============获取类的类名==============");
System.out.println(studentClass.getSimpleName());
System.out.println("==============获取类对应的包名==============");
System.out.println(studentClass.getPackage());
System.out.println("==============获取类对应的包名对象再获取包的名字==============");
System.out.println(studentClass.getPackage().getName());

3.通过字节码对象获取类的成员方法

public void eat2(){
        //1.获取字节码对象
        Class<Student> studentClass = Student.class;
        //2.通过字节码对象获取目标类中的成员方法们
        Method[] methods = studentClass.getMethods();
        //3.通过高效for循环遍历数组,拿到每一个方法对象
        for (Method method : methods) {
            System.out.println(method);
            System.out.println("==============通过方法对象获取方法名==============");
            System.out.println(method.getName());
            System.out.println("==============通过对象获取方法所有参数==============");
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println(Arrays.toString(parameterTypes));
            System.out.println("=============================================");
        }
    }

4.通过字节码对象获取类的构造方法

@Test
    public void eat3(){
        //1.获取字节码对象
        Class<Student> studentClass = Student.class;
        //2.通过字节码对象获取目标类Student的构造方法们
        Constructor<?>[] constructors = studentClass.getConstructors();
        //3.通过高效for循环遍历数组
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
            System.out.println("==============获取构造方法的名字==============");
            System.out.println(constructor.getName());
            System.out.println("==============构造函数对象获取构造函数的参数类型==============");
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            System.out.println(Arrays.toString(parameterTypes));
            System.out.println("=============================================");
        }

    }

5.通过反射获取构造方法并使用

/*
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 * 
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
     
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 * 		
 * 			调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */

@Test
    public void eat4() throws Exception {
        //1.加载Class对象
        Class<Student> studentClass = Student.class;
        //2.获取所有公有构造方法
        System.out.println("**********************所有公有构造方法*********************************");
        Constructor<?>[] constructors = studentClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor<Student> constructor = studentClass.getConstructor(null);
        System.out.println(constructor);
        //调用构造方法
        Object obj = constructor.newInstance();
        System.out.println("******************获取私有构造方法,并调用*******************************");
        Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(String.class,int.class,char.class);
        //暴力访问(忽略掉访问修饰符)
        declaredConstructor.setAccessible(true);
        //调用构造方法
        obj = declaredConstructor.newInstance("事实上",12,'男');
        System.out.print(obj);
    }

后台输出:

**********************所有公有构造方法*********************************
public cn.tedu.cn.Student()
public cn.tedu.cn.Student(java.lang.String,int,char)
************所有的构造方法(包括:私有、受保护、默认、公有)***************
public cn.tedu.cn.Student()
public cn.tedu.cn.Student(java.lang.String,int,char)
*****************获取公有、无参的构造方法*******************************
public cn.tedu.cn.Student()
******************获取私有构造方法,并调用*******************************
public cn.tedu.cn.Student(java.lang.String,int,char)
Student{string='事实上', age=12, sex=男}

6.获取成员变量并调用

 @Test
    public void eat5() throws Exception {
        /*
         * 获取成员变量并调用:
         *
         * 1.批量的
         * 		1).Field[] getFields():获取所有的"公有字段"
         * 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
         * 2.获取单个的:
         * 		1).public Field getField(String fieldName):获取某个"公有的"字段;
         * 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
         *
         * 	 设置字段的值:
         * 		Field --> public void set(Object obj,Object value):
         * 					参数说明:
         * 					1.obj:要设置的字段所在的对象;
         * 					2.value:要为字段设置的值;
         *
         */
        //1.获取Class对象
        Class<Student> studentClass = Student.class;
        //2.获取字段
        System.out.println("************获取所有公有的字段********************");
        Field[] fields = studentClass.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
        Field[] declaredFields = studentClass.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f);
        }
        System.out.println("*************获取公有字段**并调用***********************************");
        Field field = studentClass.getField("string");
        //获取一个对象  getConstructor()公共类函数.newInstance()创建一个新的实例化
        Object obj = studentClass.getConstructor().newInstance();
        //为字段设置值
        field.set(obj, "刘德华");//为Student对象中的string属性赋值--》stu.string = "刘德华"
        //验证
        System.out.println(field.get(obj));
        System.out.println("**************获取私有字段****并调用********************************");
        Field phoneNum = studentClass.getDeclaredField("sex");
        //触发无参构造利用反射创建对象
        Student student = studentClass.newInstance();
        phoneNum.setAccessible(true);//暴力反射,解除私有限定
        phoneNum.set(student, '男');
        System.out.print(phoneNum.get(student));
    }

后台输出:

************获取所有公有的字段********************
public java.lang.String cn.tedu.cn.Student.string
************获取所有的字段(包括私有、受保护、默认的)********************
public java.lang.String cn.tedu.cn.Student.string
private int cn.tedu.cn.Student.age
private char cn.tedu.cn.Student.sex
*************获取公有字段**并调用***********************************
刘德华
**************获取私有字段****并调用********************************

7.获取成员方法并调用

    @Test
    public void eat7() throws Exception {
        /*
 * 获取成员方法并调用:
 *
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
 */
        //1.获取Class对象
        Class<Student> studentClass = Student.class;
        //2.获取所有公有方法
        System.out.println("***************获取所有的”公有“方法*******************");
        Method[] methodArray = studentClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************获取所有的方法,包括私有的*******************");
        methodArray = studentClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************获取公有的show1()方法*******************");
        Method m = studentClass.getMethod("show1", String.class);
        //实例化一个Student对象
        Object obj = studentClass.getConstructor().newInstance();
        m.invoke(obj, "刘德华");
        System.out.println("***************获取私有的show1()方法******************");
        m = studentClass.getDeclaredMethod("show1", int.class);
        Constructor<Student> constructor = studentClass.getConstructor();
        Student student = constructor.newInstance();
        m.setAccessible(true);//解除私有限定
        m.invoke(student, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
    }

后台输出:

***************获取所有的”公有“方法*******************
public java.lang.String cn.tedu.cn.Student.getString()
public void cn.tedu.cn.Student.show1(int)
public void cn.tedu.cn.Student.show1(java.lang.String)
public int cn.tedu.cn.Student.getAge()
public char cn.tedu.cn.Student.getSex()
public void cn.tedu.cn.Student.setString(java.lang.String)
public void cn.tedu.cn.Student.setSex(char)
public void cn.tedu.cn.Student.setAge(int)
public java.lang.String cn.tedu.cn.Student.toString()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************获取所有的方法,包括私有的*******************
public java.lang.String cn.tedu.cn.Student.getString()
public void cn.tedu.cn.Student.show1(int)
public void cn.tedu.cn.Student.show1(java.lang.String)
public int cn.tedu.cn.Student.getAge()
public char cn.tedu.cn.Student.getSex()
public void cn.tedu.cn.Student.setString(java.lang.String)
public void cn.tedu.cn.Student.setSex(char)
public void cn.tedu.cn.Student.setAge(int)
public java.lang.String cn.tedu.cn.Student.toString()
***************获取公有的show1()方法*******************
刘德华
***************获取私有的show4()方法******************
20

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值