1.1 java基础

视频看的是狂神说Java

一、电脑快捷键

1.Ctrl + shift + esc打开任务管理器

2.win + E打开“我的电脑”

3.win + R打开运行方式

4.ctrl + F4 快速关闭网页或者文件

5.shift + delete 永久删除文件

6.在任务管理器中将“Windows资源管理器”结束任务时,桌面将会消失。点击文件中的运行任务输入explorer就会出现桌面。

二、Dos命令

打开cmd方式

  1. 开始 + windows系统 + 命令提示符;
  2. win键 + R + 输入cmd打开控制台;
  3. 在任意的文件夹空白处,按住shift键+鼠标右键点击,在此处打开命令行窗口;
  4. 我的电脑中的地址栏最前面,输出cmd+空格,即可进入控制台;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZFcyQYxF-1637023966165)(java.assets/image-20210625111722301.png)]

管理员运行方式

开始 + windows系统 + 命令提示符 + 鼠标右键 + 更多 + 以管理员方式运行(可以获得最高权限)

常用的Dos命令

#盘符切换
E: + 回车
#查看当前目录下的所有文件:dir
#切换目录 cd /d f:\文件名
#返回上一级 cd ..
#清楚屏幕 cls
#退出终端 exit
#查看ip ipconfig
#打开应用 
	calc计算机     mspaint画图    notepad记事本
#ping 命令
	ping www.baidu.com
#创建文件夹 md 文件夹名称
#删除文件夹 rd 文件夹名称
#创建文件 cd>文件名.后缀
#删除文件 del 文件名.后缀

ipconfig/release和ipconfig/renew的区别

ipconfig/release:释放全部(或指定)适配器的由DHCP分配的动态IP地址。此参数适用于IP地址非静态分配的网卡,通常和下文的renew参数结合使用。

ipconfig/renew:为全部(或指定)适配器重新分配IP地址。此参数同样仅适用于IP地址非静态分配的网卡,通常和上文的release参数结合使用。

三、Java入门

基本背景

**1. 三个问题:**高可用、高性能、高并发

2. 版本:

​ J2SE:Java2标准版 J2ME:Java2移动版 J2EE:Java2企业版

Java特性与优势

  • 简单性:比C++易理解、易操作

  • 面向对象:万物皆可成对象

  • 可移植性:一次编译,多次运行。借助的是Java虚拟机

  • 高性能:

  • 分布式:通过url可以链接网页

  • 动态性:通过反射机制实现

  • 多线程:实现实时和同时做不同事情

  • 安全性:底层代码设置了很多关于安全技术,防漏洞技术

  • 健壮性:运行代码会自主查看内存消耗情况,没有指针的到处引用现象

Java三大版本

**JavaSE:**标准版(桌面程序,控制台开发…)

**JavaME:**嵌入式开发(手机,小家电…)

**JavaEE:**企业级开发(web端,服务器开发…)

JDK / JRE / JVM

JDK: Java Development Kit

JRE: Java Runtime Environment

JVM: Java Virtual Machine

Java程序运行机制

.java文件(源文件) -> (Java编译器)—> .class文件(字节码) —> 类装载器 —> 字节码校验器 —> 解释器 —> 操作系统平台

四、Java基础

idea创建空项目

创建空的project后,随后点击file,然后点击创建module,在project Structure里指定JDK版本和下面对应的解释等级,例如JDK是1.8,则下面对应的数字为8

Java注释

单行注释:// 多行注释:/* */

文本注释:

/**
* @Description  描述代码功能信息
* @Author       编写代码的作者是谁
*/

标识符

  1. 以字母,美元符($)或者下划线开始;
  2. 首字母之后可以是字母、美元符、下划线和数字的组合;
  3. 不能以关键字命名;
  4. 区分大小写;

数据类型

**强类型语言:**所有变量都必须先定义后才能使用

弱类型语言:

类别:

基本类型:

​ **整数类型:**byte short(2) int(4) long(8)后面加L

​ **浮点类型:**float(4) 后面加F double(8)

​ **字符类型:**char(2)

​ **Boolean类型:**true或false

类型转换:

低----》 高

byte short char int long float double

强制转换 (类型)变量名 高—》低

自动转换 低----》 高

注:不能对布尔值进行转换

不能把对象类型转换为不相干的类型

在把高容量转换到低容量的时候强制转换

转换的时候可能存在内存溢出或精度问题

1B (byte,字节) = 8 bit(位)

引用类型:

接口 数组


变量

作用域

类变量: 前面有static修饰 static double salary = 2500;

实例变量: 赋初值,从属于对象,如果不初始化则为默认值,0,0.0,false(boolean),null(string)

局部变量: 定义在方法里,使用前必须声明和初始化

命名规范

类名: 首字母大写和驼峰原则

**方法名:**首字母小写和驼峰原则

常量

final 常量名 = 值;

常量名一般使用大写字符

修饰符不区分前后,public,static,final

位运算符

^ 异或 相同为0,不同为1

