JavaSE笔记

Java SE

存数据、处理数据

编程思维、API、Debug

  • 基础语法
    • 数据类型、变量、运算符、流程控制
    • 数组
    • 函数
    • String字符串
  • 面向对象
    • 封装、继承、多态
    • 面向对象编程思想
    • 面向接口编程
  • 高级编程
    • 集合
    • 异常
    • IO流
    • 反射

数据类型

基础类型 → 存储值

引用类型 → 存储的是一个地址(指针)

堆:内存大速度慢;数据

栈:内存小速度快;变量(地址)

8种基础数据类型

整数: byte 1字节=8位(-128~127)short (-32768~32767 )

int 4字节 long 8字节

小数: float 4字节 double 8字节

布尔: boolean 1字节

字符: char 2字节

基本数据类型运算

/*算式中,(注意前提),如果单有一个整数,那么该整数就是int类型*/
short s = 1;//不出错,因为不是算式
s = s+1;	//错误
short s = 1+1;	//不出错,1+1有固定明确的值,不涉及到变量,所以在编译时都已经得到了结果,相当于 short s = 2;

/*在算式中,(注意前提),如果单有一个小数,那么该小数就是double类型*/
int a = 1;
float b = a+1.0;	//错误

/*byte\short\char 数据类型在进行运算时,结果都统一转换为int类型*/
short s = 1;
byte b = 2;
s = b+s;	//错误

引用类型

  • 类类型(对象类型)
  • 数组类型
  • 接口
  • 枚举

作为参数传递时,传递地址值

默认值

int a = 0;
char c = '';
double d = 0.0;
String s = null;
boolean b = false;

二进制负数

负数的补码: 将正数按位取反,然后+1

  • 第一步:正3 0000 0011
  • 第二步: 取反 1111 1100
  • 第三步: 加1 1111 1101

运算符

短路运算

int a = 2,b = 1;
if(a < b && a++ > b){
    System.out.print(a);//&&前判断出结果&&后未运行
}
//运行结果:1

三元运算

x > y ? true : false
// :前后必须有值

流程控制

判断

注意逻辑顺序,依次执行,若出判断结果则不会继续判断

if

if(a == b){
    System.out.print("相等")
}else{
    System.out.print("不相等")
}

switch

int a = 2;
switch(a){
    case 1:
        System.out.println("1");
        break;
    case 2:
        System.out.println("2");
        break;
    default:
        System.out.println("*");
}

循环

for

for(int i = 0; i < 100; i++){
    System.out.print(i);
}
// 顺序:int i = 0; → i < 100; → print(); → i++ → i < 100; → print(); → i++ ···

while

int i = 0;
while(i<100){
    System.out.print(i);
    i++;
}

do while

int i = 0;
do{
	System.out.print(i);
	i++;    
}while(i<100);

数组

存放固定类型,固定长度的容器

格式

/*三种格式*/
int[] arr = new int[5];
int[] arr = {1,2,3,4,5};
int[] arr = new int[]{1,2,3,4,5}; //可传参使用

方法

/*扩容、截取*/
arr = Arrays.copyOf(arr,leanth);

/*转换成字符串*/
Arrays.toString(arr);

/*排序*/
Arrays.sort(arr);	//小→大
for (int i = 0; i < arr.length/2; i++) {	//大→小
    int temp = arr[i];
    arr[i] = arr[arr.length-1-i];
    arr[arr.length-1-i] = temp;
}

/*查找*/
int index = Arrays.binarySearch(arr,5);	//必须先排序,未找到返回负数

多维数组

int arr[][] = new int[3][2];
int[][] arr = {{1,2},{1,2},{1,2}};

arr.length = 3;
aee[0].length = 2;

Arrays类

int[] arr = {1,2,3};
//1.转换为字符串类型
Arrays.toString(arr);
//2.排序:小→大
Arrays.sort(arr);

函数

形参类型 和 返回值类型 无关

public static 返回类型 函数名(参数列表){
	函数体
}

返回值

无返回值

//定义
public static void printX(int n , String str){
	for(int i = 0; i < n; i++){
		System.out.println(str);
	}
}
//当前类调用
printX(5,"*****")
//其他类调用 
class.printX(5,"*****")    

有返回值

public static int sumX(){
    int sum = 0;
    for(int i = 1; i < 100; i++){
        sum = sum + i;
	}
    return sum;
}

例:往数组中放入20个不重复的随机数

public static void main(String[] args) {
    Random random = new Random();
    int[] arr = new int[20];
    int i = 0;
    while (true) {
        int r = random.nextInt(50) + 1;
        if (!contains(arr, r)) {
            arr[i] = r;
            if(i == arr.length-1){
                break;
            }
            i++;
        }
    }
    System.out.println(Arrays.toString(arr));
}

public static boolean contains(int[] arr, int num) {
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == num) {
            return true;
        }
    }
    return false;
}

可变参数

    public static void add(int... num){	//只能在结尾
        int sum = 0;
        for (int i = 0; i < num.length; i++) {
            sum = sum + num[i];
        }
        System.out.println(sum);
    }

递归

方法自己调用自己(占内存)

例:计算阶乘,5! = 5 * 4 * 3 * 2 * 1

    public static int f(int n){
        if(n == 1){
            return 1;
        }else {
            return n * f(n-1);
        }
    }

方法重载

  • 方法名相同
  • 参数列表不同
  • 与返回值无关

面向对象

本质:以类的方式组织代码,以对象的方式组织(封装)数据

类和对象

类是什么?

类是一种数据类型,是用户自己定义的数据类型。【使用角度】

类是一种复合数据类型,可以将多个相关的散乱的数据封装为一个整体。【类的属性】

类中不但可以保存数据,还可以保存功能,将对象操作的函数,也放在类中。【类的方法】

类是由属性和方法构成,一个描述静态数据,一个描述动态功能。【整体理解】

对象是什么?

类是抽象的,对象是具体的。

类类型的变量,我们称为对象。

对象的创建使用new关键字

可以通过.来访问对象的属性和对象的方法。

匿名对象

new Student().print();		//只能调用一次

构造器

//无参构造
public Rect() {
}
//含参构造
public Rect(int w, int l) {
    this.w = w;
    this.l = l;
}
  • 方法名与类名一致
  • 没有返回类型
  • new本质在调用构造器
  • 初始化对象的值
  • 如果用户一个构造方法也没有编写,系统会自动添加一个无参构造
  • 一旦用户添加任何的构造方法,系统都不会再添加这个无参构造
  • 一个类中可以写多个参数不同的构造方法,来满足不同的用途

this

指代当前对象的声明(地址值)

this();		   //调用无参构造,要求:第一行
this(name,age);//在构造函数中调用其他构造,要求:第一行

this.name = name;
             👆传进来的

全局变量和局部变量

全局变量在堆内存中

局部变量在方法区

堆内存会给全局变量(若没有值)赋予初始化值

public void add(int num){
    return this.num + num;
    //     👆全局变量    👆局部变量(就近原则)
}

三大特性

封装

高内聚,低耦合

get/set
  • 提高程序的安全性,保护数据(数据过滤)
  • 隐藏代码的实现细节
  • 统一接口
  • 系统可维护性增加
    public String getName() {
        return name;
    }

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

继承

所有的类都默认继承Object类

子类继承父类就会拥有父类方法(私有和静态除外),静态可以调用

先(默认)调用父类构造方法,子类无参构造隐藏代码:super();

super

super调用父类的构造方法,必须在第一行

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

super和this不能同时调用构造方法

super.name		//父类的参数
super.print()   //父类的方法 

和this区别:

代表的对象不同

  • this:本身调用者这个对象
  • super:代表父类对象的应用

前提

  • this:没有继承也可以使用
  • super:只能在继承条件下才可以使用

构造方法

  • this(); 本类构造
  • super(); 父类构造
方法重写

前提:

  • 方法名、参数列表、返回值必须相同
  • 修饰符范围可以扩大但不能缩小
  • 抛出异常可以缩小但不能扩大

多态

一个对象的实际类型是确定的

可指向的引用类型不确定:父类的引用指向子类

