【java】学习笔记,实时更新

API

API(Application Programming Interface,应用程序接口)是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件可以访问的一些功能集,但又无需访问源码或理解内部工作机制的细节.

API是一种通用功能集,有时公司会将API作为其公共开放系统,也就是公司制定自己的系统接口标准,当需要进行系统整合,自定义和程序应用等操作时,公司所有成员都可以通过该接口标准调用源代码。

java.lang 和java.util 包

Java.util包是java中的工具包,包含各种实用工具类/集合类/日期时间工具等各种常用工具包

java.lang包是java的核心,包含了java基础类

包括基本Object类/Class类/String类/基本数学类等最基本的类,这个包无需导入,默认会自动导入

  • class类是什么?
  1. 顶级父类 java.lang.Object

  2. Object是Java中所有类的超类,Java中的类都直接或者间接的继承自Object

  3. 如果一个类没有明确的指定父类,那么这个类默认继承Object

  4. java.lang包是Java的核心包,无需导包,会自动导入

1.1 hashCode()

作用:返回对象对应的哈希码值,Object中默认实现根据对象的地址值生成哈希码值,对象不同,哈希码值应该不同

默认实现:根据对象的地址值生成一个唯一的哈希码值

–重写后:根据传入的属性值生成哈希码

不同对象也有可能出现相同的哈希码值,称之为:哈希碰撞

1.2 toString()

重写前:打印对象的地址值

注意:我们要牢记Object中的默认实现方式,只要与默认实现不同,说明当前类就重写了Object中的实现

至于重写后是什么样的效果,得看具体的重写方式:

自定义类Student重写后: 打印Student类型 + 属性 +属性值【这个是我们自己写的类】

String类重写后:打印的是字符串的具体内容【Java自带的类】

1.3 equals()

重写前:等等比较,比较的是两个对象的地址值 ==

注意:我们要牢记Object中的默认实现方式,只要与默认实现不同,说明当前类就重写了Object中的实现

至于重写后是什么样的效果,得看具体的重写方式:

自定义类Student重写后:比较两个对象的类型+属性+属性值

String类重写后:比较的是两个字符串的具体内容

注意1:toString()不是我们主动调用的,是println()层层调用

如果你打印某个类的对象时,不想打印地址值,可以在这个类里添加重写的toString()

注意2:equals()与hashCode()的重写要一致,要重写都重写,要不重写都不重写

2、String 类

1、底层的结构是字符数组char[ ]

String str = “abc”;// 等效于:
char data[] = {‘a’, ‘b’, ‘c’};

2、String的常用方法

int length() 返回此字符串的长度。
String toUpperCase() 所有字符都转换为大写。
String toLowerCase() 所有字符都转换为小写
boolean startsWith(String prefix) 测试此字符串是否以指定的元素开头。
boolean endsWith(String suffix) 测试此字符串是否以指定的字符串结束。

String[] split(String regex) 根据给定元素来分隔此字符串。
String concat(String str) 将指定字符串连接/拼接到此字符串的结尾,注意:不会改变原串
String trim() 返回去除首尾空格的字符串
String substring(int beginIndex) 返回一个新子串,从指定下标处开始,包含指定下标
String substring(int beginIndex, int endIndex) 返回一个新子串,从执定下标开始,到结束下标为止,但不包含结束下标
byte[] getBytes() 把字符串存储到一个新的 byte 数组中
--数组中除了char类型 可以直接打印,其他都需要通过Array打印查看
--byte类型是通过ascii码转换得到的整型数

system类 父类java.lang.Object

system.currentTimeMillis() 获取当前时间 毫秒级别,从1970年开始

3、StringBuilder/StringBuffer

  1. 是可变的字符序列
  2. 内部字符数组默认初始容量是16:super(str.length() + 16);
  3. 如果大于16会尝试将扩容,新数组大小原来的变成2倍+2,容量如果还不够,直接扩充到需要的容量大小。int newCapacity = value.length * 2 + 2;
  4. StringBuffer 1.0出道线程安全,StringBuilder1.5出道线程不安全
  5. StringBuilder和StringBuffer的区别
    1.在线程安全上 :
    –StringBuffer是旧版本就提供的,线程安全的。@since JDK1.0
    –StringBuilder是jdk1.5后产生,线程不安全的。@since 1.5
    2.在执行效率上,StringBuilder > StringBuffer > String
    3.源码体现:本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题。

