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();
}
}
包装类
引用类型转换为基本类型
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
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-8、GBK、ISO-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;
}