<< 代表左移,*2 2<<3 =16

>> 代表右移 /2

没有long时,所有非int类型转为int类型

“”+a+b 输出为1020

a+b+"" 输出为30

JavaDoc

参数信息

  1. @author 作者名
  2. @version 版本号
  3. @since 指明需要最早使用的jdk版本
  4. @param 参数名
  5. @return 返回值情况
  6. @throws 异常抛出情况
命令行生成JavaDoc文档
javadoc -encoding UTF-8 -charset UTF-8 文件名.java

Scanner对象

Scanner scanner = new Scanner(System.in);    // 从键盘接受数据
Scanner.hasnext()    // 判断接受的数据是否为空,以空格作为结束标志
Scanner.hasNextLine() // 判断接受的数据是否为空,以回车符为结束标志
scanner.close();      // 用完记得关闭
int i = scanner.nextInt(); // 接受的是整数数据
float f = scanner.nextFloat(); // 接受的是浮点数据
String str = scanner.next();
获取输入的字符串
next() 以空格符结束,nextLine()  以回车符结束 
hasNext()hasNextLine() 判断是否还有输入的数据

Java流程控制

if选择结构 For循环

Switch结构

  • switch语句中的变量类型可以是:byte、short、int、string、枚举或者char;

  • 从JavaSE7开始,switch支持字符串String类型了

    • 同时case标签必须为字符串常量或字面量
  • default:前面case全没有匹配成功后,才输出默认语句,否则不输出

增强for循环

While结构 break continue

DoWhile结构

goto

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0rrLfkn-1637023966170)(java.assets/image-20210704104146461.png)]

 outer:for (int i = 100; i < 205; i++){
            for (int j = 2; j < i / 2; j++){
                if (i % j == 0){
                    continue outer; // 当不满足质数要求时候,跳到外面循环outer
                }
            }
            System.out.println(i + " ");
        }

方法的重载

  1. 重载:发生在一个类中,函数名相同,但是形参不同
  2. 规则:
  • 方法名称必须相同;
  • 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等);
  • 返回值可相同可不同;
  • 仅仅返回值类型不同构成不了方法的重载

可变参数

  1. 在方法声明中,在指定参数类型后加一个省略号(…)
  2. 一个方法中只能指定一个可变参数,必须是方法的最后一个参数
public void min(int x,int ... y)

数组初始化

1. 静态初始化

int[] a = {1,2,3};

2. 动态初始化

int[] a = new int[10];

3. 默认初始化

数组是引用类型,它的元素相当于类的实例变量,已经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

Java内存分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nr3t25jn-1637023966176)(java.assets/image-20210705160929729.png)]

Array类

  • 数组的工具类java.util.Arrays

  • Arrays类中的方法都是static修饰的静态方法,使用时直接用类名进行调用

  • 常用的功能:

    给数组赋值:

    Arrays.fill();
    

    对数组排序:

    Arrays.sort();
    

    比较数组:

    Arrays.equals();
    

    查找数组元素:

    Arrays.binarySearch();
    

    输出数组:

    Arrays.toString();
    

五、面向对象

创建与初始化对象

  • 使用new创建对象,会分配内存空间,对创建好的对象进行默认初始化,以及对类中构造器的调用;
  • 构造器的特点:
    1. 必须与类名相同;
    2. 必须没有返回类型,不能写void

三大特性

  1. 封装:高内聚、低耦合

    • 提高程序的安全性,保护数据
    • 隐藏代码的实现细节
    • 统一接口
    • 增加系统可维护性
  2. 继承

    • 关键字:extends

    • Java中类只有单继承,没有多继承!

    • 子类的无参构造函数会首先默认调用父类的无参构造函数

    • super注意点:

      ​ a. super调用父类的构造方法,必须在构造方法的第一个;

      ​ b. super必须只能出现在子类的方法或者构造方法中;

      ​ c. super和this不能同时调用构造方法。

    • super与this:

      • 代表的对象不同:

        ​ a. this:本身调用者这个对象

        ​ b. super:代表父类对象的应用

      • 前提:

        ​ a. this:没有继承也可以使用

        ​ b. super:只能在继承条件才使用

      • 构造方法:

        ​ a. this():本类的构造

        ​ b. super():父类的构造

      • 重写:

        1. 方法名,参数列表必须相同;
        2. 修饰符:范围可以扩大但不能缩小;
        3. 抛出的异常:范围,可以被缩小,但不能扩大;
      • 如果父类和子类中的方法都有static修饰时,方法的调用只和左边定义的数据类型有关;

      • 如果父类和子类中的方法都没有static修饰时,方法的调用只和右边new创建的对象和定义的数据类型有关;

  3. 多态

    • 多态是方法的多态,属性没有多态;

    • 父类与子类存在联系,否则会报:ClassCastException

    • 存在条件:继承关系,方法需要重写,父类引用指向子类对象!

      ​ 当子类没有重写父类的方法时,此时调用的是父类方法;当子类重写了父类方法时,此时调用的是子类方法;当调用子类独有的方法时,此时需要父类引用进行类型强制转换。

    • 不能重写的方法有:

      • static方法:属于类,不属于实例
      • final方法:常量无法继承
      • private方法