3、字符串的拼接

1、+  

2、StringBuffer /StringBuilder 速度特别快;比+拼接快了千倍

sb.append(str) 拼接字符串 => sb

4、String提供了支持正则表达式的方法

Matches(正则) : 当前字符串能否匹配正则表达式
replaceAll(正则,子串) : 替换子串
split(正则) : 拆分字符串

String regex = "[0-9]{17}[0-9X]"    //制定正则表达式的规则

/*本类用于正则表达式入门案例*/
//需求:接收用户输入的身份证号,并将判断的结果输出
public class TestRegex {
    public static void main(String[] args) {
        //1.编辑正则表达式
        //身份证号的规律:一共是18位,前17位是数子,第18位有可能是数字,也有可能是X
        //String regex = "[0-9]{17}[0-9X]";
        /*单个\在Java中有特殊的含义,表示转义符号,不认为是一个斜杠
         * 所以如果想要表示斜杠,需要在它的前面加一个用来转义的\
         * 也就是\\才表示成一个单纯的斜杠
         * \t -- 制表符  \r回车符 \n换行符*/
        String regex = "\\d{17}[0-9X]";

        //2.定义变量用来接收用户输入的身份证号:
        String input;

        //3.判断用户输入的数据是否符合正则表达式,如果不正确,继续输入
        do {
            System.out.println("请输入您的身份证号:");
            input = new Scanner(System.in).nextLine();
            if (input.matches(regex)) {//如果身份证号正确
                System.out.println("恭喜你!输入正确!");
                return;//结束本方法
            }
        } while (!input.matches(regex));//只要不符合正则表达式,就继续输入
    }
}

3、装箱类

byte,int,short,long,float,double,char,boolean

Integer类中包含256个Integer缓存对象,范围是 -128~127

使用valueOf()时,如果指定范围内的值,直接访问缓存对象不新建;如果指定范围外的值,直接新建对象。

Integer 对象的创建有高效—> 缓存对象,常量池

1、Integer , Double

  1. valueOf() int–>Integer 自动装箱 Integer/Double.valueOf()
  2. parseInt() 将对象切换成int类型
  3. parseDouble() 将对象切换成 double类型
  4. intValue/doubleValue() Integer —> int 自动拆箱

2、BigDecimal

//2.创建工具类对象,把基本类型a和b交给工具类对象BigDecimal来保存
        /*1.最好不要用double作为构造函数的参数,不然还会有不精确的现象,有坑!!!*/
        /*2.最好使用重载的,参数类型是String的构造函数
        * double转String,直接拼个空串就可以*/
        BigDecimal bd1 = new BigDecimal(a+"");
        BigDecimal bd2 = new BigDecimal(b+"");

/*3.除法运算,除不尽时会抛出异常ArithmeticException*/
        //方案一:(除不尽时有问题)
        //bd3 = bd1.divide(bd2);
        /*divide(m,n,o)
        m是要除以哪个对象,n指要保留几位,o指舍入方式(比如四舍五入)*/
        //方案二:
        bd3 = bd1.divide(bd2,3,BigDecimal.ROUND_HALF_UP);
        System.out.println(bd3);

高级API

1、IO流

1、File文件夹类

概述:封装一个磁盘路径字符串,对这个路径可以执行一次操作

       可以封装文件路径、文件夹路径、不存在的路径

创建对象:File(String pathname)通过将给定路径名字符串转换为抽象路径名来创建一个新的File实例

new File(“d:/abc/a.txt”);
new File(“d:/abc”,”a.txt”);

常用的方法:

1、文件/文件夹属性:getAbsolutePath()…

2、创建和删除:createNewFile(),mkdir(),mkdirs(),delete() 只能删除文件或者空的文件夹

系统不确定路径是否存在问题,所以需要住的抛出异常

输出调用方法,创建成功返回TRUE,如果文件已存在或者创建失败则返回FALSE

3、文件夹列表:

list() -----返回String[],包含文件名