前提:

  • 继承关系、方法需要重写、父类引用指向子类
//父类的引用指向子类,子类型向父类型自动转换
Person p = new Student();    //编译看左,运行看右
	p.已重写方法;	//调用子类方法,父类方法被子类覆盖
    p.同一属性;	 //调用父类属性,属性继承不存在重写
Student s = new Student();
Teracher t = new Teracher();
Person[] p = new Person[2];
p[0] = s;
p[2] = t;
instanceof
boolean b = X instanceof Y ;	//判断有无继承关系
类型转换

父类 转 子类(强转)

if(person instanceof Student){
	Student s = (Student) person;//转换后可用子类方法
    s.子类方法;
}

子类 转 父类(自动转换)

修饰符

public: 本类 、同包子类其他类 、不同包子类 、不同包其他类
protected:本类 、同包子类其他类 、不同包下子类
default: 本类 、同包下子类其他类
privated: 本类

只有public、default能修饰类

修饰类:
    public
    public final	public abstract
修饰成员变量:
	private
    public static	public final	public static final
修饰成员方法:
    public	  private (少)
	public static	public final	public abstract
修饰构造方法:
    public	  private (少)	protected (少)

static

静态方法

不能使用一个静态的引用,指向一个非静态的属性

不能被重写

static作用:一份(类的)(静态的)

  • 方法不添加static,非静态方法,对象的方法,每个对象中都有一份该方法。
  • 方法添加static后,静态方法,类的方法,该类所有的对象共用同一个方法。
  • 在静态方法中,不能使用非静态属性(方法)

静态属性

被static修饰的属性:

  • 静态属性、类属性、独一份
  • 非静态方法中,能使用静态属性

区别

静态方法(属性)是和类一起加载的,非静态方法实例化(new)之后才存在

调用:静态–类名.方法名 非静态–先new,对象名.方法名

代码块

静态代码块

static{
    //和类一起加载,只执行一次
}

执行顺序:

  • 类→静态代码块→匿名代码块(构造代码块)→构造方法
  • (静态)成员和(静态)代码块顺序取决于代码顺序

构造代码块

  • 写在类中,对构造方法初始化的操作,早于构造方法(与代码上下位置无端)

局部代码块

  • 写在函数中,与方法在同一栈区

同步代码块

  • 多线程

final

常量必须在当前类初始化之前被赋予值

public class Person{
	final int NUM;
	public Person{
		NUM = 60;
	}
}
  • 修饰类不能被继承
  • 修饰方法不能被重写
  • 修饰属性不能被更改,只能赋值一次
    • 基本类型:值不能被改变
    • 引用类型:地址值不能被改变
  • 修饰形参,在方法体内无法更改
  • 修饰对象,地址值不能更改:cat = new Cat()

抽象类

抽象类只有方法名字,没有方法的实现,但可以写普通方法

普通类继承抽象类必须重写所有方法

不能new抽象类

public abstract class demo {
    //抽象方法
    public abstract void print();
}

//继承方式
//1.重写抽象方法	2.抽象类继承抽象类
public class demo2 extends demo{
    @override
}

//多态
Person	//抽象类
Student	//普通类、继承父类Person
Person p = new Student();

作用:解决耦合度过高(不完全解决)

注意:

  • 抽象类有构造函数,用于给子类对象实例化
  • 不可以和private、static、final共存
  • 抽象类一定是父类

接口

专业约束,约束和实现分离,面向接口

接口中只能有:静态常量和抽象方法

没有构造方法,不能进行实例化

public interface demo {	//静态常量    
public static final int A = 1;    int B = 2;	//默认加public static final    
//抽象方法    
public abstract void add(int... a);    
void delete(int b);    //默认加public abstract
}
//继承
public interface demo2 extends 接口1,接口2...{}
public class demo3 extends demo4 implements 接口1,接口2...{    @override}

作用:

  • 约束
  • 定义一些方法,让不同的人实现
  • 不能被实例化(new)
  • 可以实现多个接口

常用类

Object

Object obj = new Object();
//1.比较两个对象属性值是否“相等”
obj.Equals(Object obj2)
//2.得到对象的哈希码值,代表当前对象在内存的位置
obj.HashCode():
/*需要重写以上两个方法,用于比较两对象是否一致*/

//3.返回该对象的字符串表示,把声明的地址值方式变成属性的显示方式
obj.toStirng()

System

属性:

  • err 错误输出流 System.err.println(“helloword”)
  • In 输入流 Scanner sc= new Scanner(System.in)
  • Out 输出流 System.out.println(“helloword”)

方法:

//1.运行垃圾回收器
System.gc()
//2.终止jvm参数为 0和非零(0:正常关闭  非零:非法关闭)
System.exit(int status)
//3.返回当前时间的时间戳(毫秒值),从1970/1/1 0:0:0 计为0毫秒
long l = System.currentTimeMillis()

Date

Java.util

构造方法:

//1.Date():获取当前时间
Date d = new Date();
System.out.println(d);	//Sun Jan 31 21:15:22 CST 2021
//2.Date(long l):根据时间戳,得到对应的时间
Date d = new Date(1612098922552L);
System.out.println(d);	//Sun Jan 31 21:15:22 CST 2021

成员方法:

Date d = new Date();
//getTime():将日期对象转换成对应时间的毫秒值
long time = d.getTime();
System.out.println(l);	//1612098922552
//setTime():设置时间戳
long time = d.setTime(1612098922552L);

SimpleDateFormat

//SimpleDateFormat():Date格式化
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
Date d = new Date();
String s = df.format(d)
//df.parse(str):字符串转时间,抛异常
String time = "2021-7-27 15:27:26";
Date d = df.parse(time)

Calendar

//1.getInstance():根据当前系统时区和语言环境获取日历对象
Calendar c = Calendar.getInstance();
//2.get(int field):返回给定日历字段的值
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH)+1;
int day = c.get(Calendar.DATE);
//3.void set(y,m):将给定的日历字段设置为指定的值
c.set(2022,2,2);
int year = c.get(Calendar.YEAR);	//2022
int month = c.get(Calendar.MONTH);	//2
int day = c.get(Calendar.DATE);		//2
//4.add()
c.add(Calendar.YEAR,-3);	//2018
//5.after()、before():c1是否在c2之后/前
boolean b = c.after(c1,c2);
boolean b = c.before(c1,c2);

LocalDate

//1.获取当前日期 
LocalDate localDate = LocalDate.now()  
//2.根据参数设置日期
LocalDate localDate = LocalDate.of(y,m,d)
//3.get...方法
localDate.getDayOfMonth()	//获取当前日期是所在月的第几天
localDate.getDayOfWeek()	//获取当前日期是星期几
localDate.getDayOfYear()	//获取当前日期是所在年的第几天
localDate.getMonth()		//获取当前日期所在月份
localDate.getMonthValue()	//获取当前日期所在月份的数值
//4.计算
localDate.lengthOfMonth()	//获取当前日期所在月份有多少天
localDate.lengthOfYear()	//获取当前日期所在年有多少天
localDate.isLeapYear()		//判断当前日期所在年是否是闰年
//5.替换
localDate.withDayOfMonth(int dm)//替换参数中的日
localDate.withDayOfYear(int dy)	//替换参数中的天数
localDate.withMonth(int m)		//替换参数中的月
localDate.withYear(int y)		//替换参数中的年
//6.加减,Weeks、Months、Years同理
localDate.plusDays(long days)	//将当前日期加一天
localDate.minusDays(long days)	//将当前日期减一天

LocalTime

//1.获取当前时间
LocalTime localTime = LocalTime.now();
//2.根据参数设置时间
LocalTime localTime = LocalTime.of(12,35,59);
localTime = LocalTime.of(12,35);
//3.get...方法
localTime.getHour()		//获取当前时间的小时数
localTime.getMinute()	//获取当前时间的分钟数
localTime.getSecond()	//获取当前时间的秒数
//4.替换
localTime.withHour(int h)	//替换参数中的小时
localTime.withMinute(int m)	//替换参数中的分钟
localTime.withSecond(int s)	//替换参数中的秒
//5.加减,Minutes、Seconds同理
localTime.minusHours(long hours)//将当前时间减一小时
localTime.plusHours(long hours)	//将当前时间加一小时