static

  1. 非静态方法可以调用静态方法,静态方法不能调用非静态方法;
  2. static修饰的方法会随着类的创建一起出现,而非静态方法需要创建对象后再调用;
  3. 当类中出现匿名函数、静态代码块、无参构造函数时,调用顺序为静态代码块->匿名函数->无参构造函数
public class Person{
    // 匿名函数
    {
        System.out.println("匿名函数");
    }

    // 静态代码块
    static {
        System.out.println("静态代码块");   //只执行一次
    }

    // 无参构造函数
    pra01(){
        System.out.println("无参构造函数");
    }
}
//输出的结果为:
静态代码块
匿名函数
无参构造函数

抽象类

  1. 抽象类里可以有抽象方法和正常的方法
  2. 抽象类的所有方法,继承了它的子类都必须实现它的方法,除非也是抽象的
  3. 不能new抽象类,只能靠子类去实现它
  4. 抽象类可以有普通方法
public abstract class Person {
    //抽象方法
    public abstract void run();
    //正常方法
    public void go(){
        System.out.println("正常的方法");
    }
}

接口

  1. 普通类、抽象类和接口

**普通类:**只有具体实现

**抽象类:**具体实现和规范(抽象方法)都有

**接口:**只有规范

  1. 声明类的关键字是class,声明接口的关键字为interface,声明实现接口的关键字为implements,声明抽象的关键字为abstract
  2. 接口里的所有方法都是public abstract
  3. 接口里所有的变量都是静态常量public static final
  4. 接口可以实现多继承
  5. 接口不能实例化,没有构造方法

内部类

  1. 分类:成员内部类、静态内部类、局部内部类、匿名内部类
  2. 一个Java类中只有一个public类,能有多个class
public class Outer{
	private int id = 10;
	public void out(){
		System.out.println("这是外部类的方法");
	}
	//成员内部类
	public class Inner{
		public void in(){
		System.out.println("这是内部类的方法");
		}
		//获得外部类的私有属性
		public void GetID(){
			System.out.println(id);
		}
	}
}
Outer outer = new Outer();
//通过外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.getID();

异常

  1. 体系结构
    • 所有异常的超类:java.lang.Throwable
    • 异常分为两大类:错误Error和异常Exception

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-29ZZUCwW-1637023966180)(java.assets/image-20210708092755954.png)]

  1. Error

    • Error类对象由Java虚拟机生成并抛出;
    • Virtual MachineErrorJava虚拟机运行错误;OutOfMemoryError当Java不再有继续执行操作所需的内存资源
    • NoClassDefFoundError类定义错误;LinkageError链接错误
  2. Exception

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivQ29Y75-1637023966183)(java.assets/image-20210708093551468.png)]

异常处理机制

  1. 两大类

    • 抛出异常
    • 捕获异常
  2. 异常处理五个关键字:try catch finally throw throws

  3. finally:不管是否出现异常,都会执行finally里的相关语句

  4. 当出现多个catch语句时,从上往下范围依次变大,最后只有一个起作用,其他catch语句不输出

  5. idea快捷键:ctrl + alt + T

// 方法上抛出异常,使用throws
    public void test(int a, int b) throws ArithmeticException{
        if(b == 0){
            // 主动抛出异常,一般在方法中使用throw
            throw new ArithmeticException();
        }
    }

自定义异常

  1. 需要继承Exception
  2. 步骤
    • 创建自定义异常类
    • 在方法中通过throw关键字排除异常对象
    • 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理异常;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
    • 在出现异常方法的调用者中捕获并处理异常

实际应用的经验总结

  1. 采用逻辑去合理规避同时辅助try-catch处理
  2. 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
  3. 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  4. 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
  5. 根据不同的业务需求和异常类型去决定如何处理异常
  6. 尽量添加finally语句块去释放占用的资源。

六、多线程

进程与线程

**进程:**系统资源分配的单位

**线程:**CPU调度和执行的单位。

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

创建线程

方法一:继承Thread类

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
public class TestThread1 extends Thread{
    //重写run方法
    public void run(){
        for (int i = 0; i < 10; i++){
            System.out.println("代码在跑步。。。。");
        }
    }
    public static void main(String[] args) {
        TestThread1 thread1 = new TestThread1();
        thread1.start();
        for (int i = 0; i < 1000; i++){
            System.out.println("main主线程");
        }
    }
}

线程不一定立即执行,CPU安排调度

注意:

 如果main函数里调用的是 thread1.start()语句,则main主线程和分支线程同时交替执行
 如果main函数里调用的是 thread1.run()语句,则先执行创建的分支线程,再执行main主线程

方法二:实现 Runnable接口

  • 定义MyRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程

推荐使用Runnable对象,因为Java单继承的局限性