listFile() -------返回File[],包含文件对象

2、字节流读取(输入)

字节流是由字节组成的,字符流是由字符组成的.

Java里字符由两个字节组成.字节流是基本流,主要用在处理二进制数据。

所以字节流是比较常用的,可以可以处理多种不同种类的文件,比如文本文档/音频/视频等

字节流比字符流更加通用,字符流只能用来处理字符相关的传输操作

InputStream 子类 是字节输入中所有类的超类,是一个抽象类,不能直接创建对象

FileStream 子类 直接插在文件上,直接读取文件数据

//构造方法
FileInputStream(File file)—直接传文件对象
//通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定
FileInputStream(String pathname)—传路径
//通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定

BufferStream 高效输入流:创建对象是,会在内存中创建一个内部缓冲区数组(默认8k)

3、字符流读取(输入)

常用于处理纯文本数据,读写容易出现乱码的现象,在读写时,最好指定编码集为UTF-8

Reader 是字符流中的超类,也是一个抽象类

FileReader 子类 用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。

  • 如何在FileInputStream 上构造一个InputStreamReader

构造方法与InputStream相似

BufferReader 子类 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了

4、字节/字符写出(输出)

字节:OutputStream – FileOutputStream —BufferOutputStream

字符:Writer — FileWriter — BufferWriter

public Test(){
  public static void main(String[] args){
    methods1();
    methods2();
  }
  public static void methods1(){
    Writer out = null;
    try{
      out = new BufferWriter(new FileWriter("d://ready//1.txt"));
      int b;
      out.writer(97);
      out.writer(97);
      out.writer(97);

    }catch(Exception e){
       e.printStackTrace();
    }finally{
      try{
        out.close();
      }catch(Exception e){
        e.printStacktrace();
      }
      
    }
}
}

复制文件:

String f = new Scanner(System.in).nextLine();
String t = new Scanner(System.in).nextLine();
public static void methods(String f, String t){
  InputStream in = null;
  OutputSteam out = null;
  try{
    in = new BufferInputStream(new FileInputStream(f));
    out = new BufferOutputStream(new FileOutputStream(t));
    int b ;
    while(!(b = in.reader() == -1){
      out.writer(b);
    }
          }catch(Execption e ){
            e.printStackTrace();
          }finally{
            try{
              out.close();
            }catch(IOException e){
              e.printStackTrace();
            }
            try{
              in.close();
            }catch(IOException e){
              e.printStacktrace();
            }
          }
}

2、序列化与反序列化

字节和字符流输入或输出都是通过操作字节或者是字符实现,程序与硬盘之间的传输,

如果想要直接操作对象,则需要使用序列化的操作。

概念:序列化是指将对象的状态信息转换为可以存储或传输形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后可以通过从存储区中读取或者反序列化对象的状态,重新创建该对象.

序列化:将对象的信息,通过一种固定的格式转成一串字节输出并持久保存在硬盘

利用ObjectOutputStream类

反序列化:读取之前序列化好的数据信息,重新恢复成对象。

序列化条件:

1、必须实现一个Serializable接口,该接口只是一个空接口,起标记作用

2、被序列化的文件都有一个唯一版本的ID,系统默认自动生成,也可以自行设置

3、反序列化时,如果序列化的版本ID不一致,无法完成反序列化

4、不需要序列化的数据可以修饰成static,原因:static资源属于类资源,不随着对象被序列化输出

代码实现序列化与序列化

1、创建学生类Student

  1. 必须实现Serializable接口
  2. 生成get,set方法
  3. 重写toString方法
  4. 写上全参构造和无参构造方法
  5. 根据需求是否设置serialVersionID

2、创建序列化测试类

public void test(){
  public static void main(String[] args){
    methods1();
    methods2();
  }
  public static void methods1(){
    ObjectOutputStream out =null;
    ObjectInputStream in = null;
    try{
      out = new ObjectOutputStream(new FileOutputStream("D://ready//1.txt"));
      Student s = new Student();
      out.ObjectWrite(s);
    }catch(Exceptioin e){
      e.printStackTrace();
    }finally{
      try{
        out.close();
      }catch(IOException e){
        e.printStackTrace();
      }
    }
  }
  public static void methods2(){
    ObjectInputStream in = null;
    try{
      in = new ObjectInputStream(new FileInputStream("D://ready//1.txt"));
      Object o = in.ObjectReader();
    }catch(Exceptioin e){
      e.printStackTrace();
    }finally{
      try{
        in.close();
      }catch(IOException e){
        e.printStackTrace();
      }
    }
  }
}

3、泛型

其实就是< ? >的部分,它就是泛型

泛型是(Generics)JDK1.5 的一个新特性,通常用来和集合对象一起使用

泛型概念非常重要,它是程序的增强器

我们可以把泛型理解成一个“语法糖”,本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓“泛型”的概念的。

我们可以通过泛型的语法定义<>,来约束集合中元素的类型,编译器可以在编译期根据泛型约束提供一定的类型安全检查,这样可以避免程序运行时才暴露BUG,代码的通用性也会更强

泛型可以提升程序代码的可读性,但是它只是一个“语法糖”(编译后这样的部分会被删除,不出现在最终的源码中),所以不会影响JVM后续运行时的性能.

在方法的返回值前声明了一个,表示后面出现的E是泛型,而不是普通的java变量

private static  void print(E[] e) {
		for(E d :e) {
			System.out.println(d);
		}
	}

4、集合Collection

retainAll —取公共交集(公共元素)

 //3.迭代集合/遍历集合
        /**迭代步骤:
         * 1.获取集合的迭代器 c.iterator();
         * 2.判断集合中是否有下一个可迭代的元素 it.hasNext()
         * 3.获取当前迭代到的元素 it.next()*/
        Iterator it = c.iterator();
        while(it.hasNext()){
            Integer num = it.next();
            System.out.println(num);
        }

1、List集合

System.out.println(Arrays.toString(list.toArray()));//将集合转成数组
System.out.println(list.set(7,"蝎子精"));//修改指定索引处元素的值为蝎子精   返回被修改的元素
System.out.println(list.remove(1));//200,移除指定位置处的元素 返回被删除的匀速

ArrayList
1、List接口的实现类
2、底层结构是数组,内存空间是连续的
3、元素有下标,有序,允许存放重复的数据
4、通常进行的是根据下标进行操作
5、增删操作慢,查询操作比较快【数据量大的时候】

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

2、map接口 和collection没有继承关系

底层存的是entry,一个对应关系

  1. Map可以根据键来提取对应的值

  2. Map的键不允许重复,如果重复,对应的值会被覆盖

  3. Map存放的都是无序的数据

  4. Map的初始容量是16,默认的加载因子是0.75

    contaionsKey();
    contaionsValue();
    Collection values = map.values();取出value值,放入collection集合中
    //将map集合转为set集合以后,再使用set的迭代器 map没有迭代器
    Set set = map.KeySet(); //将key转存到set集合中
    Iterator iterator = set.Iterator();
    while(iterator.hasNext()){
    System.out.println(iterator.next());
    }
    Map.Entry 内部类

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

    //获取一个字符串中每个字符出现的次数
    String input = new Scanner(System.in).nextLIne();
    Map map = new hashMap<>();
    for(i=0;i char key = input.charAt(i);
    Integer value = map.get(key);
    if(value == null){
    map.put(key,1)
    }else{
    map.put(key,value+1);
    }
    }

3、set集合

1、无序,无下标,不可重复

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

进程与线程

1、进程

进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。

独立性

进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间

动态性

进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,程序加入了时间的概念以后,称为进程,具有自己的生命周期和各种不同的状态,这些概念都是程序所不具备的.

并发性

多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响.

2、线程

线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.

一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程。

我们看到的进程的切换,切换的也是不同进程的主线程

多线程可以让同一个进程同时并发处理多个任务,相当于扩展了进程的功能。

每个线程在共享同一个进程中的内存的同时,又有自己独立的内存空间.

所以想使用线程技术,得先有进程,进程的创建是OS操作系统来创建的,一般都是C或者C++完成

1、随机性

一个CPU【单核】只能执行一个进程中的一个线程。由OS底层算法决定

2、cpu分时调度

串行与并行

串行是指同一时刻一个CPU只能处理一件事,类似于单车道

并行是指同一时刻多个CPU可以处理多件事,类似于多车道

并发:相对来说资源比较紧缺,多个进程同时抢占公共资源,比如多个进程抢占一个cpu

时间片,即CPU分配给各个线程的一个时间段,称作它的时间片,即该线程被允许运行的时间,如果在时间片用完时线程还在执行,那CPU将被剥夺并分配给另一个线程,将当前线程挂起,如果线程在时间片用完之前阻塞或结束,则CPU当即进行切换,从而避免CPU资源浪费,当再次切换到之前挂起的线程,恢复现场,继续执行。

注意:我们无法控制OS选择执行哪些线程,OS底层有自己规则,如:

FCFS(First Come First Service 先来先服务算法)

SJS(Short Job Service短服务算法)

3、线程的状态

由于线程状态比较复杂,我们由易到难,先学习线程的三种基础状态及其转换,简称”三态模型”

  • 就绪(可运行)状态:线程已经准备好运行,只要获得CPU,就可立即执行

  • 执行(运行)状态:线程已经获得CPU,其程序正在运行的状态

  • 阻塞状态:正在运行的线程由于某些事件(I/O请求等)暂时无法执行的状态,即线程执行阻塞

  • 创建状态:线程的创建比较复杂,需要先申请PCB,然后为该线程运行分配必须的资源,并将该线程转为就绪状态插入到就绪队列中

  • 终止状态:等待OS进行善后处理,最后将PCB清零,并将PCB返回给系统
    PCB(Process Control Block):为了保证参与并发执行的每个线程都能独立运行,OS配置了特有的数据结构PCB来描述线程的基本情况和活动过程,进而控制和管理线程

4、多线程代码实现

1、通过继承Thread实现
/*本类用于多线程编程实现方案一:继承Thread类来完成*/
1、继承Thread类
2、自定义类中重写Thread中的run方法,方法中写自己的业务
String getName()
//返回该线程的名称
3、创建自定义类的对象----对应线程中的新建状态
4、调用start()以多线程的方式将多个线程对象加入到就绪状态中,等待OS选中
void start()
  //使该线程开始执行:Java虚拟机调用该线程的run()
5、优点:写法简单;缺点:继承只能单继承类


2、通过实现Runnable接口
1、自定义多线程类,实现Runnable接口
2、重写Runnable接口中的run方法,方法中写自己的业务
static Thread currentThread()
//返回对当前正在执行的线程对象
3、创建自定义类的对象--作为业务对象 发布的任务 自定义对象不能直接调用 start()
4、创建多个线程对象----独立干活的  new Thread(new myRunnable());
5、调用start()以多线程的方式将多个线程对象加入到就绪状态中,等待OS选中
6、优点: 自定义的线程类只是实现了Runnable接口或Callable接口,后续还可以继承其他类,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、还有数据分开(解耦),形成清晰的模型,较好地体现了面向对象的思想
缺点: 编程稍微复杂,如想访问当前线程,则需使用Thread.currentThread()方法

//需求:设计4个售票窗口,总计售票100张。用多线程的程序设计并写出代码
1、继承Thred类,实现多线程任务模拟
//3.定义变量,保存要售卖的票数
    /*问题:4个线程对象共计售票400张,原因是创建了4次对象,各自操作各自的成员变量
    * 解决:让所有对象共享同一个数据,票数需要设置为静态*/
    static int tickets = 100;
 //5.创建多个线程对象
        TicketThread t1 = new TicketThread();
        TicketThread t2 = new TicketThread();
        TicketThread t3 = new TicketThread();
        TicketThread t4 = new TicketThread();
//6.以多线程的方式启动
        t1.start();
        t2.start();
        t3.start();
        t4.start();
2、实现runnable接口,完成多线程任务模拟
/*由于自定义类对象只创建了一次,所以票数被所有线程对象Thread类的对象共享*/
    int tickets = 100;
//5.创建Runnable接口的实现类对象,作为目标业务对象
        TicketRunnable target = new TicketRunnable();
        //6.创建多个Thread类线程对象,并将target业务对象交给多个线程对象来处理
        Thread t1 = new Thread(target);
        Thread t2 = new Thread(target);
        Thread t3 = new Thread(target);
        Thread t4 = new Thread(target);
        //7.以多线程的方式启动多个线程对象
        t1.start();
        t2.start();
        t3.start();
        t4.start();

5、多线程 数据安全隐患

–多线程程序,有共享数据,多条语句操作共享数据

Thread.sleep(10);//休眠10毫秒

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

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

异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。

坏处就是有安全隐患,效率要高一些。

//处理多线程 数据安全隐患
1、extend Thread类
Object o = new Object();    //锁对象不唯一
//static Object o = new Object();
//synchronized(o){    ---继承与实现不一样 
synchronized(MyThread.class){ //真正方式超卖
  if(tickets>0){
    
  }
  if(tickets<=0){
    
  }
}
2、实现接口Runnable
Object o = new Object();
synchronized(o){ //解决重买的问题
  if(tickets>0){
    //解决超卖的问题
  }
  if(tickets<=0){
    
  }
}
3、线程的其他创建方法---线程池
//5.创建接口实现类TicketR3类的对象作为目标业务对象
        TicketR3 target = new TicketR3();
        /*Executors是用来辅助创建线程池的工具类对象
        * 常用方法是newFixedThreadPool(int)这个方法可以创建指定数目的线程池对象
        * 创建出来的线程池对象是ExecutorService:用来存储线程的池子,负责:新建/启动/关闭线程*/
        //6.使用Executors工具创建一个最多有5个线程的线程池对象ExecutorService池对象
        ExecutorService pool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            /*execute()让线程池中的线程来执行业务,每次调用都会将一个线程加入到就绪队列*/
            pool.execute(target);/*本方法的参数就是你要执行的业务,也就是目标业务类对象*/
        }