Scanner

Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
String s = sc.next();
sc.close;

Math

//圆周率
double pi = Math.PI;
//绝对值
int a = Math.abs(-1);
//向上取整
double d = Math.ceil(3.14);	//4
//向下取整
double d = Math.floor(3.9);	//3
//四舍五入
double d = Math.round(3.5);	//4
//取两数中大/小值
int a = Math.max(3,4);	//4
int a = Math.min(3,4);	//3
//10的2次方
Math.pow(10,2);	//100
//平方根
int a = Math.sqrt(4);	//2
//0-1间随机小数
double d = Math.random();
//1-15间随机小数
double d = Math.random()*15+1;

Random

//伪随机数
Random r = new Random();
r.nextInt(101)+1; //1~100
r.nextBytes(byte[] bytes)
r.nextDouble()	//0-1
//种子Random(long l)
Random r = new Random(4564154484);
/*
种子的作用:
1.不含参的构造函数每次都使用当前时间作为种子,随机性更强
2.随机数的生成是从种子值开始。 如果反复使用同一个种子,就会生成相同的数字系列,产生不同序列的一种方法是使种子值与时间相关
*/

UUID

UUID uuid = UUID.randomUUID();
String s =uuid.toString().replace("-","");

内部类

外部类是用来保护内部类的,外部类需要创建对象访问内部类成员

成员内部类

public class Outter {
    //内部类👉类的成员
    private class Inner {
        int age = 5;
    }
    //调用内部类的方法
    public void in(){
        Inner i = new Inner();
        i.age;
    }
}

静态内部类

public class Outter {
    static String name = "外部类属性";
    //静态内部类👉类的成员
    private static class Inner {
        String name = "内部类属性";
        public void show(){
            System.out.println(name);
            System.out.println(Outter.name);//外部类属性
        }
    }
}

匿名内部类

//接口
public interface Light {
    void shine();
}
//测试类
public class TestLight {
    public static void main(String[] args) {
        //匿名内部类👉类的成员
    	Light l = new Light() {
            @Override
            public void shine() {
                System.out.println("shine in yellow");
            }
        };
        l.shine();
    }
}

局部内部类

public class Outter {
    private static String str = "外部类属性";
    public void method() {
        String str = "外部类函数局部变量";
        //静态内部类👉外部类的函数中
        class Inner {
            String str = "局部内部类属性";
            public void show(){
                System.out.println(str);
                System.out.println(Outter.str);//外部类属性
            }
        }
        Inner in = new Inner();
        in.show();
    }
}    

包装类

引用类型转换为基本类型

基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Integer

byte、short、long、float、double、boolean同int

//字符串转为整形
String s = "1";
Integer i = new Integer(s);	//装箱
int a = i.intValue();		//拆箱
--------------------------------------------------------
//jdk1.6后自动装/拆箱
Integer i = 1;
int a = i;
--------------------------------------------------------
👇
int a = new Integer(s);
👇
int a = Integer.parseInt(s);

Character

char c = new Character('a');
boolean b = Character.isUpperCase(c);	//判断是否为大写字母
boolean b = Character.isLowerCase(c);	//判断是否为小写字母
char c2 = Character.toUpperCase(c);	//转换成大写字母
char c2 = Character.toLowerCase(c);	//转换成小写字母
boolean b = Character.isDigit(c);	//判断是否为数字

String类

String str = null; 是指对象未创建,给了一个空的声明,不能调用方法

构造方法

String(byte[] b)	//把字节数组转换成字符串
String(byte[] b,int start,int len)
String(char[] c)	//把字符数组转换成字符串
String(char[] c,int start,int len)

方法

String str = “abcdef”;

判断功能
str.equals(“abc”)比较两个对象的内容是否一致
str.equalsIgnoreCase(“ABC”)同上,不区分大小写
str.contains(“abc”)是否包含abc
str.startsWith(“a”)是否是a开头
str.endsWith(“c”)是否是c结尾
str.isEmpty()字符串是否为空
获取功能
int a = str.length()获取字符串值的长度
char c = str.charAt(0)根据字符串值的下标,获取指定字符
int a = str.indexOf(‘c’)获取指定字符第一次出现的索引
int a = str.indexOf(“bc”,3)获取指定字符串第一次出现的索引,从3开始
int a = str.lastIndexOf(‘c’)获取指定字符最后一次出现的索引
String s = str.substring(3)从指定位置截取后面字符串
String s = str.substring(3,5)从指定位置开始到指定位置结束,前闭后开
转换功能
byte[] b = str.getBytes()把字符串转换成字节数组
char[] c = str.toCharArray()把字符串转换成字符数组
String s = String.valueOf(1)把其他类型转换成字符串
String s = str.toLowerCase()转换为小写字母
String s = str.toUpperCase()转换为大写字母
String s = str.concat(str2)两个字符串拼接
替换功能
String s = str.replace(‘a’,‘b’)替换字符
String s = str.replace(“old”,“new”)替换字符串
其他功能
String s = str.trim();去除字符串两端空格
String[] s = str.split(“,”)以指定字符/字符串分割字符串,开头多结尾省,返回字符串数组
String s = String.join(“+”,str[])以字符+分隔数组中的元素
String.format(“%.2f”,d)保留两位小数

String容器

StringBuffer

线程同步,效率低,安全

字符缓冲区,存放String,可以修改

构造方法:

  • StringBuffer():创建一个初始容量为16的字符串缓冲区,自动扩容
  • StringBuffer(int capacity):初始容量为capacity,不会自动扩容
  • StringBuffer(String s):创建一个有初始值的字符缓冲区

方法:

StringBuffer stringBuffer = new StringBuffer();
//1.返回字符个数
int len = stringBuffer.length()
//2.返回当前容量
int cap = stringBuffer.capacity()
//3.添加值(任何类型,自动转换成字符串),返回本身
stringBuffer.append(s)
//4.在指定位置插入值,返回其本身
stringBuffer.insert(1,"abc")
//5.删除指定下标字符,返回本身
stringBuffer.deleteCharAt(1)
//6.删除1-3下标字符(前闭后开),返回本身
stringBuffer.delete(1,3)
//7.替换指定索引的值,返回本身
stringBuffer.setCharAt(2,'a')
//8.替换指定位置区间的值,返回本身
stringBuffer.replace(1,3,"abc")
//9.获取对应下表字符
char c = stringBuffer.charAt(2)
//10.获取指定字符第一次出现的索引
int a = stringBuffer.insexOf('a')
//11.反转,返回本身
stringBuffer.reverse()
//12.截取,前闭后开
String str = stringBuffer.substring(0,9)
//13.StringBuffer👉String
String str1 = stringBuffer.substring(0)
String str2 = new String(stringBuffer)
String str3 = stringBuffer.toString()
String str4 = "" + stringBuffer;

StringBuilder

线程不同步,效率高

集合

存放不定长度,任意引用类型的容器

Collection

ArryayList:有序可重复

HashSet:无需不可重复

Collection c = new ArryayList()
//1.添加引用类型元素
boolean b = c.add("a")	//自动装箱并向上转型
//2.获取集合长度
int a = c.size()
//3.删除指定元素
boolean b = c.remove("a")
//4.清空集合
c.clear();
//5.判断是否包含某个元素
boolean b = c.contains("a")
//6.集合是否为空
boolean b = c.isEmpty()

//7.把一个集合中的元素添加到另一个集合
boolean b = c.addAll(c2)
//8.参照指定集合,删除与该集合相同的元素
boolean b = c.removeAll(c2)
//9.一个集合中是否完全包含另一个集合
boolean b = c.containsAll(c2)

List

Collection的子接口,有序可重复,继承父类方法

List的常用实现类:ArrayList、LinkedList、Vector

  • 相同:都是List的实现类,都是有序可重复集合
  • 不同:
    • ArrayList:数组结构,查询快增删慢,线程不安全,效率高
    • LinkedList:链表结构,查询慢增删快,线程不安全,效率高
    • Vector:数组结构,查询快增删慢,线程安全,效率低