public class TestThread2 implements Runnable{
    public void run(){
        ....
    }
    
    public static void main(String[] args) {
        TestThread2 thread2 = new TestThread2();
        new Thread(thread2).start(); // 将实现Runnable接口的对象放入Thread构造函数中进行启动
       ....
    }
}

常用的方法

Thread.currentThread().getName()//获取当前线程的名字
Thread.sleep();                       // 让线程睡眠,单位毫秒

方法三:实现 Callable接口

  • 实现Callable接口,需要返回值类型
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:
 ExecutorService ser = Executors.newFixedThreadPool(3);  // 3代表创建线程的个数
  • 提交执行
Future<Boolean> result1 = ser.submit(t1);    // 泛型<>里的类型与实现Callable接口返回值类型一致
  • 获取结果
boolean r1 = result1.get();     // 与实现Callable接口返回值类型一致
  • 关闭服务
ser.shutdownNow();

lamda表达式

其实质属于函数式编程

(params) -> expression[表达式]

(params) -> statement[语句]

(params) -> {statements}

总结:

  • lambda表达式只能有一行代码的情况下才能简化成一行,如果是多行,那么就用代码块包裹
  • 前提是接口为函数式接口
  • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

函数式接口

  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
  • 对于函数式接口,我们可以通过lamda表达式老创建该接口的对象

例子

package com.ty.Thread_prac.lamda;

public class Testlambda1 {

    // 3.静态内部类
    static class Like2 implements Ilike{
        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }
    }

    public static void main(String[] args) {
        Ilike like = new Like();
        like.lambda();

        like = new Like2();
        like.lambda();

        // 4.局部内部类
        class Like3 implements Ilike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        like = new Like3();
        like.lambda();

        // 5.匿名内部类,没有类的名称,只能借助接口或者父类
        like = new Ilike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();

        // 6.用lambda简化
        like = ()->{
            System.out.println("i like lambda5");
        };
        like.lambda();

        // 7.lambda再简化
        like = ()-> System.out.println("i like lambda6");
        like.lambda();
    }


}
// 1.定义一个函数式接口
interface Ilike{
    void lambda();
}

// 2. 实现类
class Like implements Ilike{
    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

线程状态

创建状态、就绪状态、阻塞状态、运行状态、死亡状态

创建状态

Thread t = new Thread()线程对象一旦创建就进入到了新生状态

就绪状态

当调用start() 方法,线程立即进入就绪状态,但不意味着立即调度执行

阻塞状态

当调用sleep,wait或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞时间解除后,重新进入就绪状态,等待cpu调度执行

运行状态

进入运行状态,线程才真正执行线程体的代码块

死亡状态

线程中断或者结束,一旦进入死亡状态,就不能再次启动

常用的方法

方法说明
setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程,别用这个方式
boolean isAlive()测试线程是否处于活动状态

停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量,当flag=false,则终止线程运行

线程休眠

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

模拟倒计时

public class TestSleep {
    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 模拟倒计时
    public static void tenDown() throws InterruptedException{
        int num = 10;
        while(true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num < 0){
                break;
            }
        }
    }
}

打印当前系统时间

public static void main(String[] args) {
        // 打印当前系统时间
        Date startTime = new Date(System.currentTimeMillis());  // 获取系统当前时间
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());//更新当前时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,但不一定成功!看cpu心情
public class TestYield {
    public static void main(String[] args) {
        MyYield myi = new MyYield();
        new Thread(myi,"a").start();
        new Thread(myi,"b").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程成功了!");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "线程成功了!");
    }
}

线程强制执行(插队)

thread.join()

观测线程的状态

Thread.State state = thread.getState()         // 状态有NEW  RUNNABLE  BLOCKED WAITING TIMED_WAITING   TERMINATED    

线程的优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
  • 线程的优先级用数字表示,范围从1~10
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
  • 使用以下方式改变或获取优先级
getPriority()        setPriority(int xxx)

优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都看cpu的调度

优先级的设定建议再sart()调度前

守护(daemon)线程

  • 线程分为用户线程守护线程

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护线程执行完毕

  • 如,后台记录操作日志,监控内存,垃圾回收等待

// 默认是false表示是用户线程,正常的线程都是用户线程
Thread.setDaemon(true)        

线程同步(等待机制)

**并发:**同一个对象被多个线程同时操作

锁机制synchronized导致的一些问题:

 1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;
 2. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换  和  调度延时,引起性能问题;
 3. 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置,引起性能问题

同步方法

  • synchronized关键字,包括两种用法:synchronized方法和synchronized块
  • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

同步块

  • 同步块:synchronized(obj){}
  • obj称之为同步监视器
    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
  • 同步监视器的执行过程
    1. 第一个线程访问,锁定同步监视器,执行其中代码;
    2. 第二个线程访问,发现同步监视器被锁定,无法访问;
    3. 第一个线程访问完毕,解锁同步监视器;
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

死锁

产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用;
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺;
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

注意:

只要破坏其中任意一个或多个条件就可以避免死锁发生