6、线程锁

synchronized 互斥锁(悲观锁,有罪假设)

采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。

每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。

ReentrantReadWriteLock 读写锁(乐观锁,无罪假设)

因此引入了ReentrantReadWriteLock,顾名思义,ReentrantReadWriteLock是Reentrant(可重入)Read(读)Write(写)Lock(锁),我们下面称它为读写锁。

读写锁内部又分为读锁和写锁,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。

读锁和写锁分离从而提升程序性能,读写锁主要应用于读多写少的场景。

static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
lock.writeLock().lock();
			try {
				if(tickets > 0) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "=" + tickets--);
				}
				if(tickets <= 0) break;
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				//3.finally{}中释放锁,注意一定要手动释放,防止死锁,否则就独占报错了
				lock.writeLock().unlock();
			}
		}
	}
} 

反射

//获取Class实例
1、调用Class类的静态方法forName(String classPath) //使用较多
Class clazz1 = Class.forName("cn.tedu.reflection.Student");
2、调用运行时类的属性:.class
类名.class;//Class clazz = Person.class;
3、通过运行时类的对象,调用getClasss()
new Student.getClass();
4、使用类的加载器:ClassLoader(了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz = classLoader.loadClass("com.atguigu.java.Person");

1、java.lang.Class

1、类的加载过程 --加载,链接,初始化

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)