List<String> l = new ArrayList<>();
//1.在指定索引添加元素,当前索引元素后移
boolean b = l.add(2,"abc");
//2.删除所在索引的元素并返回
Object o = l.remove(2);
//3.获取指定索引的值
Object o = l.get(2)
//4.设置指定索引的值,返回被替换的值
Object o = l.set(2,"def")
ArrayList*

List的子类,继承父类方法

ArrayList<String> al = new ArrayList<>();
LinkedList*

List的子类,链表结构

LinkedList l = new LinkedList();
//1.添加到第一个位置,所在元素后移
l.addFirst("abc");
//2.添加到最后,所在元素前移
l.addLast("xyz");
//3.获取第一个元素
Object o = l.getFirst();
//4.获取最后一个元素
Object o = l.getLast();
//5.删除第一个元素
Object o = l.removeFirst();
//6.删除最后一个元素
Object o = l.removeLast();
Vector
List<String> list = new Vector<String>();

Set

Collection的子接口,无序不可重复,继承父类方法

无序:

  • 存入顺序和取出顺序不一致(Hash码排序)
  • 根据HashCode和Equals方法去除重复
    • 首先查看两个对象的hashcode值是否相同
    • hashcode值不同的对象是不同对象,可直接存储
    • hashcode值相同,用equals方法比对属性值是否一致
HashSet*

Set子类,无序不可重复

Set<String> s = new HashSet<String>();
LinkedHashSet
  • 元素有序唯一
  • 有链表保证元素有序
  • 由哈希表保证元素唯一
  • 没有下标
Set<Integer> s = new LinkedHashSet<Integer>();
TreeSet

Set子类,自然顺序(二叉树原理:按字母顺序、数字从小到大)不可重复

Set<String> s = new TreeSet<String>();

Collections类

ArrayList<Integer> al = new ArrayList<Integer>();
//1.排序:小→大
Collections.sort(al);
//2.二分法查询值,前提:先排序
int i = Collections.binarySearch(al,50)
//3.集合中的最大值
Collections.max(al)
//4.集合倒序
Collections.reverse(al)
//5.随机排序
Collections.shuffle(al)

Map

是体系的根接口,是一个双列集合

值是键值对:key-value,表示一组数据

key唯一不重复,value是key的映射

HashMap*

无序,key不可重复,value可重复

Map<Integer,String> m = new HashMap<Integer,String>();
//1.添加,同key后替换前,返回被替换的值
String value = m.put(1,"abc");
//2.删除指定key的值并返回
String value = m.remove(1);
//3.返回集合长度
int a = m.size();
//4.清空集合
m.clear();
//5.判断是否为空
boolean b = m.isEmpty();
//6.判断指定key是否存在
boolean b = m.containsKey(1);
//7.判断指定值是否存在
boolean b = m.containsValue("abc");
//8.根据key取出值
String value = m.get(1);
//9.获取所有值
Collection<String> c = m.values()
//10.遍历集合
//(1)
Set<Integer> i = m.keySet();	//获取所有key值,返回数组
for (Integer key : i) {
    String value = m.get(key);
    System.out.println(key+":"+value);
}
//(2)
Set<Map.Entry<Integer,String>> s = m.entrySet();
for(Map.Entry<Integer,String> entry : s){
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key+":"+value);
}
//(3)快捷生成:iter
for (Map.Entry<Integer,String> entry : map.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key+":"+value);	
}
LinkedHashMap

key有序

TreeMap

遍历集合

集合转数组

  • toArray():集合转换为数组
Collection<String> c = new ArryayList<>();
Object[] arr = c.toArray();
for(int i = 0, i < arr.length, i++){
    System.out.println((String)arr[i]);
}

迭代器

  • next(): 返回迭代的下一个元素
  • hasNext(): 如果仍有元素可以迭代,则返回true
Collection<String> c = new ArryayList<>();
Iterator it = c.iterator();
while(it.hasNext()){
	System.out.println((String)it.next());
}

增强for

专一用来遍历容器

for(Student stu : list){	//快捷生成iter
    System.out.println(stu)
}

数据结构

  • 栈 👉先进后出
  • 队列 👉先进先出
  • 数组 👉查询快,增删慢
  • 链表 👉查询慢,增删快
  • 哈希表

泛型

作用:强行定义类型

格式:

  • 类:类名+<引用类型>
  • 方法:public+<引用类型>
  • 接口:接口名+<引用类型>
    • <?> 任意类型
    • <? extends Animal> 👉 Animal以及其子类
    • <? super Cat> 👉 Cat以及其父类

异常

Throwable

  • Error
  • Exception
    • 编译时异常
    • 运行时异常(RunTimeException)

捕获异常

try {
    //监控区
}catch (Exception e){ //异常类
    //捕获异常、解决方案
    e.printStackTrace();	//错误输出流,输出错误信息和位置
}finally{
    //一定执行,一般放关闭资源代码,不能写return
}

抛异常

//throws
public viod test() throws Exception{
}
//throw
throw new MyException("z");	//主动抛异常,只能抛出一个异常类对象

自定义异常

public class MyException extends Exception{
    public MyException() {
        super();
    }
    public MyException(String message) {
        super(message);
    }
}

IO流

输入输出流,是Java中用来传输数据的方式

File类

File file = new File("C:/Work");

//构造方法
File("C:/Work/FileTest")				//(路径)
File("C:/Work/FileTest", "Test.txt")	//(文件夹;文件或文件夹)
File(file, "FileTest/Test.txt")			//(File类;文件或文件夹)
//成员方法
boolean b = file.createNewFile()	//创建文件
boolean b = file.mkdir()			//创建单极目录
boolean b = file.mkdirs()			//创建目录
boolean b = file.delete()			//删除文件或文件夹(文件夹必须为空)
boolean b = file.renameTo(file2)	//重命名,路径不同(剪切+重命名)
//判断功能
file.isDirectory()	//判断File对象是否为目录
file.isFile()		//判断File对象是否为文件
file.exists()    	//判断File对象是否存在
file.canRead()		//判断File是否可读
file.canWrite()		//判断File是否可写
file.isHidden()		//判断File是否隐藏
//获取功能
String str = file.getAbsolutePath()	//获取绝对路径
String str = file.getPath()    		//获取相对路径
String str = file.getName()			//获取文件名
int l = file.length()				//获取文件长度
long l = file.lastModified()		//获取最后一次修改时间
String[] str = file.list()			//获取文件夹下所有文件(夹)的名称数组
File[] file = file.listFile()		//获取文件夹下所有文件(夹)的File对象数组   

字节流读写文件

构造方法

FileOutputStream(File file)
FileOutputStream(String name)
FileOutputStream(String name,boolean append)	//在原基础上+

字节流:

  • InputStream:字节输入流的顶层抽象类
    • FileInputStream:
    • BufferedInputStream:
  • OutputStream:字节输出流的顶层抽象类
    • FileOutputStream:
    • BufferedOutputStream:

拷贝文件

FileInputStream is = new FileInputStream("lib/1.jpg");
FileOutputStream os = new FileOutputStream("lib/2.jpg");
//按字节读写
int data;
while((data = is.read()) != -1){
    os.write(data);
}
//按字节数组读写
byte[] b = new byte[1024];
int len;
while((len = is.read(b)) != -1){
    os.write(b,0,len);
}
is.close();
os.close();