Lock(锁)

class TestLock implements Runnable{
    int tickNums = 10;
    // 定义lock锁,ReentrantLock可重用锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock(); // 加锁
                if (tickNums > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tickNums--);
                }else {
                    break;
                }
            } finally {
                // 解锁
                lock.unlock();
            }
        }
    }
}

如果同步代码有异常,要将unlock()写入finally语句块

synchronized与Lock的对比

  • Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:
    • Lock > 同步代码块 (已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

线程协作

应用场景:生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止;
  • 如果仓库中有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止

线程通信

  • wait()
  • wait(long timeout)
  • notify()
  • notifyAll()

解决方式1:

并发写作模型“生产者/消费者模式” -->管程法

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

package com.ty.Thread_prac.state;

// 测试:生产者消费者模型-->利用缓冲区解决:管程法
// 生产者,消费者,产品,缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }

}

// 生产者
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }
    }
}

// 消费者
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }
}

// 产品
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

// 缓冲区
class SynContainer{
    Chicken[] chickens = new Chicken[10];
    int count = 0;

    // 生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if (count == chickens.length){
            //通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果没有满,我们就需要丢入产品
        chickens[count]=chicken;
        count++;
        // 可以通知消费者消费了
        this.notifyAll();
    }

    // 消费者消费产品
    public synchronized Chicken pop(){
        // 判断能否消费
        if (count == 0){
            // 等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 如果可以消费
        count--;
        Chicken chicken = chickens[count];

        // 吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }

}

解决方式2:

并发协作模型“生产者/消费者模式” -->信号灯法

package com.ty.Thread_prac.state;

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

// 生产者--> 演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 ==0){
                this.tv.play("快乐大本营播放中");
            }else{
                this.tv.play("抖音:记录美好生活");
            }
        }
    }
}

// 消费者--> 观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

// 产品 -- > 节目
class TV{
    // 演员表演,观众等待  T
    // 观众观看,演员等待  F
    String voice;// 表演节目
    boolean flag = true;

    // 表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:" + voice);
        // 通知观众
        this.notifyAll(); // 通知唤醒
        this.voice = voice;
        this.flag = !this.flag;
    }

    // 观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了:" + voice);
        // 通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

线程池

思路

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具

好处

  • 提高响应速度(减少了创建新线程的时间)
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 便于线程管理(…)
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

  • JDK5.0起提供了线程池相关API:ExecutorService和Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
    • Futuresubmit(Callabletask):执行任务,有返回值,一般用来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

package com.ty.Thread_prac.state;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        // 1.创建服务,创建线程池,参数为线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        // 2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

线程总结:

public class ThreadNew{
	pulic static void main(String[] args){
		new MyThread1().start();
		
		new Thread(new MyThread2()).start();
		
		FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
		new Thread(fatureTask).start();
		
		try{
			Intrger integer = futureTask.get();
			System.out.println(integer);
		}catch(InterruptedException e){
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

//1、继承Thread类
class MyThread1 extends Thread{
	@override
	public void run(){
		System.out.println("MyThread1");
	}
}

//2、实现Runnable接口
class MyThread2 imlpements Runnable{
	@override
	public void run(){
		System.out.println("MyThread2");
	}
}

//3、实现Runnable接口
class MyThread3 implements Callable<Integer>{
	@override
	public Integer call() throes Exception{
		System.out.println("MyThread3");
		return 100;
	}
}

七、注解

什么是注解Annotation

**作用:**可以被其他程序(编译器)读取

格式:@注释名

**使用:**通过反射机制编程实现对元数据的访问

内置注解

**@Override:**定义在java.lang.Override中,表示一个方法声明打算重写超类中的另一个方法声明(作用在方法)

**@Deprecated:**定义在java.lang.Deprecated中,不鼓励使用(作用在方法、属性和类)

**@SupperessWarnings:**定义在java.lang.SupperessWarnings中,用于抑制编译时的警告信息,这个需要参数(“all” “unchecked” )

元注解

 // @Target  表示我们的注解在什么地方起作用
@Target(value ={ElementType.METHOD,ElementType.TYPE})

// @Retention  表示我们的注解在什么地方还有效  SOURCE<CLASS<RUNTIME
@Retention(value= RetentionPolicy.RUNTIME)

// @Documented   表示是否将我们的注解生成在Javadoc中
@Documented

// @Inherited    子类可以继承父类的注解
@Inherited

//定义一个注解,直接写内部类
 @interface MyAnnotation{}

自定义注解

  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
  • 分析:
    • 格式:public @interface注解名{定义内容}
    • 其中每一个方法实际上是声明了一个配置参数
    • 方法的名称就是参数的名称
    • 返回值类型就是参数的类型(基本类型)
    • 可以通过default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名为value
    • 注解元素必须要有值,定义注解元素时,经常使用空字符串、0作为默认值

例子

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Test03 {
    // 注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @MyAnnotation02(age = 18,name = "李四")  //有默认值可以不写
    public void test(){}
    
    @MyAnnotation03("赵四")		//默认值只有一个,用value表示,使用时可以省略
    public void test02(){}    
}

@Target({ElementType.TYPE,ElementType.METHOD})  //作用在类和方法上
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02{
    // 注解的参数:参数类型 + 参数名();
    String name() default "";
    int age() default 0;
    int id() default -1;  // 如果默认值为-1,代表不存在
    String[] schools() default {"清华大学","北京大学"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation03{
    // 注解的参数:参数类型 + 参数名();
   String value();
}

八、反射

静态VS动态语言

动态语言

  • 在运行时,代码可以根据某些条件改变自身结构
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等

静态语言

  • 在运行时,代码不可以改变自身结构
  • Java称为“准动态语言”。可以利用反射机制获得类似动态语言的特性。

Java Reflection

  • 反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。通过这个对象看到类的结构。形象称之为反射

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjhcoixr-1637023966187)(java.assets/image-20210806210619030.png)]

反射对象优缺点

**优点:**可以实现动态创建对象和编译,体现出很大的灵活性

**缺点:**性能较慢

(原因:反射是一种解释操作,告诉JVM希望做什么并让JVM满足要求,这类操作总是慢于直接执行相同的操作)

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
package com.ty.JavaPractice.annotation;

public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的class对象
        Class c1 = Class.forName("com.ty.JavaPractice.annotation.User");
        System.out.println(c1);

        // 一个类在内存中只有一个class对象
        // 一个类被加载后,类的整个结构都会被封装在class对象中
        Class c2 = Class.forName("com.ty.JavaPractice.annotation.User");
        Class c3 = Class.forName("com.ty.JavaPractice.annotation.User");
        Class c4 = Class.forName("com.ty.JavaPractice.annotation.User");
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
}

Class类

在Object类中定义了以下的方法,此方法将被所有子类继承

public final Class getClass()

反射的本质:通过对象反射求出类的名称

Class类的内容:

类的属性、方法和构造器、实现的接口

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都记得由哪个类实例生成
  • 通过Class可以获得类所有加载的结构信息
  • Class是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类常用的方法

方法名功能说明
static Class.forName(String name);返回指定类名name的Class对象
Object newInstance();调用缺省构造函数,返回对象的一个实例
getName();返回此Class对象所表示的实体(类、接口、数组类或void)的名称
Class getSupperClass();返回当前Class对象的父类的Class对象
Class[] getinterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Method getMethod(String name,Class… T)返回一个Method对象,次对象的形参类型为paramType
Field[] getDeclaredFields()返回Field对象的一个数组

获取Class类的实例

  1. 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class clazz = Student.class;//通过类名.class获得
clazz输出为:class demo01.Student
  1. 已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = person.getClass();//通过对象获得
  1. 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
Class clazz = Class.forName("demo01.Student");//通过forname获得
  1. 内置基本数据类型可以直接用类名.Type
  2. 还可以利用ClassLoader

哪些类型可以有Class对象

  • **class:**外部类、成员(成员内部类、静态内部类),局部内部类,匿名内部类

  • **interface:**接口

  • **[]:**数组

  • **enum:**枚举

  • **annotation:**注解@interface

  • **primitive type:**基本数据类型

  • void

// 类
Class c1 = Object.class;
// 接口
Class c2 = Comparable.class;
// 一维数组
Class c3 = String[].class;
// 二维数组
Class c4 = int[][].class;
// 注解
Class c5 = Override.class;
// 枚举
Class c6 = ElementType.class;
// 基本数据类型
Class c7 = Integer.class;
// void
Class c8 = void.class;
// Class
Class c9 = Class.class;

注:只要元素类型与维度一样,就是同一个Class

类加载内存分析

package com.ty.JavaPractice.annotation;

public class Test05 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
        /*
        1.加载到内存,会产生一个类对应Class对象
        2.链接,链接结束后m=0,赋默认值;
        3.初始化
           <clinit>(){
           System.out.println("A的静态代码块区域");
           m=10;
           m=20;
           }
          输出m=20
        */
    }
}

class A{
    static {
        System.out.println("A的静态代码块区域");
        int m = 10;
    }
    static int  m = 20;
    public A(){
        System.out.println("A类的无参构造函数初始化");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-47l98Vsn-1637023966189)(java.assets/image-20210807094931326.png)]

什么时候会发生初始化