接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,次运行时类,就作为Class的一个实例。

2、Class实例对应着一个运行时类

3、加载到内存中的运行类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取次运行时类。

2、类的加载器

自定义类—系统类加载器

系统类----扩展类加载器 classLoader.getParent();

java核心类库—引导类加载器

读取配置文件

public void test throw Exception(){
  Properties pros = new Properties();
  //读取配置文件方式一:  此时文件默认在当前module下
  FileInputStream fis = new FileInputStream("jdbc.properties");
  pros.load(fis);
  //读取配置文件二: 使用ClassLoader   默认在当前module的src下
  ClassLoader classLoader = new ClassLoaderTest.class.getClassLoader();
  InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
  pros.load(is);
  String user = pros.getProperty("user");
  String password = pros.getProperty("password");
}

创建运行时类的对象的条件

Class clazz =  Person.getClass();
Person obj = clazz.newInstance();   //运行时类Person的对象
//要想通过此方法正常的创建运行时类的对象,要求:
1、运行时类必须提供空参的构造器
2、空参的构造器的访问权限的够。通常,设置为public

//在javabean中要求提供一个public的空参构造器。原因:
1、便于通过反射,创建运行时类的对象
2、便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

反射的动态性

//反射的动态性
@Test  //单元测试方法
public void testReflect(){
  String classPath = "";
  int num = new Random().nextInt(3);
  switch(num){
    case 0:
      classPath = "java.util.Data";
        break;
    case 1:
      classPath = "java.sql.Data"; //没有空构造器
      break;
    case 2:
      classPath = "com.atguigu.java.Person";
      break;
  }
  try{
    Object obj = getInstance(classPath);
  }catch(){
    
  }
}
public Object getInstance(String classPath){
  Class clazz = Class.forName(classPath);
  return clazz.newInstance();
}