//try...catch写法
public static void main(String[] args) {
    try (
        FileInputStream in = new FileInputStream("E:/1.jpeg");
        FileOutputStream out = new FileOutputStream("D:/1.jpeg")
    ) {
        int len;
        byte[] bytes = new byte[1024];
        while ((len = in.read(bytes)) != -1) {
            out.write(bytes, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

高效字节流

BufferedInputStream is = new BufferedInputStream(new FileInputStream("lib/01.jpg"));
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("lib/02.jpg"));
//按字节读写
int data;
while((data = is.read()) != -1){
    os.write(data);
}
//按字节数组读写
int len;
byte[] b = new byte[1024];
while((len = is.read(b)) != -1){
    os.wrute(b,0,len);
}

is.close();
os.close();

//os.flush()	刷新(提交)缓冲区
//BufferedOutputStream(OutputStream out,int size);	size缓冲区大小,容量不足自动提交

字符流读写文件

字符流 = 字节流 + 编码表

构造方法

InputStreamReader(FileInputStream out)
InputStreamReader(FileInputStream out, String charsetName)		//编码格式
OutputStreamWriter(FileOutputStream out)
OutputStreamWriter(FileOutputStream out, String charsetName)	//编码格式

UTF-8GBKISO-8859-1

字符流:

  • Reader:字符输入流的顶层抽象类
    • FileReader:
    • BufferedReader:
  • Writer:字符输出流的顶层抽象类
    • FileWriter:
    • BufferedWriter:

读取数据

//1.处理异常
throws IOException
//2.创建字符输入流对象    
Reader reader = new FileReader("lib/test.txt");
//3.读取数据
1.按单个字符读取
int data;	//存储字符对应ASCII码
while((data=reader.read()) != -1){
    System.out.print(data);
}
2.按字符数组读取
char[] c = new char[5];
int len;	//存储读取到字符长度,无内容返回-1
while((len=reader.read(c)) != -1){
    String s = new String(c,0,len);
    System.out.print(s);
}
//4.释放资源
reader.close();

写入数据

//1.处理异常
throws IOException
//2.创建字符输出流对象    
Writer writer = new FileWriter("lib/test.txt");
//3.写入数据
1.按单个字符写入
writer.writer('牛');		//void
2.按字符数组写入
char[] c = {'加','油','!'};
writer.writer(c,0,2);
3.按字符串写入
writer.writer("啦啦啦");
//4.释放资源
writer.close();

拷贝数据

//1.处理异常
throws IOException
//2.创建字符输入/输出流对象    
Reader reader = new FileReader("lib/1.txt");
Writer writer = new FileWriter("lib/2.txt");
//3.拷贝数据
1.按单个字符拷贝
int data;
while((data=reader.read()) != -1){
    writer.writer(data);
}
2.按字符数组拷贝
char[] c = new char[1024];
int len;
while((len=reader.read(c)) != -1){
    writer.writer(c,0,len);
}
//4.释放资源
reader.close();
writer.close();

字符缓冲流

缓冲区8192个字符(16kb)

BufferedReader br = new BufferedReader(new FileReader("lib/1.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("lib/2.txt"));
//普通用法(按字符数组拷贝)
int data;
while((data=br.read()) != -1 ){
    bw.write(data);
}
//一次读写一行
String str;
while((str = br.readLine()) != null){
    bw.write(str);
    bw.newLine();	//换行
}
br.close();
bw.close();

Properties

.properties配置文件

格式:key=value

Properties类

  • 简介:Map的子类,key和value必须是String格式
  • 作用:把集合中的数据写道文件中,实现持久化
Properties p = new Properties();
p.setProperties("key","value");
p.getProperties("key");
//读取文件中的数据到集合中
FileInputStream in = new FileInputStream(".../src/test.properties");
p.load(in);
in.close();
//把集合中的数据写到文件中,一个文件只能存储一个对象
FileOutputStream out = new FileOutputStream(".../src/test.properties");
p.store(out,"description");
out.close();

序列化流

public class Student implement Serializable{
    //动态序列化id 42841392348718407L;
    private static final long serialVersionUID = 2454845...;
    //默认序列化id
    private static final long serialVersionUID = 1L;
}
//序列化
//把对象写出到文件中实现对象的持久化存储
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:/test.txt"));
Student s = new Student();
out.writeObject(s)
out.close();
//反序列化
//把ObjectOutputStream写出的对象重构到内存中
ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:/test.txt"));
Object obj = in.readObject();
Student s = (Student)obj;
in.close();

dos命令

md		创建文件夹
echo	创建文件,echo 内容>>文件名
rd		删除文件夹(只能删除空文件)
del		删除文件
cd		切换目录

多线程

Thread类

继承Thread类

  • 子类继承类具备多线程能力
  • 启动线程:t.start()
  • 缺点:单继承限制
//继承Thread后成为线程类
public class ThreadTest extends Thread{
    @Override
    public void run() {
        //线程执行任务
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) {
        //创建3个线程
        new ThreadTest().start();
        new ThreadTest().start();
        new ThreadTest().start();
        //展开
        ThreadTest t = new ThreadTest();
        t.start();     //启动线程,jvm调用run,不能多次启动
    }
}

线程状态

  • NEW:尚未启动的线程状态
  • RUNNABLE:在Java虚拟机中执行的线程状态
  • BLOCKED:被阻塞等待监视器锁定的线程状态
  • WAITING:正在等待另一个线程执行特定动作的线程状态
  • TIMED_WAITING:等待另一个线程执行动作达到指定等待时间的线程状态
  • TERMINATED:已退出的线程状态

Thread方法

ThreadTest t = new ThreadTest();

获取方法
t.setName(“线程1”)设置线程的名字
String name = this.getName()得到线程的名字
Thread thread = Thread.currentThread()得到正在执行的线程
t.getState()得到线程状态
Thread.State.TERMINATED线程状态
线程操控
int p = t.getPriority()查看优先级1-10低到高(默认为5)
t.setPriority(2)设置优先级
Thread.sleep(1000)线程休眠,每个对象都有一把锁,sleep不会释放锁
t.join()线程插队,设置某一线程先执行完毕
Thread.yield()线程礼让,线程执行一次然后让给其他线程,不一定成功
t.setDaemon(true)守护线程,只剩守护线程直接结束
this.stop()关闭线程(过时)
boolean b = this.interrupted()判断线程是否被中断
this.interrupt()中断线程
boolean b = t.isAlive()判断线程是否存活

Runnable接口

实现Runnable接口

  • 实现接口具有多线程能力
  • 启动线程:new Thread®.start();
  • 优点:避免单继承局限性,方变同一个对象被多个线程使用
//实现Runnable接口,一般类
public class RunnableTest implements Runnable{
    boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println(i+);
        }
    }
	//通过外部标识位停止线程
    public void stop(){
    	this.flag = false;    
    }
    
    public static void main(String[] args) {
        RunnableTest r = new RunnableTest();
        new Thread(r,"线程1").start();
        new Thread(r,"线程2").start();
        new Thread(r,"线程3").start();
        //展开
        Thread t = new Thread(r);
        t.start();
        
        for(int i = 0; i < 1000; i++){
            System.out.println("main线程-->"+i);
            if(i == 100){
                r.stop();
                System.out.println("Runnable线程停止");
            }
        }
    }
}

线程安全

  • 同步:同一时间只有一个线程操作,效率低,安全
    • 适用于多线程操作同一资源
  • 异步:多个线程干各自的事,互不干扰,效率高,不安全
    • 适用于操作不同资源

线程同步:共享数据加锁

synchronized:

隐式锁:自动获取锁、自动释放锁

//同步代码块
@Override
public void run() {
    while (true){
        synchronized (this){	//锁:obj、this、.class
            if(ticket > 0){
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-->"+ticket--);
            }
        }
    }
}
//同步方法,默认this锁
public synchronized void sellTicket(){
    while (true){
        if(ticket > 0){
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->"+ticket--);
        }
    }
}

Lock接口:

显式锁:主动加锁、释放锁

ReentranLock实现类:可重入互斥锁

Lock l = new ReentranLock()

  • 获取锁:l.lock()
  • 释放锁:l.unlock()
Lock l = new ReentranLock();	//创建锁
//类中加锁
@Override
public void run() {
    while (true){
        l.lock;		//手动获取锁
        try {
            Thread.sleep(5);
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName()+"-->"+ticket--);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            l.unlock();		//释放锁
        }
    }
}
//方法加锁
public void sellTicket(){
    lock.lock();
    try {
        if(ticket > 0){
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName()+"-->"+ticket--);
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        lock.unlock();
    }
}

线程间通信

等待唤醒机制:

  • wait():线程进入锁池等待,需要其他线程唤醒
  • notify():随机唤醒锁池中的一个线程
  • notifyAll():唤醒所有线程

前提:

  • 操作的必须是同一资源

  • 要通信的任务用的是同一把锁

**作用:**避免cpu性能大量浪费

把所有从锁池中唤醒的线程重新判断(while)

每次唤醒所有等待中的线程,避免死锁

boolean state = false;
int num = 0;
//送货
public synchronized void produce(){
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    while(state == true){	//if→while重新判断
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println(Thread.currentThread().getName()+"送达第"+(++num)+"批货");
    state = true;
    this.notifyAll();	//唤醒全部线程,避免死锁
}
//取货
public synchronized void customer(){
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    while(state == false){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println(Thread.currentThread().getName()+"取了第"+num+"批货");
    state = false;
    this.notifyAll();
}

Condition接口

Condition可以为Lock提供多组等待唤醒机制

  • await():线程等待
  • signal():唤醒一个等待线程
  • signalAll():唤醒所有等待线程
boolean state = false;
int num = 0;

Lock lock = new ReentrantLock();	//可重入互斥锁
Condition pro = lock.newCondition();	//送货的锁池
Condition cus = lock.newCondition();	//取货的锁池
//送货
public void produce(){
    lock.lock();
    try {
        Thread.sleep(5);
        while(state == true){
            pro.await();
        }
        System.out.println(Thread.currentThread().getName()+"送达第"+(++num)+"批货");
        state = true;
        cus.signal();
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        lock.unlock();
    }
}
//取货
public synchronized void customer(){
    lock.lock();
    try {
        Thread.sleep(5);
        while(state == false){
            cus.await();
        }
        System.out.println(Thread.currentThread().getName()+"取了第"+num+"批货");
        state = false;
        pro.signal();
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        lock.unlock();
    }
}

线程池

线程池:存放线程的容器

线程池存放一些线程,有需求就分配,不用就还给线程池

Executor:线程池的顶级接口

Executors:提供各种线程池

ExecutorService:继承Executor提供方法

  • Future<?> f = es.submit(Thread t):提交可运行的任务执行
  • f.get():获取结果
ExecutorService es = Executors.newCachedThreadPool();	//创建不定线程数线程池
ExecutorService es = Executors.newFixedThreadPool(3);	//创建固定线程数线程池
ExecutorService es = newSingleThreadExecutor();	//创建使用单个worker线程的Executor

ExecutorTest e1 = new ExecutorTest();
ExecutorTest e2 = new ExecutorTest();

es.submit(e1);
es.execute(e2);
service.shutdown();

Callable接口

  • 实现Callable接口,需要返回值类型Callable<?>
  • 重写call方法,需要抛异常
  • 创建目标对象
  • 创建执行服务:ExecutorService es = Executors.newFixedThreadPool(3)
  • 提交执行:Future<?> f = es.submit(t)
  • 获取结果:f.get()
  • 关闭服务:es.shutdownNow()
public class CallableTest implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
        return "执行完毕";
    }
    public static void main(String[] args) throws Exception {
        CallableTest c1 = new CallableTest();
        CallableTest c2 = new CallableTest();
        CallableTest c3 = new CallableTest();
        ExecutorService es = Executors.newCachedThreadPool();
        Future<String> str = es.submit(c1);
        es.submit(c2);
        es.submit(c3);
        System.out.println(str.get());
    }
}

Lambda表达式

优点:

  • 避免匿名内部类过多
  • 代码简洁
  • 函数式编程

函数式接口:

  • 只有一个抽象方法的接口
  • 函数式接口可以通过Lambda表达式创建给接口的对象,例:Runnable
    • new Thread( ()->System.out.println(“new RunnableTest”) ).start();
//接口
public interface Light {
    void shine();
}
//测试类
public class TestLight {
    Light light = null;
    public static void main(String[] args) {
        //匿名内部类
    	light = new Light() {
            @Override
            public void shine() {
                System.out.println("shine in yellow");
            }
        };
        //Lambda表达式简化,(int a)中是参数
        light = ()->{
            System.out.println("shine in yellow");
        };
        //终极简化,可省略参数类型,只有一个参数可省略(),只有一行内容可省略{}
        light = ()->System.out.println("shine in yellow");
        light = a->System.out.println("shine in yellow");	//带参
        
        //调用方法
        light.shine();
    }
}

设计模式

  • 创建型模式
    • 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式
    • 适配器模式、交接模式、组合模式、装饰模式、外观模式、亨元模式、代理模式
  • 行为型模式
    • 访问者模式、模板模式、策略模式、状态模式、观察者模式、备忘录模式、
    • 中介者模式、迭代器模式、解释器模式、命令模式、责任链模式

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

单例模式是指定某对象在整个程序中是唯一对象,例:线程池

  • 饿汉式:程序启动第一时间创建该对象

    • 这种方式比较常用,但容易产生垃圾对象。
    • 优点:没有加锁,执行效率会提高。
    • 缺点:类加载时就初始化,浪费内存。
  • 懒汉式:什么时候用到该对象什么时候创建

    • 优点:第一次调用才初始化,避免内存浪费。
    • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
    • 注意:不加锁的懒汉式严格意义上不能算是单例模式,多线程时不能正常工作
//饿汉式
public class Person{
    //私有构造方法,不能new对象
    private Person(){}
    //创建私有对象
    private static Person p = new Person();
    //公开的访问方法
    public static Person person(){
        return p;
    }
}
//懒汉式
public class Person{
    private Person(){}
    private static Person p = null;
    public static synchronized Person person(){
        if(p == null){
            p = new Person();
        }
        return p;
    }
}
public class Test{
    public static void main(String[] args){
        Person p1 = Person.person();
        Person p2 = Person.person();	//两者地址值相同
    }
}

工厂模式

在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

简单说:把创建对象的过程交给一个工厂类来管理

  • 优点:

    • 一个调用者想创建一个对象,只要知道其名称就可以了。
    • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
    • 屏蔽产品的具体实现,调用者只关心产品的接口。
  • 缺点:

    • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
public interface Animal{
    void eat();
}
public class dog implements Animal{
    @Override
    public void eat(){
        System.out.println("狗粮");
    }
}
public class cat implements Animal{
    @Override
    public void eat(){
        System.out.println("猫粮");
    }
}
//工厂类
public class AnimalFactory{
    public Dog getDog(){
        return new Dog();	//调用类
    }
    public Cat getCat(){
        return new Cat();
    }
    //静态工厂
    public static Dog getDog(){
		return new Dog();
	}
	public static Cat getCat(){
		return new Cat();
	}
    //利用反射原理创建任意对象,静态工厂
    public static Object createObject(String pathName){
        Class clazz;
        try{
            clazz = Class.forName(pathName);
            Object obj = clazz.newInstance();
            return obj;
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
public class Test{
    public static void main(String[] args){
        AnimalFactory a = new AnimalFactory();
        a.getDog();
        a.getCat();
        Object o = AnimalFactory.createObject(com.study.day23.Cat);
    }
}

抽象工厂模式

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类

每个生成的工厂都能按照工厂模式提供对象,参考集合

  • 优点:
    • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  • 缺点:
    • 产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码
public interface Sender{
    void send();
}
public class QQ implements Sender{
    @Override
    public void send(){
        System.out.println("QQ发消息");
    }
}
public class Email implements Sender{
    @Override
    public void send(){
        System.out.println("Email发消息");
    }
}
//创捷接口,用于抽象接口工厂
public interface Proviter{
	Sender produce();    
}
//创建QQ的抽象工厂
public class QQFactory implements Proviter{
    @Override
    public Sender produce(){
        return new QQ;
    }
}
//创建Email的抽象工厂
public class EmailFactory implements Proviter{
    @Override
    public Sender produce(){
        return new Email;
    }
}
public class Test{
    public static void main(String[] args){
        Proviter p1 = new QQfactory();
        Sender qq = p1.produce();
        qq.send();
        Proviter p2 = new EmailFactory();
		Sender email = p2.produce();
		email.send();
    }
}

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

  • 优点:
    • 装饰类和被装饰类可以独立发展,不会相互耦合
    • 装饰模式是继承的一个替代模式,可以动态扩展一个实现类的功能
    • 可代替继承。
  • 缺点:
    • 多层装饰比较复杂
public class LOL{
    public void garen(){
        System.out.println("QWER");
    }
}
//装饰类
public class LOLChina{
    private LOL lol;
    public LOLChina(LOL lol){
        this.lol = lol;
    }
    public void garen(){
        System.out.println("德玛西亚!");	//添加装饰
        lol.garen();	//原有
    }
}
public class Test{
    public static void main(String[] args){
        LOLChina lol = new LOLChina(new LOL());
        lol.garen();
    }
}

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,创建具有现有对象的对象,以便向外界提供功能接口。

  • 优点:
    • 职责清晰。
    • 高扩展性。
    • 智能化。
  • 缺点:
    • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
    • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

静态代理

  • 目标对象:前提基于接口(实现接口)
  • 代理对象

例:Runnable,Thread

// 结婚 == Runnable
interface Marry{
    void happyMarry();
}

// 目标对象:人 == RunnableTest
class You implements Marry{
    @Override
    public void happyMarry() {
        System.out.println("张三结婚");
    }
}

// 代理对象:代理 == Thread
class WeddingProxy implements Marry{
    private Marry marry;
    public WeddingProxy(Marry marry){
        this.marry = marry;
    }
    @Override
    public void happyMarry() {
        System.out.println("方法前,");
        marry.happyMarry();
        System.out.println("方法后。");
    }
}

public class Proxy {
    public static void main(String[] args) {
        new WeddingProxy(new You()).happyMarry();
    }
}

动态代理

jdk动态代理:目标对象需要实现接口

代理对象:实现InvocationHandler接口

//通过jdk动态代理实现业务
public class MyProxy implements InvocationHandler {
    //目标对象
    private Object obj;

    //获取代理对象
    public Object getProxyInstance(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null; 
        
        try{
            result = method.invoke(obj, args);
            System.out.println("事务提交...");
        }catch{
            System.out.println("事务回滚..");
        }
        
        return result;
    }
}

测试

@Test
public void myProxyTest(){
    UserDao userDao = new UserDaoImpl();

    MyProxy myProxy = new MyProxy();
    UserDao userDaoProxy = (UserDao) myProxy.getProxyInstance(userDao);

    User user = new User();
    user.setName("管理员");
    user.setUsername("admin");
    user.setPassword("123");
    int num = 0;
    try {
        num = userDaoProxy.addUser(DBUtil.getConn(),user);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        DBUtil.close();
    }
    System.out.println(num);
}

cglib动态代理:目标对象不需要实现接口,也称子类代理

引入cglib的jar包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

代理对象:实现MethodInterceptor接口

//动态在内存中构建目标对象的子类对象
public class CglibProxy implements MethodInterceptor {
    //获取代理对象
    public Object getProxyInstance(Object obj){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法前,");

        Object result = methodProxy.invokeSuper(o, objects);

        System.out.println("方法后。");
        return result;
    }
}

测试

@Test
public void cglibProxyTest(){
    MyTest t = new MyTest();

    CglibProxy cglibProxy = new CglibProxy();
    MyTest myTest = (MyTest) cglibProxy.getProxyInstance(t);
    myTest.myPrint();
}

总结

JDK动态代理与CGLib动态代理均是实现Spring AOP的基础

在Spring的AOP编程中:

  • 如果加入容器的目标对象有实现接口,用JDK代理
  • 如果目标对象没有实现接口,用Cglib代理

注解

Annotation是从JDK5.0开始引入的新技术.

  • 作用:
    • 不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)
    • 可以被其他程序(比如:编译器等)读取
  • 格式:
    • 注解是以"@注释名"在代码中存在的,还可以添加一些参数值
    • 例如:@SuppressWarnings(value=“unchecked”).
  • Annotation使用位置:
    • 可以附加在package 、class 、 constructor、method 、field等上面
    • 可以通过反射机制编程实现对这些元数据的访问

内置注解:

  • @Override:重写注解
  • @Deprecated:过时注解,不推荐使用
  • @SupperessWarnings(“all”):警告镇压

元注解

元注解是用来修饰注解

4个标准的meta-annotation类型:Retention、Target、Documented、Inherited

@Retention():生命周期

  • RetentionPolicy.SOURCE
    • 在源文件中有效,即源文件保留
    • 编译器直接丢弃这种策略的注释
  • RetentionPolicy.CLASS
    • 在class文件中有效,即class保留
    • 当运行 Java 程序时, JVM不会保留注解,这是默认值
  • RetentionPolicy.RUNTIME
    • 在运行时有效,即运行时保留
    • 当运行 Java 程序时, JVM 会保留注释,程序可以通过反射获取该注释
@Retention(value = RetentionPolicy.RUNTIME)		//标识当前注解生命周期
@interface MyAnnotation{
}

@Target():位置

用于指定被修饰的Annotation能用于修饰哪些程序元素

@Target也包含一个名为value的成员变量

  • ElementType的值决定了该注解可以出现的位置:
    • PACKAGE:描述包
    • TYPE:描述类、接口(包括注解类型)或enum声明
    • FIELD:描述域
    • PARAMETER:描述参数
    • CONSTRUCTOR:描述构造器
    • METHOD:描述方法
@Target(value = {ElementType.CONSTRUCTOR,ElementType.FIELD})
@interface MyAnnotation{
}

@Documented

  • 被修饰的 Annotation 类将被javadoc工具提取成文档(默认javadoc是不包括注解)

  • 定义为Documented的注解必须设置Retention值为RUNTIME

  • 需要把当前类生成的帮助文档(api)中时,需要该注解

@Inherited

  • 被修饰的 Annotation 将具有继承性,其子类将自动具有该注解

自定义注解

关键词@interface定义

自定义注解自动实现java.lang.annotation.Annotation接口

  • 注解有成员变量,以无参方法的形式出现
    • 类型八种基本数据类型
    • String类型、Class类型、enum类型、Annotation类型、 以上所有类型的数组
  • 指定成员变量的初始值使用 default 关键字
  • 如果只有一个参数成员,可使用参数名为value,可以省略 “value=”
//【1】自定义注解@interface,自动实现Annotation接口
@Target({ElementType.TYPE})		//描述类、接口
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1{
    //定义参数,参数类型+参数名();
    int age() default 0;//设置默认值为0,可重新赋值,如果为-1,代表不存在
    String name();
    String[] address();
}
//【2】只有一个参数成员
@Target({ElementType.METHOD})	//描述方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2{
	//只有一个参数,参数名可以写value
	String[] value();
}
//使用
@MyAnnotation1(age=20,name="张三",address={"北京","天津"})
public class Test{
    //省略"value="
    @MyAnnotation2({"北京","天津"})
    public void test(){
        System.out.println("test方法");
    }
}

反射

Reflection(反射)是Java被视为动态语言的关键

反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

  • 构造
    • newInstance
    • 获取构造创建对象
  • 属性
    • get、set
    • 获取、设置属性
  • 方法
    • invoke
    • 获取、使用属性

获取Class对象

//【1】Object对象中的getClass()方法
Person p = new Perdon();
Class<Person> clazz = (Class<Person>)p.getClass;

//【2】类名.class,任意类型都有该静态属性
Class<Person> clazz = Person.class;

//【3】使用Class类中的静态方法forName(全类名)
Class clazz = Class.forName("com.study.Person")
    
//【4】只能用于基本数据类型的包装类
Class clazz = Integer.TYPE;
//获得父类class
Class claz = clazz.getSuperclass();
  • newInstance():实例化对象
    • 返回值:Object
//调用公开无参构造,直接创建本类对象
Object obj = clazz.newInstance();
Student stu = (Student)obj;	//针对已知类

获取构造方法

  • getModifiers():获取修饰符,如果有多个修饰符累计出现则返回值累加

    • 返回值:int
    • 0-默认修饰符、1-public、2-private、4-protected、8-static
      16-final、32-synchronize、64-volatile、128-transient
      1024-abstract、2048-strict、256-native、512-interface
  • TypeParameterTypes():获取参数列表

    • 返回值:Class数组
  • newInstance():实例化对象

    • 返回值:Object
//获取所有公开构造方法
Constructor[] arr = clazz.getConstructors();

//获取所有构造方法
Constructor[] arr = clazz.getDeclaredConstructors();

for(Constructor con : arr){
    //获取修饰符
    int num = con.getModifiers();
    //获取参数列表
    Class[] arrclass = con.TypeParameterTypes();
    for(Class c : arrclass){
        String name = c.getName();
    }
}
//获取公开无参构造
Constructor con = clazz.getConstructor();
Object obj = con.newInstance();

//获取公开有参构造
Constructor con = clazz.getConstructor(String.class);
Object obj = con.newInstance("");

//获取任意修饰符的构造方法
Constructor con = clazz.getDeclaredConstructor(String.class);
Object obj = con.newInstance("");

//获取私有方法需要强制其权限,才可创建对象
con.setAccessible(true);

获取属性

//获取所有的公有属性(父类本类)
Field[] arr = clazz.getFields();
for(Field f :arr){
	System.out.println("修饰符:"+f.getModifiers());
    System.out.println("属性名:"+f.getName());
}

//获取所有修饰符属性(本类)
Field[] arr = clazz.getDeclaredFields();
for(Field f :arr){
	System.out.println("修饰符:"+f.getModifiers());
    System.out.println("属性名:"+f.getName());
}
//得到某一个指定属性名的属性
Field f = clazz.getField("phone");
System.out.println("修饰符:"+f.getModifiers());
System.out.println("属性名:"+f.getName());

//得到任意一个修饰符的属性
Field f = clazz.getDeclaredField("name");
System.out.println("修饰符:"+f.getModifiers());
System.out.println("属性名:"+f.getName());

f1.setAccessible(true);
Constructor con = clazz.getConstructor();
Object obj = con.newInstance();

Object valueObj1 = f.get(obj);
System.out.println(valueObj1);
f.set(obj, "张三");	//给属性赋值

Object valueObj2 = f1.get(obj);
System.out.println(valueObj2);

获取成员方法

  • getReturnType():获取方法返回值
    • 返回值类型:类对象
  • invoke(Object obj,Object… args):执行该方法
    • obj : 要调用方法的对象,args:调用方式时所传递的实参
//得到所有公开方法(父类本类)
Method[] arrm = clazz.getMethods();
for(Method m :arrm){
    String methodName = m.getName();	//函数名
    int num = m.getModifiers();			//修饰符
    Class returnType = m.getReturnType();	//返回值类型
    System.out.println("修饰符:" + num);
    System.out.println("函数名:" + methodName);
    System.out.println("返回值类型:" + returnType);
    Class[] arrp = m.getParameterTypes();	//参数列表
    for(Class c : arrp){
        System.out.println("参数类型:"+c.getTypeName());
    }
}

//得到所有修饰符方法(本类)
Method[] arrm = clazz.getDeclaredMethods();
for(Method m :arrm){
    String methodName = m.getName();	//函数名
    int num = m.getModifiers();			//修饰符
    Class returnType = m.getReturnType();	//返回值类型
    System.out.println("修饰符:" + num);
    System.out.println("函数名:" + methodName);
    System.out.println("返回值类型:" + returnType);
    Class[] arrp = m.getParameterTypes();	//参数列表
    for(Class c : arrp){
        System.out.println("参数类型:"+c.getTypeName());
    }
}
//得到单个公开方法,知道方法名和参数列表,无参可以写null或者不写
Method m = clazz.getMethod("show1");
Object obj = clazz.newInstance();
//调用该方法
m.invoke(obj, args);	//执行该方法

//调用一个有参且私有的方法
Method m1 = clazz.getDeclaredMethod("show5",int.class,boolean.class);
m1.setAccessible(true);
m1.invoke(obj, 18,true);

类加载

类加载的过程

装载(Load),链接(Link)、初始化(Initialize)

  • 装载

    • 查找并加载类的二进制数据
  • 链接

    • 验证:确保被加载类的正确性
    • 准备:为类的静态变量分配内存,并将其初始化为默认值
    • 解析:把类中的符号引用转换为直接引用
  • 初始化:

    • 为类的静态变量赋予正确的初始值

加载器的种类

  • Bootstrap ClassLoader

引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

  • Extension ClassLoader

扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

  • App ClassLoader

系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

  • Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

枚举

枚举类

Jdk1.5后,提出了枚举类的定义,使用enum关键字类定义

Enum类是所有枚举类的根类,所有的枚举类都直接或间接继承Enum类

使用enum关键字来定义枚举类

  • 枚举类的定义方式:
    • 对外提供的固有对象,必须在代码开头
    • 枚举会自动认为其固有对象是静态常量
    • 枚举是一一列举,固有对象,用 , 分割用 ; 结束
public enum Season{
	SPRING("春天"),
	SUMMER("夏天"),
	AUTUMN("秋天"),
	WINTER("冬天");
	//定义为常量
	private final String SeasonName;//季节名称
    //私有构造
	private Season(String SeasonName){
		this.SeasonName = SeasonName;
	}
    //调用
    public static void main(String[] args) {
        Season autumn = Season.AUTUMN;
        System.out.println(autumn);
    }
}
  • values()方法:返回枚举类型的对象数组
  • valueOf(String str):把一个字符串转为对应的枚举类对象
public enum Week{
    //name(value)
	MONDAY("星期一"),    
	TUSEDAY("星期二"),        
	WENDESDAY("星期三"),       
	THURSDAY("星期四"),        
	FRIDAY("星期五"),      
	SATURDAY("星期六"),        
	SUNDAY("星期日");	
}
public static void main(String[] args) {
    //Week枚举类
    Week[] arr = Week.values();
    for(Week week : arr){
        System.out.println(week.value);	//星期一..
        System.out.println(week.name);	//MONDAY..
    }
	//根据name获取枚举类
    Week w = Week.valueOf("MONDAY");
    //等同
    Week w = Week.MONDAY;
}

枚举实现接口

public interface InterDemo {
	void show();
}
public enum Season implements InterDemo{
	//在类的内部,是可以调用私有的构造,也可以反复调用
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "阳光灿烂"),
    AUTUMN("秋天", "硕果累累"),
    WINTER("冬天", "白雪皑皑");
    //定义为常量
    private final String SeasonName;//季节名称 春夏秋冬
    private final String SeasonDes;//季节描述
    //由于Season的对象是4个对象
    //我们直接把这四个对象创建出来,不让调用者自主创建
    //我们固定的提供好着四个对象
    private Season(String SeasonName,String SeasonDes){
        this.SeasonName = SeasonName;
        this.SeasonDes = SeasonDes;
    }
    @Override
    public String toString() {
        return SeasonName+"---"+SeasonDes;
    }
    //重写show方法,供每个对象调用
    @Override
    public void show() {
        System.out.println("这是重写的方法,来自Inter接口的show");
    }
}
public static void main(String[] args) {
    //想要获取方法,必须获取对象
    Season s = Season.SPRING;
    s.show();
    Season s1 = Season.SUMMER;
    s1.show();
}
//每个对象调用自己的show方法
public enum Season implements InterDemo{
    SPRING("春天", "春暖花开"){
        @Override
        public void show() {
            System.out.println("春");	
        }
    },
    SUMMER("夏天", "阳光灿烂"){
        @Override
        public void show() {
            System.out.println("夏");
        }
    },
    AUTUMN("秋天", "硕果累累"){
        @Override
        public void show() {
            System.out.println("秋");	
        }
    },
    WINTER("冬天", "白雪皑皑"){
        @Override
        public void show() {
            System.out.println("冬");
        }
    };
    private final String SeasonName;
    private final String SeasonDes;
    private Season(String SeasonName,String SeasonDes){
        this.SeasonName = SeasonName;
        this.SeasonDes = SeasonDes;
    }
    @Override
    public String toString() {
        return SeasonName+"-"+SeasonDes;
    }
}
public static void main(String[] args) {
    Season s = Season.SPRING;
    Season s1 = Season.SUMMER;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值