类的主动引用(一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法堆类进行反射调用
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
  • 通过数组定义类引用,不会触发此类的初始化
  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
//测试类什么时候初始化
public class Test06 {
    static {
        System.out.println("main所在的类被加载");
    }
    public static void main(String[] args) {
        //1.主动引用
        Son son = new Son();
        /**
        输出结果为:
            main所在的类被加载
            父类被加载
            子类被加载
        **/
    }
}
class Father{
    static int b = 1;
    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 10;
    }
    static  int m =20;
    static  final  int M = 25;
 public static void main(String[] args) throws ClassNotFoundException {
        //2.反射也会产生主动引用
        Class.forName("com.ty.JavaPractice.annotation.Son");
        /**
         * 输出结果为:
         *      main所在的类被加载
         *      父类被加载
         *      子类被加载
         */
    }
public static void main(String[] args) throws ClassNotFoundException {
        // 3.不会产生类的引用
        System.out.println(Son.b);
    /*
    输出结果为:
    	main所在的类被加载
        父类被加载
        1
    */
    }
public static void main(String[] args) throws ClassNotFoundException {        
        Son[] array = new Son[5];
        /**
         * 输出结果为;
         *      main所在的类被加载
         */
    }
 public static void main(String[] args) throws ClassNotFoundException {
       // 子类里的常量
        System.out.println(Son.M);
        /*
        * 输出结果为:
        *   main所在的类被加载
            25
        * */
    }

类加载器

**类加载器作用:**将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java,lang.Class对象,作为方法区中类数据的访问入口

**类缓存:**标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0bnQpFzD-1637023966191)(java.assets/image-20210807105911900.png)]

类型:

  • 引导类加载器(Bootstap Classloader):C++编写,JVM自带的类加载器,负责Java平台核心库,用来加载核心类库。无法直接获取
  • 扩展类加载器(Extension Classloader):负责jre/lib/ext目录下的jar包 或 java.ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器(System Classloader):负责java-classpath 或 java.class.path所指的目录下的类与jar包装入工作(常用的加载器)

加载机制:

  • 双亲委派机制:如果自定义一个与java自带的库相同的包,则会选择加载java自带的,保证安全
package com.ty.JavaPractice.annotation;

public class Test07 {
    public static void main(String[] args) {
        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        // jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d

        // 获取系统类加载器的父类加载器-->扩展加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        // jdk.internal.loader.ClassLoaders$PlatformClassLoader@723279cf

        // 获取扩展加载器的父类加载器-->根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
        // null
        
        //如何获取系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
    }
}

获取类的运行时结构

package com.ty.JavaPractice.annotation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.ty.JavaPractice.annotation.User");

        // 获得类的名字
        System.out.println(c1.getName());  // 获得包名+类名
        System.out.println(c1.getSimpleName()); // 获得类名

        // 获得类的属性
        System.out.println("====================");
        Field[] fields = c1.getFields(); // 只能找到public属性
        fields = c1.getDeclaredFields(); // 找到所有的属性
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("====================");
        // 获得指定的属性
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        // 获得类的方法
        System.out.println("====================");
        Method[] methods = c1.getMethods(); // 获得本类及其父类的全部public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        Method[] declaredMethods = c1.getDeclaredMethods();// 获得本类的所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        // 获得指定方法
        // 由于是重载,所以得指定参数
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);

        // 获得指定的构造器
        System.out.println("====================");
        Constructor[] constructors = c1.getConstructors();
        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        // 获得指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println(declaredConstructor);
    }
}

动态创建对象执行方法

  • 调用Class对象的newInstance()方法的条件:
    • 类必须有一个无参数的构造器
    • 类的构造器的访问权限需要足够
  • 当没有无参构造函数时,需要借助Class类的getDeclaredConstructor()方法来创建对象
  • 调用类中的方法通过getDeclaredMethod()
    • 通过Class类的getDeclaredMethod(String name,Class…parameterTypes)取得对象,设置相应的参数
    • 使用invoke(对象,值)进行调用
  • 方法、属性和构造器对象都有setAccessible()方法
    • setAccessible()作用是启动和禁用访问安全检查的开关
    • 参数为true表示反射对象在使用时取消Java语言访问检查,即可以访问私有属性
package com.ty.JavaPractice.annotation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test10 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 获得class对象
        Class c1 = Class.forName("com.ty.JavaPractice.annotation.User");

        // 构造一个对象
        User user1 = (User) c1.newInstance(); // 本质是调用了类的无参构造器
        System.out.println(user1);

        // 通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 =(User) constructor.newInstance("李四", 23, 2);
        System.out.println(user2);

        // 通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);
        // invoke:激活的意思(对象,”方法的值“)
        setName.invoke(user3,"赵四");
        System.out.println(user3);

        // 通过反射操作属性
        System.out.println("=================================");
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        // 私有属性需要关闭程序的安全检测,属性或方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"王五");
        System.out.println(user4);
    }
}

性能对比分析

普通调用方法 最快

关闭Java语言检查进行反射调用,setAccessible(true) 其次

直接反射调用方法 最慢

反射操作泛型

  • Java采用泛型擦除的机制来引入泛型。

    • 仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题。
    • 一旦编译完成,所有和泛型有关的类型全部擦除
  • 反射泛型相关的类:

    • ParameterizedType:表示一种参数化类型
    • GenericArrayType:表示一种元素类是参数化类型或者类型变量的数组类型
    • TypeVariable:是各种类型变量的公共父接口
    • WildcardType:代表一种通配符类型表达式