通过反射获取属性结构

Class clazz = Person.class;
Field[] declareFields = clazz.getDeclareFields();
for(Field f : field[])
//通过反射获取属性结构
1、getFields();获取当前运行时类以及父类中声明为public访问权限的属性
2、getDeclaredFields();获取当前运行时类中声明的所有属性,不包括父类中的
//权限修饰符   数据结构   变量名
for(Field f : field[])
1、权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier));//public 
2、数据结构
Class type = f.getType();
System.out.print(type.getName); //int
3、变量名
String fName = f.getName();

获取运行时类的方法结构

Class clazz = Person.class;
//获取运行时类的方法结构
1、getMethods);获取当前运行时类以及父类中声明为public访问权限的方法
2、getDeclaredMethods();获取当前运行时类中声明的所有方法包括父类中的
/*@xxx
权限修饰符  返回值类型 方法名(数类型1 形参名1,...)throws xxxException{}*/
Class clazz = Person.class;
Method[] declareMethods = clazz.getDeclareMethods();
for(Method m : declareMethods){
1、获取方法声明的注解//RunTime  注解在运行时生效才能获取的到
 Annotation[] annos = m.getAnnotations();
  for(Annotation a : annos){
2、权限修饰符
  Modifier.toString(m.getModifiers());
3、返回值类型
  m.getReturnType().getName();
4、方法名
  m.getName();
5、形参列表
  Class[] parameterTypes = m.getParameterTypes();
    if(!(parameterTypes == null && parameterTypes.length == 0))
      parameterTypes[i].getName();// int String
6、抛出的异常
  Class[] exceptionTypes = m.getExceptionTypes();
    if(exceptionTypes.length > 0)
      exceptionTypes[i].getName();

枚举类和注解

1、枚举类的使用

  1. 枚举类的理解:类的对象只有有限个,确定的,称此类为枚举类
  2. 当需要定义一组常量时,强烈建议使用枚举类
  3. 如果枚举类中只要一个对象,则可以作为单例模式的实现方式

2、如何定义枚举类

  1. jdk5.0之前,自定义枚举类

  2. jdk5.0,可以使用enum关键字定义枚举类 —继承于java.lang.Enum

  3. Enum类的常用方法
    1、values() Season[] values = Season.values(); 当前枚举类状态
    2、valuesOf() Season winter = Season.valueOf(“WINTER”); 返回枚举类中对象是objName的对象
    3、toString()
    4、实现接口 1、在枚举类中实现抽象方法
    2、每个枚举类都是实现不一样的抽象方法

    enum Season implement info{
    //1、提供当前枚举类的对象,多个对象之间使用“,”隔开,末尾使用“;”结束
    SPRING(“SPRING”,“S”){
    @Override
    public void show(){
    }
    },
    SUMMER(“SUMMER”,“S”),
    AUTUMN(“SUTUMN”,“S”),
    WINTER(“WINTER”,“W”);
    //2、声明Season对象的属性:private final 修饰
    private final String seasonName;
    private final String seasonDesc;

    private Season(String seasonName,String seasonDesc){
    this.seasonName = seasonName;
    this。seasonDesc = seasonDesc;
    }
    //其他诉求1获取枚举类对象的属性get方法
    //2提供toString()

    }

2、元注解

@Target 注解用在哪里:类上、方法上、属性上等等

@Retention 注解的生命周期:源文件中、字节码文件中、运行中

你可能感兴趣的:(学习笔记,java,学习,intellij-idea)