package com.ty.JavaPractice.annotation;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test11 {
    public static void test01(Map<String,User> map, List<User> list){
        System.out.println("test01");
    }
    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method test01 = Test11.class.getMethod("test01", Map.class, List.class);

        Type[] genericParameterTypes = test01.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            if (genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        System.out.println("================================");
        Method test02 = Test11.class.getMethod("test02", null);
        Type genericReturnType = test02.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
}

ORM(对象关系映射)

  • 类和表对应
  • 属性和字段对应
  • 对象和记录对应
package com.ty.JavaPractice.annotation;

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.ty.JavaPractice.annotation.Student");

        // 通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //输出结果:@com.kuang.reflection.TableA(value=db_student)
        // 获得注解的val值
        TableA tableA = (TableA)c1.getAnnotation(TableA.class);
        String value = tableA.value();
        System.out.println(value);

        // 获得指定的注解
        Field name = c1.getDeclaredField("name");
        FieldA annotation = name.getAnnotation(FieldA.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
		//输出为:db_name   varchar    10
    }
}

@TableA("db_student")
class Student{
    @FieldA(columnName = "db_name",type = "varchar",length = 10)
    private String name;
    @FieldA(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldA(columnName = "db_age",type = "int",length = 10)
    private int age;

    public Student() {
    }

    public Student(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableA{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldA{
    String columnName();
    String type();
    int length();
}

面试

基本数据类型

浮点数

​ float和double表示的长度都是优先的,存在舍入误差的情况,所以float和double类型的变量虽然赋予的值相等,但是二者比较则不会相等。

float f = 0.1f;
double d = 1.0/10;
System.out.println(f==d); // false

银行业务设计浮点数时,使用BigDecimal数学工具类

BigDecimal num1 = new BigDecimal("24");
BigDecimal num2 = new BigDecimal("2");
BigDecimal sum_result = num1.add(num2);  // 加法
BigDecimal sub_result = num1.subtract(num2);  // 减法
BigDecimal mul_result = num1.multiply(num2); // 乘法;
BigDecimal abs_result = num1.abs();     // 绝对值
BigDecimal div_result = num1.divide(num2);  // 除法;

字符

编码Unicode: 占2字节 0~65536

转义字符:

\t 制表符

\n 换行

​ \u Unicode编码 \u0061表示16进制数,10进制为65

字符数组

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);  //false;

String str3 = "hello";
String str4 = "hello";
System.out.println(str3 == str4); // true

StringBuilder 和 StringBuilder的区别

共同点

都继承的是抽象的字符串父类:AbstractStringBuilder

区别点

1. 线程安全

​ StringBuffer的所有公开方法都是synchronized修饰的,而StringBuilder并没有;因此StringBuffer是线程安全的,而StingBuilder是线程不安全的

2.缓冲区

​ StringBuffer每次获取toString都会直接使用缓冲区的toStringCache值来构造一个字符串;StringBuilder则每次都需要复制一次字符数组,再构造一个字符串

3.性能

​ 由于StringBuilder没有线程安全,因此性能要远大于StringBuffer。

name;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

@Override
public String toString() {
    return "Student{" +
            "name='" + name + '\'' +
            ", id=" + id +
            ", age=" + age +
            '}';
}

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableA{
String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldA{
String columnName();
String type();
int length();
}












# 面试

## 基本数据类型

### 浮点数

​			float和double表示的长度都是优先的,存在舍入误差的情况,所以float和double类型的变量虽然赋予的值相等,但是二者比较则不会相等。

```java
float f = 0.1f;
double d = 1.0/10;
System.out.println(f==d); // false

银行业务设计浮点数时,使用BigDecimal数学工具类

BigDecimal num1 = new BigDecimal("24");
BigDecimal num2 = new BigDecimal("2");
BigDecimal sum_result = num1.add(num2);  // 加法
BigDecimal sub_result = num1.subtract(num2);  // 减法
BigDecimal mul_result = num1.multiply(num2); // 乘法;
BigDecimal abs_result = num1.abs();     // 绝对值
BigDecimal div_result = num1.divide(num2);  // 除法;

字符

编码Unicode: 占2字节 0~65536

转义字符:

\t 制表符

\n 换行

​ \u Unicode编码 \u0061表示16进制数,10进制为65

字符数组

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);  //false;

String str3 = "hello";
String str4 = "hello";
System.out.println(str3 == str4); // true

StringBuilder 和 StringBuilder的区别

共同点

都继承的是抽象的字符串父类:AbstractStringBuilder

区别点

1. 线程安全

​ StringBuffer的所有公开方法都是synchronized修饰的,而StringBuilder并没有;因此StringBuffer是线程安全的,而StingBuilder是线程不安全的

2.缓冲区

​ StringBuffer每次获取toString都会直接使用缓冲区的toStringCache值来构造一个字符串;StringBuilder则每次都需要复制一次字符数组,再构造一个字符串

3.性能

​ 由于StringBuilder没有线程安全,因此性能要远大于StringBuffer。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值