java学习笔记

1 安装
1.1 JDK下载与安装
# 进入链接,选择java,选择jdk
https://www.oracle.com/downloads/

JDK = JRE + java开发工具
JRE = JVM + 核心内库
1.2 path环境变量配置
# 新增环境变量
JAVA_HOME   jdk安装主目录

# 编辑PATH变量
%JAVA_HOME%\bin

# 检测是否配置成功
javac

# 作用
在dos的任意目录,可以去使用java和javac
1.3 helloworld
// 新建文件HelloWorld.java
public class HelloWorld{
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}


// 编译,生成HelloWorld.class字节码文件
// javac HelloWorld.java

// 执行,本质就是将.class文件装载到jvm执行
// java HelloWorld

// 反编译
// javap HelloWorld

// 一个源文件中只能有一个public类,但是可以有多个其他类,编译后每个类都对应一个class文件

// 快捷键
psvm  主函数
sout  打印
ctrl+alter+v 生成左边
alter+insert 弹出构造方法,get,set快捷生成方式
ctrl+b 进入类、方法
ctrl+h 查看类的继承关系
ctrl+alter+b 查看方法的实现
ctrl+u 查看父类的实现
ctrl+F12 查看类的所有方法
ctrl+shift+/  当前位置添加注释


// 定义模板
file -> settings -> editor --> live templates
2 基础语法
2.1 注释
// 单行注释

/*
多行注释
*/

// 文档注释javadoc生成文档,javadoc -d 文件夹名 -author -version 文件名
/**
 * @author 作者
 * @version 标签
*/
2.2 数据类型
数据类型: 
	基本数据类型:
		数值型:
			整型: byte(1 8), short(2 16), int(4 32), long(8 64) 
			浮点数: float(4), double(8)
			字符: char(2) 
		非数值型:
			布尔类型: boolean(1)
	引用数据类型:
		类
		接口
		数组
2.3 变量
// 定义
数据类型 变量名 =;

// 标识符
由数字、字母、下划线或美元符$组成
方法名或变量采用小驼峰,类采用大驼峰

// 注意
作用域内变量名不能重复
局部变量未赋值前无法使用,全局变量有默认值
long类型的变量定义的时候,值后面要加L,防止整数过大
float类型的变量定义时,值后面要加f,防止类型不兼容


public class HelloWorld {
    public static void main(String[] args){

        long n = 1000000000000000L;
        System.out.println(n);
        
        float f = 12.45f;
        System.out.println(f);
        System.out.println(2.3e10);

    }
}
2.4 类型转换
// 自动类型转换:数据范围小的赋值给数据范围大的
((byte -> short) | char) -> int -> long -> float -> double

// 示例
double b = 10;   // 打印出10.0


// 强制类型转换: 数据范围大的赋值给数据范围小的
目标数据类型 变量名 = (目标数据类型)值或变量;

// 示例
int n = (int)b;
2.5 算术运算
// 整数操作的结果还是整数,要想得到小数,要有浮点数的参与

// 字符的+操作: 拿字符在计算机底层对应的数值来进行计算,'A'对应65,'a'对应97,'0'对应48

// 算术表达式中包含多个基本数据类型的值的时候,整个算术表达式的类型会自动进行提升。
/*
提升规则:
	byte类型,shor类型和char类型将提升到int类型
	整个表达式的类型自动提升到表达式中最高等级的操作数相同的类型
*/

// 字符串的+操作: 字符串进行拼接
/*
"aa" + 100  --> "aa100"
100 + "aa"  --> "100aa"
"aa" + 99 + 1 --> "aa991"
1 + 99 + "aa" --> "100aa"
*/

// += 隐含了强制类型转换
/*
short s = 10;
s += 10   // 可以正常执行,拆成 s = s + 10 会报错,类型不兼容
*/

// i++ 和 ++i:变量在前边,先参与操作,然后做++,变量在后面,先做++,然后再参与操作
/*
int i = 1;
int j = i++;  // i为2,j为1,先赋值给j,然后再++
int k = ++i;  // i先加1,然后赋值给k
*/

public class HelloWorld {
    public static void main(String[] args){

        int i = 1;
        int j = i++;
        int k = ++i;
        System.out.println(j);   // 1
        System.out.println(k);   // 3

    }
}
2.6 逻辑运算符
/*
&  与
|  或
^  异或
!  非

&& 短路与:一旦左边为false不会执行右边
|| 短路或:一旦左边为true不会执行右边
*/
2.7 三元运算符
// 格式
a > b ? a: b;  // 条件为真,结果为a,否则为b
2.8 数据输入
// Scanner
import java.util.Scanner;
// 创建对象
Scanner sc = new Scanner(System.in);
// 接收数据
int i = sc.nextInt();
2.9 if与switch`
if (关系表达式){
    语句体1;
}else if (关系表达式2){
    语句体2;
}else{
    语句体n;
}
switch(表达式){
    case1:
    case 值x:
        语句体1;
        break;  
    case2:
        语句体2;
        break;
    default:
        语句体n;
        [break;]
}


// 示例
import java.util.Scanner;


public class HelloWorld {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int i1 = sc.nextInt();
        System.out.println("i1: " + i1);
        switch (i1){
            case 1:
            case 2:
            case 3:
                System.out.println("1");
                break;
            case 4:
            case 5:
            case 6:
                System.out.println("2");
                break;
            default:
                System.out.println(i1);
        }
    }
}
2.10 循环
// for循环
/* 条件控制的变量生命周期仅在for循环内,和python不同
for (初始化语句;条件判断语句;条件控制语句){
	循环体语句;
}
*/

public static int[] swap(int[] nums) {
    for (int start = 0, end = nums.length - 1; start <= end; start += 1, end -= 1){
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
    }
    return nums;
}
// while
/*
初始化语句;
while (条件判断语句){
	循环体语句;
	条件控制语句;
}
*/
// do while
/* 先执行一次循环题语句
初始化语句;
do {
	循环体语句;
	条件控制语句;
}while(条件判断语句);
*/
2.11 随机数
// Random

import java.util.Random;

public class HelloWorld {
    public static void main(String[] args) {
        Random r = new Random();
        // 取值范围[0, 10)
        int number = r.nextInt(10);
        // 取值范围(0, 1)
        double d = r.nextDouble(1);
        System.out.println(number);
        System.out.println(d);
    }
}
2.12 IDEA中创建项目结构
1 创建一个空项目 file -> new -> project
2 创建一个新模块 project structure -> Modules -> "+" -> new module -> java -> 选SDK -> next
3 在src下创建一个包 package
4 创建java类文件
2.13 数组
// 格式
数据类型[] 变量名
// int[] arr   定义一个int类型数组,数组名为arr
// int arr[]   定义了一个int类型变量,变量名为arr数组


// 数组初始化
// 动态初始化
数据类型[] 变量名 = new 数据类型[数组长度];
// 静态初始化
数据类型[] 变量名 = new 数据类型[]{数据1,数据2, ...};
数据类型[] 变量名 = {数据1,数据2, ...};


// 访问方式
数组名[索引]

for (int i=0; i<arr.length; i++){
	System.out.println(arr[i]);
}

// 获取元素个数
arr.length
2.14 方法
// 定义:不能嵌套定义,void表示无返回,可以省略return,或者return后面无返回值

// 无返回值
public static void 方法名(数据类型 变量名, ...){
    // 方法体
    [return;]
}

// 有返回值
public static 数据类型 方法名(参数){
    return 数据;
}

// 调用
方法名(参数);


// 方法重载:一个类中定义多个同名方法,参数类型或数量不同
package lab;

public class HelloWorld {
    public static void main(String[] args) {
        double x = sum(10.0, 12);
        System.out.println(x);
    }

    public static int sum(int a, int b){
        return a + b;
    }

    public static int sum(int a, int b, int c){
        return a + b;
    }

    public static double sum(double a, double b){
        return a + b;
    }
}

// 可变参数,实参可以是0或任意多个,也可以直接是数组
/*
访问修饰符 返回类型 方法名(数据类型... 形参名){}
*/
public void show(int a, String... args){
    // args当作数组来处理,非可变参数放前面,可变参数放后面,且可变参数最多只能有一个
}
2.15 print
// 输出并换行
System.out.println("内容");

// 输出不换行
System.out.print("内容");
2.16 类定义
// java程序的基本组成单位

public class 类名{
    // 成员变量:堆内存中,有初始默认值
    数据类型 变量名;
    // private权限修饰符,保证不被类外直接访问,可以通过get,set方法来操作
    private int age;  
    public int getAge(){
        return this.age;
    }
    public void setAge(int age){
        // this关键字,修饰age表示成员变量的age
        // 局部变量和同名变量同名时,应使用this
        // java虚拟机会给每个对象分配this,表示当前对象
        this.age = age;
    }
    
    // 成员方法
    public void 方法名(){
        // 局部变量,栈内存,无初始默认值,不能直接使用,且不能加修饰符
    }
    
    // 构造方法:构造方法的调用一般由系统完成,完成对新对象的初始化
    public 类名(){
        this(参数);  // this调用构造方法,只能放在构造方法的第一行
        // 方法名和类名相同
        // 无参构造方法,系统默认自带,一旦给出构造方法,默认的构造方法失效
        // 无返回值,也不能加void
        // 无法主动调用
    }
    
    public 类名(参数){
        // 带参构造方法
    }
}

// 创建
类名 对象名 = new 类名();
2.17 String
// java.lang包下,所有使用时不需要导包
// 字符串底层是byte[]字节数组,双引号。

// 创建
// 空白字符串对象
String s1 = new String();
// 根据字符数组内容来创建
char[] chs = {'a', 'b', 'c'};
String s2 = new String(chs);
// 根据字节数组构建
byte[] bys = {97, 98, 99};
String s3 = new String(bys);
// 直接赋值
String s4 = "abc";

// 字符串是对象,当字符序列相同,JVM只会创建一个String对象,并在字符串池中维护
String s4 = "abc";
String s5 = "abc";
s3 == s4;  // false,地址不同
s4 == s5;  // true,引用类型比较地址值是否相同

// 比较字符串的值是否相同,使用equals()
s3.equals(s4);  // true

// 获取字符串长度,字符串.length()
s3.length();

// 获取指定索引位置的字符,字符串.charAt(index)
s3.charAt(2);

// 遍历字符串
for (int i=0; i<s4.length(); i++){
    System.out.println(s4.charAt(i));
}

// 以aa结尾
s.endsWith("aa");

// 切分,最多分割1次,结构数组长度为2,如果是1则不分割,分割\应当使用\\\\
String[] ss = s.split("a", 2);

// 字符串转字节数组
byte[] bys = s.getBytes();

// 字节数组转字符串
String s = new String(bys);
2.18 StringBuilder
// 可变的字符串容器,拼接字符串时省去了向常量池插入拼接常量的过程
StringBuilder sb = new StringBuilder();

// 添加字符串,并返回对象本身
sb.append("hello");
sb.append("world");
// 链式表示
sb.append("hello").append("world").append("100");

// 反转
sb.reverse();

// 转为String
sb.toString();

// String转StringBuilder
StringBuilder sb = new StringBuilder(s);
2.19 ArrayList<E>
// 可调整大小的数组实现,E泛型
ArrayList<String> arr = new ArrayList<>();

// 添加元素
arr.add("hello");

// 指定位置插入元素,索引不能大于size且不能小于0
arr.add(1, "world");

// 删除指定元素,返回是否删除成功
arr.remove("hello");

// 删除指定位置元素,返回被删除的元素
arr.remove(1);

// 修改指定位置的元素,返回被修改的元素,指定位置不能越界
arr.set(1, "kava");

// 获取指定位置的元素
arr.get(1);

// 返回元素的个数
arr.size();

示例

学生类

public class Student {

	// 按住 alt+insert 快速生成构造方法
    private String name;
    private int age;

    public Student(){}

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

    public String getName(){
        return this.name;
    }
    
    public int getAge(){
        return this.age;
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    public void setAge(int age){
        this.age = age;
    }
}

定义学生的arrayList

import java.util.ArrayList;

public class StudentTest {
    public static void main(String[] args) {
        ArrayList<Student> arr = new ArrayList<>();
        Student s1 = new Student("a", 1);
        Student s2 = new Student("b", 2);
        Student s3 = new Student("c", 3);
        arr.add(s1);
        arr.add(s2);
        arr.add(s3);

        for(int i=0; i<arr.size(); i++){
            System.out.println(arr.get(i).getName() + arr.get(i).getAge());
        }
    }
}
2.20 封装与继承
/*
封装:将属性私有化,不能直接修改,而是通过公共的set、get方法来获取
*/

// 继承使用extends关键字,只支持单继承,不能同时继承多个类,可以多层继承
public class 子类 extends 父类 {
    
}

// 子类中访问一个变量
/* 子类局部范围 --> 子类成员变量范围 --> 父类成员变量范围 */
// 子类无法访问父类的私有属性

// this访问本类的成员变量
this.age;
// super访问父类的成员变量
super.age;

// 子类中所有构造方法默认都会访问父类中无参构造方法,完成父类数据的初始化。
// 如果父类没有无参构造,则需要使用super显示调用父类的构造方法,且必须放在第一行
// 每一个子类构造方法的第一条语句默认都是super(),调用父类无参构造方法
// 对父类带参构造方法显示进行初始化,执行super(参数)
// super()和this()调用只能选择一个,且都只能放在构造函数的第一行

// 方法重写
// 子类的方法的参数和方法名称要与父类完成一致,返回值类型要么相同,要么是父类返回类型的子类
// 子类方法访问权限不能更低(public > 默认 > 私有)
// 父类中私有方法,子类不能重写,不能加@Override

// 当子类对象创建时,先加载父类再加载子类到方法区,构建父类和子类的关系,然后在堆内存中开辟整个对象的内存空间,空间内父类和子类分别占用独立内存块保存各自的属性值,并初始化。

// 方法重载overload,本类,方法名相同,参数不同,返回类型无要求,修饰符无要求
// 方法重写overload,父子类,方法名相同,参数相同,返回类型一致或子类,权限不能缩小
2.21 包
// 包:文件夹,对类进行分类管理
// 包名: 不能数字开头,不能时关键字,建议按如下规则
// com.公司名.项目名.业务模块名
package 包名;
// 自动建包: java -d . HelloWorld.java

// 导包
import.// java.lang.* 默认引入,不需要导包
// java.util.* 系统提供的工具包,工具类
// java.net.*  网络包,网络开发
// jva.awt.*   java界面开发,GUI
2.22 修饰符
// 权限修饰符
/* 同类,同包,子类,不同包
private: 仅同一个类中可以访问
默认:不写时为默认,同一个包中的类可以访问,子类可以访问
protected:同包可以访问,不同包的子类可以访问,不同包的无关类不可以访问
public:都可以访问

同包中对象可以访问默认、protected、public,不能访问private

*/

// 状态修饰符
/*
final: 修饰成员变量表示常量,不能修改,修饰方法表示最终方法,不能重写,类时不能继承
static: 静态,被类的所有对象共享,可直接通过类名来访问,静态成员方法只能访问静态成员
*/
2.23 多态
/*
前提和体现:
	有继承
	有方法重写
	有父类引用指向子类对象: 父类 a = new 子类();
	
访问: 属性没有重写,成员方法才有重写
	成员变量:编译看左边,执行看左边
	成员方法:编译看左边,执行看右边(成员方法有重写)
	
不能使用子类的特有功能
*/


// 多态的转型
/*
向上转型:
	从子到父
	父类引用指向子类对象
	父类 a = new 子类();

向下转型:
	从父到子
	父类引用转向子类对象
	子类 c = (子类)a;
*/

/*
一个对象的编译类型和运行类型可以不一致;编译类型在定义对象时确定,不能改变;运行类型可以变化
父类 a = new 子类(); 编译类型是父类,运行类型是子类
*/
2.24 抽象类abstract
// 一个没有方法体的方法定义为抽象方法,含有抽象方法的类必须定义为抽象类,使用abstract修饰,抽象类可以继承,可以含有成员变量、成员方法、构造方法

// 抽象类无法直接创建对象,须采用多态的方式,重写抽象方法,要有继承
// 抽象类 a = new 子类();

public abstract class Fu{
    public int age;
    public abstract void show();
}
2.25 接口interface
// 公共的规范标准,更多体现在对行为的抽象,只能参照多态的方式进行实例化
// 类实现接口, implements
// 接口 a = new 实现类();
// 接口与接口之间可以多继承,类可以实现多个接口


// 接口,不能有构造方法
public interface Ani {
    // 接口中成员变量默认被static,final修饰
    public int num = 10;
    public abstract void eat();
    
    // 抽象方法
    void show();
    
    // 默认方法
    default void show1(){}
    
    // 静态方法,只能通过接口名来调用-++
    static void show2(){}
    
    // 私有方法,提取接口中相同代码实现
    private void show3(){}
    
    // 私有静态方法
    private static void show4(){}
    
}

// 实现类
public class Cat implements Ani{
    @Override
    public void eat() {
        System.out.println("eat");
    }
}


// 抽象类和接口的区别
/*
抽象类:变量,常量,构造方法,抽象方法,默认方法,静态方法,私有方法。对类进行抽象,包括属性和行为
接口:常量;抽象方法。对行为进行抽象,主要是行为
*/
2.26 内部类
public class 类名{
    修饰符 class 类名{
        // 成员内部类
    }
    
    public void method(){
        // 局部内部类
       class 类名{
            // 外部无法访问
       }
    }
}

// 内部类可以直接访问外部类的成员包括私有,外部类访问内部类成员,必须创建对象


// 匿名内部类
new 类名或接口名(){
    重写方法;
    // 本质是继承了该类或接口的实现类的匿名对象
}
2.27 Math
// 基本的数学函数
// 绝对值
Math.abs(88);

// 大于等于的最小double值等于一个整数
Math.ceil(12.34);   // 13.0

// 较大值
Math.max(1, 2);

// a的b次方
Math.pow(2.0, 3.0);
2.28 System
// 不能被实例化,直接通过类名访问

// 终止java虚拟机
System.exit(0);

// 毫米值
System.currentTimeMillis();
2.29 基本类型包装类
// Integer: 包装原始类型int值
Integer i = Integer.valueOf(100);

// 自动装箱
Integer ii = 100;

// 拆箱
int i2 = ii.intValue();

// 自动拆箱
int i3 = ii + 100;
2.30 int和字符串相互转换
// int和String相互转换

// int转字符串
String s1 = "" + 100;  // 拼接
String s2 = String.valueOf(100);

// String转int
// 方式1,通过Integer
String s = "100";
Integer i = Integer.valueOf(s);
int x = i.intValue();
// 方式2
int y = Integer.parseInt(s);
2.31 日期
// Date
import java.util.Date;

public class StudentTest {
    public static void main(String[] args) {
        Date d1 = new Date();
        System.out.println(d1);
        
        long t = 1000*60*60;
        Date d2 = new Date(t);
        System.out.println(d2);
    }
}

// 获取从1970到现在的毫秒值
Date d = new Date();
d.getTime();


// SimpleDateFormat 日期格式化和解析
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class StudentTest {
    public static void main(String[] args) throws ParseException {
        Date d1 = new Date();
        // 格式化Date -> String
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String s = sdf.format(d1);
        System.out.println(s);
        // 解析 String -> Date
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date dd = sdf2.parse("2022/07/24 14:19:41");
    }
}


// Calendar日历
import java.util.Calendar;

public class StudentTest {
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        // 获取时间
        System.out.println(c.get(Calendar.YEAR));
        // 加减时间
        c.add(Calendar.YEAR, -3);
        System.out.println(c.get(Calendar.YEAR));
        // 设置时间
        c.set(2022, 11,11);
        System.out.println(c.get(Calendar.MONTH));
    }
}
2.32 异常
/*
Throwable:异常的超类
	Error:严重问题不需要处理
	Exception:异常类
		RuntimeException: 编译期间不检查,运行时异常
		编译时异常
		
JVM默认处理方案:抛出异常名称,原因,行数,并停止执行
*/


try {
    // 代码;
} catch(异常类名 e){
    // 异常处理代码;
    // e.printStackTrace();   异常信息最全
    // e.getMessage();   异常原因
    // e.toString();
    
} finally{
    // 一定执行,除非jvm推出
}


// jdk7以后,自动关闭流对象,省略了finally
try(流对象创建语句){
    //
} catch(异常类名 e){
    e.prinStackTrace();
}


// 放在方法的括号后面,仅仅是抛出异常
throws 异常类名;


// 自定义异常
public class 异常类名 extends Exception {
    // 无参构造
    public 类名() {}
    
    // 带参构造
    public 类名(String message){
        super(message);
    }
}

// 自定义异常的使用
// 定义异常
public class ZdyException extends Exception {
    public ZdyException() {
    }

    public ZdyException(String message) {
        super(message);
    }
}

// 使用异常
public class Score {
    // throws用在方法声明后面,异常由调用者处理,表示异常发生的可能性
    public void checkScore(int score) throws ZdyException {
        if (score < 0){
            // throw用在方法体内,抛出异常
            throw new ZdyException("分数不能为负");
        }
    }

}

// 捕获
public class ScTest {
    public static void main(String[] args) {
        Score s = new Score();
        try {
            s.checkScore(-10);
        } catch (ZdyException e) {
            e.printStackTrace();
        }
    }
}
2.33 集合
// Collection单列集合,包含List和Set
Collection<String> c = new ArrayList<String>();
// 添加元素
c.add("hello");
// 移除元素
c.remove("hello");
// 清空
c.clear();
// 判断是否存在指定元素
c.contains("world");
// 判断集合是否为空
c.isEmpty();
// 获取集合元素个数
c.size();

// 返回元素的迭代器Iterator
Iterator<String> it = c.iterator();   
// 迭代器获取元素
it.next();
// 迭代器判断是否还有元素
it.hasNext();

// List集合,可根据索引操作
List<String> list = new ArrayList<String>();
list.add("a");  // 最后插入
list.add(idnex, "a");  // 指定位置插入
list.remove(index);
list.set(index, "b");
list.get(index);

// 列表迭代器,可逆序遍历
ListIterator<String> lit = list.listIterator();
lit.previous();  // 上一个元素
lit.hasPrevious();  // 是否有上一个元素

// 增强for:内部就是一个迭代器
for(元素数据类型 变量名: 数组或Collection集合){
    // 
}

// LinkedList: 链表
LinkedList<String> led = new LinkedList<>();
led.add("a");
// 头部添加
led.addFirst("1");
// 尾部添加
led.addLast("2");
// 获取头部元素
led.getFirst();
// 获取尾部元素
led.getLast();
// 从头部删除
led.removeFirst();
// 从尾部删除
led.removeLast();


// HashSet
Set<String> set = new HashSet<>();
set.add("hello");
// 获取对象的hash值
set.hashCode();  // 通话 重地 

// LinkedHashSet,链表保证元素顺序,HashSet保证不重复

// TreeSet:元素排序,可以指定比较器来排序
public class ScTest {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(10);
        ts.add(40);
        ts.add(20);
        ts.add(10);

        for(Integer i : ts){
            System.out.println(i);
        }
    }

}
2.34 基于比较器的排序

当compare方法返回的值为0时,TreeSet会去重

// Student类实现接口Comparable,实现compareTo方法
public class Student implements Comparable<Student>{
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

    public Student() {
    }

    public void show(){
        System.out.println(this.name + this.age);
    }

    public int compareTo(Student s){
        int num = this.age - s.age;
        // 年龄相同,使用name排序
        return num==0? this.name.compareTo(s.name) : name;
    }

}


// 使用
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();
        Student s1 = new Student("lix", 20);
        ts.add(s1);
        ts.add(new Student("zx", 30));
        ts.add(new Student("xaa", 10));
        ts.add(new Student("za", 30));

        for (Student s : ts){
            s.show();
        }
    }
}
// 使用比较器Comparator,重写compare方法
import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>(
                new Comparator<Student>() {
                    @Override
                    public int compare(Student o1, Student o2) {
                        int num = o1.getAge() - o2.getAge();
                        return num == 0 ? o1.getName().compareTo(o2.getName()) : num;
                    }
                }
        );

        Student s1 = new Student("lix", 20);
        ts.add(s1);
        ts.add(new Student("zx", 30));
        ts.add(new Student("xaa", 10));
        ts.add(new Student("za", 30));

        for (Student s : ts){
            s.show();
        }
    }
}
2.35 泛型
/* 
编译时安全检测机制,本质时参数化类型,所有操作的数据类型被指定为一个参数,将原来具体的类型参数化,调用时传类型

格式:
<类型> 指定一种类型的格式,形参
<类型1,类型2 ...> 指定多种类型的格式,形参
具体使用时传入实参,且只能是引用数据类型

泛型类格式:
修饰符 class 类名<T>{}

泛型方法格式:
修饰符<T> 返回值类型 方法名(T 变量名){}

泛型接口格式:
修饰符 interface 接口名<T> {}

类型通配符: <?>  可以匹配任何类型,各泛型的父类
类型通配符上限: <? extends 类型>,  List<? extends Number>表示的类型是Number或其子类型
类型通配符下限:<? super 类型> ,  List<? super Number>表示的类型是Number或其父类型

可变参数定义格式: 参数个数可变,变量其实是个数组
修饰符 返回值类型 方法名(数据类型... 变量名){}
List.of(1, 2, 3);  相当于元组,无法增删改
*/

// T相当于类型参数
public class Generic<T> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
    
    public <E> void show(E t){
        // 泛型方法
    }
}
2.36 Map
import java.util.*;

public class TreeSetDemo {
    public static void main(String[] args) {
        HashMap<String, Character> hm = new HashMap<>();
        // 对键进行排序
        // TreeMap<String, Character> hm = new TreeMap<>();

        // 添加元素
        hm.put("a", 'a');
        hm.put("b", 'b');

        // 根据键删除元素,键可以不存在
        hm.remove("c");

        // 清空
        // hm.clear();

        // 是否包含指定键
        boolean bc = hm.containsKey("c");
        boolean ba = hm.containsKey("a");
        System.out.println(bc);
        System.out.println(ba);

        // 是否包含指定值
        boolean bvc = hm.containsValue('c');
        boolean bva = hm.containsValue("a");
        System.out.println(bvc);
        System.out.println(bva);

        // 判断是否为空
        System.out.println(hm.isEmpty());

        // 获取元素个数
        System.out.println(hm.size());

        System.out.println(hm);
    }
}
2.37 Collections排序
// Collections.sort排序
import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(4);
        list.add(3);
        list.add(2);
        Collections.sort(list);
        System.out.println(list);
    }
}

// Collections.reverse反转
Collections.reverse(list);

// 随机置换
Collections.shuffle(list);

// 指定比较器来排序Comparator
import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        ArrayList<Student> als = new ArrayList<>();
        als.add(new Student("a", 1));
        als.add(new Student("b", 3));
        als.add(new Student("c", 2));
        Collections.sort(als, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        for (Student st : als) {
            System.out.println(st.getName());
        }
    }
}
2.38 文件创建删除File
import java.io.File;
import java.io.IOException;

public class FileDemo {
    public static void main(String[] args) throws IOException {
        File f1 = new File("C:\\Users\\mian\\Desktop\\a\\a.txt");
        // 创建文件,文件不存在时创建返回true,存在返回false
        System.out.println(f1.createNewFile());

        File f2 = new File("C:\\Users\\mian\\Desktop\\a");
        // 创建目录,目录不存在创建返回true,存在返回false,创建失败也返回false
        System.out.println(f2.mkdir());

        File f3 = new File("C:\\Users\\mian\\Desktop\\a\\c\\d\\e");
        // 创建多级目录,目录不存在创建返回true,存在返回false
        System.out.println(f3.mkdirs());

        File f4 = new File("x.txt");
        // 根据相对路径创建
        System.out.println(f4.createNewFile());

        // 判断是否为目录
        System.out.println(f2.isDirectory());

        // 判断是否为文件
        System.out.println(f2.isFile());

        // 判断路径是否存在
        System.out.println(f2.exists());

        // 获取绝对路径
        System.out.println(f1.getAbsoluteFile());

        // 获取路径字符串
        System.out.println(f1.getPath());

        // 获取文件或目录最后一级名称
        System.out.println(f1.getName());
        System.out.println(f2.getName());

        // 返回目录中的文件和目录的名称字符串数组
        String[] names = f2.list();
        for (String str: names){
            System.out.println(str);
        }

        // 返回目录中文件和目录的File对象
        File[] files = f2.listFiles();
        for (File file: files){
            System.out.println(file);
        }

        // 删除文件或目录,如果目录下有文件,无法直接删除,要先删除文件
        System.out.println(f4.delete());
    }
}
2.39 文件读写

字节流:图片,视频等
字符流: 记事本打开,还能读懂内容

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class FileStream {
    public static void main(String[] args) throws IOException {
        // 写入数据:FileOutputStream
        // 创建文件,创建字节输出流对象,让字节输出流对象指向文件
        // 第二个参数可以指定是否为追加写入
        FileOutputStream fos = new FileOutputStream("a.txt");
        // 写入1个字节数据
        fos.write(97);
        // 写入一个字节数组数据,换行"\n".getBytes()
        byte[] bytes = "aaabbb".getBytes();
        fos.write(bytes);

        // 释放资源
        fos.close();

        FileOutputStream efos = null;
        try {
            efos = new FileOutputStream("aa/A.X");
            efos.write("aaa".getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (efos != null) {
                efos.close();
            }
        }

        // 读取数据: FileInputStream
        FileInputStream fis = new FileInputStream("a.txt");
        // 读取一个字节数据,文件到达末尾,返回-1
        int by = fis.read();  // by为-1表示读取完了
        System.out.println(by);
        // 循环读取
        int byWhile;
        while ((byWhile = fis.read()) != -1) {
            System.out.println(byWhile);
        }

        // 读取一个字节数组长度的数据
        byte[] bys = new byte[5];
        int len = fis.read(bys);
        System.out.println(len);
        System.out.println(new String(bys, 0, len));

        // 循环读取
        byte[] byss = new byte[1024];
        int lens;
        while ((lens = fis.read(bys)) != -1) {
            System.out.println(new String(byss, 0, lens));
        }

        fis.close();
    }
}

字节缓冲流

import java.io.*;
import java.nio.charset.StandardCharsets;

public class CopyPng {
    public static void main(String[] args) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("ab.txt"));
        bos.write("hello".getBytes(StandardCharsets.UTF_8));
        bos.close();

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("ab.txt"));
        byte[] bytes = new byte[1024];
        int len;
        while ((len=bis.read(bytes)) != -1){
            System.out.println(new String(bytes, 0, len));
        }
    }
}

字节流与字符流转换

import java.io.*;

public class CopyPng {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("abc.txt"));
        // 字符流转字节流
        osw.write("中工人");
        // osw.write(97);
        // osw.flush();  //刷新缓冲,close默认刷新
        osw.close();

        
        InputStreamReader isr = new InputStreamReader(new FileInputStream("abc.txt"));
        // 字节流转字符流
        /*
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.println((char) ch);
        }
        */
        char[] chs = new char[1024];   // 使用字符数组
        int len;
        while ((len = isr.read(chs)) != -1) {
            System.out.println(new String(chs, 0, len));
        }
    }
}

简化相当于字节字符流

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileStreamDemo {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("ba.txt");
        FileReader fr = new FileReader("com.pare/src/FileDemo.java");
        int len;
        char[] ch = new char[1024];
        while ((len = fr.read(ch)) != -1) {
            fw.write(ch, 0, len);
        }
        fw.close();
        fr.close();
    }
}

字符字节缓冲流,以及读一行,写换行

import java.io.*;

public class BufferDemo {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("bb.txt"));
        BufferedReader br = new BufferedReader(new FileReader("com.pare/src/CopyPng.java"));
        char[] chs = new char[1024];
        int len;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }
        bw.close();
        br.close();

        BufferedWriter bwn = new BufferedWriter(new FileWriter("bc.txt"));
        for (int i = 0; i < 10; i++) {
            bwn.write("hello" + i);
            // 写入换行符
            bwn.newLine();
            bwn.flush();
        }
        bwn.close();

        BufferedReader brn = new BufferedReader(new FileReader("bc.txt"));
        String s;
        // 读取一行,不会读取换行符,且到达流结尾返回null
        while ((s = brn.readLine()) != null) {
            System.out.println(s);
        }
        brn.close();
    }
}

相当于Scanner

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;

public class SystemDemo {
    public static void main(String[] args) throws IOException {
        // Scanner
        InputStream is = System.in;
        // 字符流转换为字节流
        InputStreamReader isr = new InputStreamReader(is);
        // 缓冲流,可以读一行
        BufferedReader br = new BufferedReader(isr);
        System.out.println(br.readLine());
        int i = Integer.parseInt(br.readLine());
        System.out.println(i);
    }
}

字符打印流

import java.io.*;

public class SystemDemo {
    public static void main(String[] args) throws IOException {
        PrintStream ps = new PrintStream("ps.txt");
        ps.print(97);  // 不会转码,原样写入
        ps.println(98);
        ps.print(99);
        ps.write(98);  // 会转码
        ps.close();
    }
}
2.40 对象序列化与反序列化流
import java.io.*;

public class ObOutStreDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 序列化流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
        // 类需要实现Serializable接口,无需重写方法,应当配置序列化serialVersionUID值
        // private static final long serialVersionUID = 1000;
        // 被transient修饰的成员变量不参与序列化  private transient int age;
        Student s = new Student("名字", 18);
        oos.writeObject(s);
        oos.close();

        // 反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
        Object obj = ois.readObject();
        Student ns = (Student) obj;
        ns.show();
        ois.close();
    }
}
2.41 Properties与IO流结合,集合与文件相互转换
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        // 往集合写入数据
        prop.setProperty("a", "aa");
        prop.setProperty("b", "bb");
        prop.setProperty("c", "cc");
        // 读取数据
        System.out.println(prop.getProperty("a"));

        // 集合数据写入文件 store
        FileWriter fw = new FileWriter("pfw.txt");
        prop.store(fw, null);
        fw.close();

        // 把文件数据加载进集合 load
        Properties pr = new Properties();
        FileReader fr = new FileReader("pfw.txt");
        pr.load(fr);
        fr.close();
        System.out.println(pr);
    }
}
2.42 多线程

Thread

/*
继承Thread类,重写run方法,调用start方法
*/
// 线程类
public class MyThread extends Thread {

    public  MyThread(){}

    public  MyThread(String name){
        super(name);
    }

    public void show() throws InterruptedException {
        for (int i = 0; i < 20; i++) {
            // 获取线程名称 getName
            System.out.println(getName() + ": " + i);
            // sleep
            Thread.sleep(100);
        }
    }

    @Override
    public void run() {
        try {
            show();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


// 执行类
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread("a2");
        // 给线程设置名称
        my1.setName("a1");

        // 抢占式线程,存在优先级
        // 获取线程的优先级,默认是5,范围1~10
        System.out.println(my1.getPriority());
        System.out.println(my2.getPriority());
        // 设置优先级
        my1.setPriority(1);
        my2.setPriority(6);

        // 启动
        my1.start();
        my2.start();

        // join
        try {
            my1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 获取当前线程的线程名称
        System.out.println(Thread.currentThread().getName());

        // 设置守护线程
        my2.setDaemon(true);
    }
}

Runnable接口

// 实现Runnable接口,避免单继承的局限性

// 实现接口
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}


// 使用
public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr, "线程1");
        Thread t2 = new Thread(mr, "线程2");

        t1.start();
        t2.start();
    }
}

线程安全synchronized同步锁

/*
同步代码块,给代码块加锁

同步方法锁:this对象
修饰符 synchronized 返回值类型 方法名(方法参数){}

同步静态方法: 类名.class
修饰符 static synchronized 返回值类型 方法名(方法参数){}
*/


// 锁多条语句操作共享数据
public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            // 加锁
            synchronized (obj) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出票" + tickets);
                    tickets--;
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程安全的类

/*
StringBuffer:StringBuilder
Vector: ArrayList
Hahstable: HashMap

List<String> strings = Collections.synchronizedList(new ArrayList<String>());
*/

Lock锁

// 加锁 lock()
// 释放锁 unlock()

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            // 加锁
            try {
                lock.lock();
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出票" + tickets);
                    tickets--;
                }
            } finally {
                // 释放锁
                lock.unlock();
            }


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

            }
        }
    }
}
2.43 生产者与消费者

wait等待 notifyAll唤醒

Box

public class Box {
    private int milk;
    private boolean state = false;

    public synchronized void put(int milk) {
        if (state){
            try {
                // 等待
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.milk = milk;
        System.out.println("生产了" + milk);
        state = true;
        // 唤醒
        notifyAll();
    }

    
    public synchronized void get() {
        if (!state){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费了" + this.milk);
        state = false;
        notifyAll();
    }
}

Product

public class Product implements Runnable {
    private Box b;

    public Product() {
    }

    public Product(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            this.b.put(i);
        }
    }
}

Customer

public class Customer implements Runnable {
    private Box b;

    public Customer() {
    }

    public Customer(Box b){
        this.b = b;
    }

    @Override
    public void run() {
        while (true){
            this.b.get();
        }
    }
}

test

public class Demo {
    public static void main(String[] args) {
        Box b = new Box();
        Product p = new Product(b);
        Customer c = new Customer(b);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}
2.44 udp

发送数据

/* 无连接 */
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class SendDemo {
    public static void main(String[] args) throws IOException {

        // 创建发送端Socket对象,构造数据报套接字
        DatagramSocket ds = new DatagramSocket();

        // 创建数据并打包
        byte[] bys = "aaa".getBytes(StandardCharsets.UTF_8);
        int length = bys.length;
        InetAddress address = InetAddress.getByName("192.168.1.102");
        int port = 8008;
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);

        // 发送
        ds.send(dp);

        // 关闭
        ds.close();
    }
}

接收数据

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 创建socket
        DatagramSocket ds = new DatagramSocket(8008);

        // 创建一个数据包,接收数据
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys, bys.length);

        // 接收数据
        ds.receive(dp);

        // 解析数据包
        byte[] datas = dp.getData();
        int len = dp.getLength();
        String s = new String(datas, 0, len);
        System.out.println(s);

        ds.close();
    }
}
2.45 TCP

服务端

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SerivceDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务端Socket对象
        ServerSocket ss = new ServerSocket(10000);

        // 监听要连接到此套接字并接收返回一个对应的socket对象
        Socket s = ss.accept();

        // 获取输入流,读数据并显示
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String data = new String(bys, 0, len);
        System.out.println(data);

        s.close();
        ss.close();
    }
}

客户端

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建Socket对象
        Socket s = new Socket("192.168.1.102", 10000);

        // 获取输出流,写数据
        OutputStream outputStream = s.getOutputStream();
        outputStream.write("hello".getBytes(StandardCharsets.UTF_8));

        // 释放
        s.close();
    }
}
2.46 lambda
/*
格式:
() -> {}
形式参数 箭头 代码块

使用前提:
有一个接口,且接口中有且仅有一个抽象方法
多个参数时可以省略类型,1个参数时可以省略小括号,代码块只有一条语句时,可以省略大括号和分号,如果有return还要省略return
x, y -> x+y

注意事项:
接口只能有一个抽象方法
必须有上下文环境,才能推导对应的接口

匿名内部类的区别:
lambda只能作用于1个抽象方法的接口,匿名内部类可以作用于具体类,抽象类,接口
匿名内部类编译后会产生一个单独的class文件,lambda没有,运行时动态生成

方法引用符 ::
	类名::静态方法
	对象::成员方法
	类名::成员方法  第一个参数作为调用者

引用构造器
	类名::new
*/


// 接口
public interface Eat {
    void eat();
}


// 使用lambda
public class EatDemo {
    public static void main(String[] args) {
        doing(() -> {
            System.out.println("aaa");
        });
        
        // 方法引用
        doing(System.out::println);
    }

    public static void doing(Eat e){
        e.eat();
    }
}
2.47 函数式接口
// 接口且有仅有一个抽象方法 
// 注解 @FunctionalInterface


// 函数式接口使用Lambda表达式作为参数传递
public class MyThread {
    public static void main(String[] args) {
        // 相当于实现了Runnable接口并重写了run方法
        startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
    }

    public static void startThread(Runnable r){
        new Thread(r).start();
    }
}


// 函数式接口使用Lambda表达式作为返回值
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class MyComparator {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("1111");
        al.add("22");
        al.add("333");
        Collections.sort(al, getComparator());
        System.out.println(al);
    }

    private static Comparator<String> getComparator(){
        return (s1, s2) -> s1.length() - s2.length();
    }
}


// 常用函数式接口
// Supplier<T>接口:生产型接口,Supplier<T>包含一个无参方法 T get()
import java.util.function.Supplier;

public class SupplierDemo {
    public static void main(String[] args) {
        String s = getString(() -> "名称");
        System.out.println(s);

        int num = getInt(() -> 12);
        System.out.println(num);
    }

    private static String getString(Supplier<String> sup){
        return sup.get();
    }

    private static int getInt(Supplier<Integer> sup){
        return sup.get();
    }
}


// Consumer<T>接口: 消费型接口,accept<T t>给定参数执行此操作,andThen(Consumer after)返回一个组合的Consumer,依次执行此操作,然后执行after操作
import java.util.function.Consumer;

public class MyConsumer {
    public static void main(String[] args) {
        operatorString("呵呵a", System.out::println, s -> System.out.println(new StringBuilder(s).reverse()));
    }

    private static void operatorString(String name, Consumer<String> con, Consumer<String> con2) {
        con.andThen(con2).accept(name);
    }
}


// Predicate<T>:返回条件判断结果
import java.util.function.Predicate;

public class MyPredicate {
    public static void main(String[] args) {
        boolean result = checkString("aaaaa", s -> s.length() > 8, s -> s.length() < 6);
        System.out.println(result);
    }

    private static boolean checkString(String s, Predicate<String> pre, Predicate<String> pre2) {
        return pre.or(pre2).test(s);
    }
}


// Function<T,R>: 参数类型T,结果类型R
import java.util.function.Function;

public class MyFunction {
    public static void main(String[] args) {
        convert("100", s -> Integer.parseInt(s), s -> String.valueOf(s + 100));
    }

    private static void convert(String s, Function<String, Integer> fun, Function<Integer, String> fun2) {
        String ss = fun.andThen(fun2).apply(s);
        System.out.println(ss);
    }
}
2.48 Stream流
/* 
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::println);

中间操作方法:
	filter 过滤
	limit  获取前n个
	skip   跳过前n个
	concat 合并两个流,静态方法
	distinct 去重
	sorted  排序
	map  给定函数应用于此流,并组成结果流
	mapToInt  返回IntStream流,可以继续调用sum()求和
	
终结操作方法:
	forEach  
	count  统计元素数
	
收集操作collect:
	collect(Collectors.toList())   # 转成list
	collect(Collectors.toSet())   # 转成set
*/

import java.util.*;
import java.util.stream.Stream;

public class MyStream {
    public static void main(String[] args) {
        // Collection体系可以直接使用stream生成流
        List<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> setStream = set.stream();

        // Map体系的集合间接的生成流
        Map<String, Integer> map = new HashMap<String, Integer>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

        // 数组可以通过Stream静态方法of生成流
        String[] strArray = {"a", "b", "c"};
        Stream<String> strArrayStream = Stream.of(strArray);
    }
}
2.49 反射
/*
类加载:将class文件读入内存,并创建一个java.lang.Class对象
类的连接:验证,准备,解析。检验加载的类是否有正确的内部结构,类变量分配内存,设置默认初始化值,符号引用替换为直接引用
类的初始化:对类变量进行初始化

反射机制:运行时去获取一个类的变量和方法信息
*/


// 获取Class类对象
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Student> c1 = Student.class;
        Class<Student> c2 = Student.class;
        
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        
        Class<?> c4 = Class.forName("T2.Student");
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c3 == c4);
    }
}


// 反射获取构造方法并创建对象
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class<?> c = Class.forName("T2.Student");
        // 获取公共的构造方法
        Constructor<?>[] cons = c.getConstructors();
        // 获取所有的构造方法
        Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
        // 获取单个构造方法
        Constructor<?> con = c.getConstructor();
        // 通过构造方法创建对象
        Object obj = con.newInstance();
        System.out.println(obj);
        // 私有构造方法获取对象,需要抑制检查
        Constructor<?> con2 = c.getDeclaredConstructor(String.class);
        con2.setAccessible(true);  //暴力反射,抑制检查
        Object hello = con2.newInstance("hello");

        // 获取成员变量,包括私有
        Field[] fields = c.getDeclaredFields();
        // 获取公共成员变量
        // Field[] fields = c.getFields();
        for(Field field: fields){
            System.out.println(field);
        }
        // 获取单个成员变量,可以获取私有成员变量
        Field name = c.getDeclaredField("name");
        Constructor<?> cc = c.getConstructor();
        Object ob = cc.newInstance();
        // 字段取消检查
        name.setAccessible(true);
        // 给对象的name设置为"哈哈"
        name.set(ob, "哈哈");
        System.out.println(ob);

        // 获取本类所有成员方法
        Method[] dms = c.getDeclaredMethods();
        // 获取本来及父类的所有公共方法
        Method[] methods = c.getMethods();
        for(Method dm: methods){
            System.out.println(dm);
        }
        // 获取单个方法,invoke调用方法
        Method gdm = c.getDeclaredMethod("getName");
        Object gname = gdm.invoke(ob);
        System.out.println(gname);
    }
}


// ArrayList<Integer>集合中添加字符串元素
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class IntegerStringList {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<Integer> array = new ArrayList<>();

        Class<? extends ArrayList> aClass = array.getClass();
        Method add = aClass.getMethod("add", Object.class);
        add.invoke(array, "hello");
        add.invoke(array, "world");
        add.invoke(array, 15);
        System.out.println(array);
    }
}
2.50 模块
/*
在模块src下新建文件module-info.java
模块导出格式:exports 包名;
模块依赖格式:requires 模块名;

服务提供:provides 接口名 with 实现类名;
使用接口: user 接口名;
加载服务实现类: ServiceLoader.load(接口.class);
*/
3 maven
3.1 下载、安装与配置
官网下载地址:
	https://maven.apache.org/download.cgi
	https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/
	
windows选择zip包下载并解压,不要选择太高版本,idea容易不支持
依赖java,装jdk,环境变量配置JAVA_HOME
配置MAVEN_HOME,路径为解压路径,path配置为%MAVEN_HOME%\bin

检测配置是否成功
mvn

本地仓库配置:
	选择D盘下创建目录maven/repository
	配置conf中settings.xml文件中的localRepository,路径为上述目录D:\maven\repository
	<localRepository>D:\java_learn\maven_store\repository</localRepository>
	
中心镜像仓库配置:
	配置settings.xml文件中的mirrors
	<mirror>
      <id>mav_aliyun</id>
      <mirrorOf>central</mirrorOf>
      <name>mvn_aliyun</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    </mirror>
3.2 坐标
坐标查询网站:
	https://mvnrepository.com/

<dependency>
    <groupId>组织ID</groupId>
    <artifactId>项目ID</artifactId>
    <version>版本号</version>
</dependency>
3.3 maven构建命令
mvn compile  # 编译
mvn clean    # 清理
mvn test     # 测试
mvn package  # 打包
mvn install  # 安装到本地仓库,依赖其他模块时,其他模块需要先install
3.4 IDEA创建工程
# 创建项目
new project -> file -> project structure -> project -> 选择sdk
# 配置Maven
file -> settings -> 搜索maven -> 选择maven home path 和 user settings file
# 创建maven模块
file -> project structure -> modules -> 点击+号,选new model -> 选maven -> (web时选择webapp) -> 填写名称与坐标
3.5 插件
file -> settings -> plugins -> 搜索maven -> maven helper (右键maven项目,弹出run maven和debug maven)
4 JavaWeb
4.1 JDBC

步骤

1 获取Connection连接
2 定义SQL
3 获取PreparedStatement预编译对象
4 设置参数
5 执行SQL
6 处理结果
7 释放资源

连接数据库,并执行sql语句

import java.sql.*;

public class JdbcDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 注册驱动
        // Class.forName("com.mysql.jdbc.Driver");
        // 获取连接
        String url = "jdbc:mysql://192.168.126.139:3306/db1?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection conn = DriverManager.getConnection(url, username, password);

        // 定义sql语句
        String sql = "update tt set name='aaa' where id=1";
        String sql2 = "create database db2";
        String sql3 = "select * from tt";

        // 获取执行sql的对象
        Statement stmt = conn.createStatement();

        // 执行sql
        try {
            // 开启事务
            conn.setAutoCommit(false);  // 关闭自动提交

            // executeUpdate执行DML、DDL语句,executeQuery执行DQL语句
            int result = stmt.executeUpdate(sql);  // 执行DML语句受影响的行数
            System.out.println(result);

            // int result2 = stmt.executeUpdate(sql2);  // 执行DDL语句,结果可能是0
            // System.out.println(result2);

            ResultSet rs = stmt.executeQuery(sql3);  // 执行DQL语句
            while (rs.next()){  // 光标移动到下一行
                int id = rs.getInt("id");
                // int id = rs.getInt(1);
                String name = rs.getString(2);
                System.out.println(id + name);
            }

            // 提交事务
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            // 回滚事务
            conn.rollback();
        }
        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

防止sql注入

// 预编译sql,性能更高,防止SQL注入,字符转义
import java.sql.*;

public class JdbcDemo2 {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mysql://192.168.126.139:3306/db1?useSSL=false&userServerPrepStmts=true";   // 开启预编译
        String username = "root";
        String password = "1234";

        Connection conn = DriverManager.getConnection(url, username, password);

        String sql = "select * from tt where id = ?";
        // 获取预编译sql对象
        PreparedStatement pstm = conn.prepareStatement(sql);
        // 设置参数值
        int id = 1;
        pstm.setInt(1, id);
        // 执行sql
        ResultSet rs = pstm.executeQuery();
        while (rs.next()) {
            int i = rs.getInt("id");
            String n = rs.getString("name");
            System.out.println(i + n);
        }
        
		rs.close();
        pstm.close();
        conn.close();
    }
}
4.2 Druid连接池

配置

# druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.126.139:3306/db1?useServerPrepStmts=true
username=root
password=1234
# 初始化连接数层
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000

获取连接

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;

public class DruidDemo {
    public static void main(String[] args) throws Exception {
        // 获取当前路径
        System.out.println(System.getProperty("user.dir"));
        // 导入jar包
        // 定义配置文件
        // 加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("java01/src/main/resources/druid.properties"));
        // 获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        // 获取数据库连接
        Connection connection = dataSource.getConnection();

    }
}
4.3 MyBatis

快速入门

# 官网地址:https://mybatis.net.cn/getting-started.html
# 装插件mybaitsx


# 1 pom.xml中配置坐标

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>

<!-- mysql连接-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>



# 2 resources下创建核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--指定一个包名-->
    <typeAliases>
        <package name="com.learn.pojo"/>
    </typeAliases>

    <!--
    environments配置数据库的环境信息,可以配置多个,通过default属性切换不同的environment
    -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.126.139:3306/db1?useServerPrepStmts=true"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载sql的映射文件-->
        <!--<mapper resource="com/learn/mapper/UserMapper.xml"/>-->

        <!--Mapper代理,自动扫描-->
        <package name="com.learn.mapper"/>
    </mappers>
</configuration>


# 3 创建模型类User

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

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

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

    public String getName() {
        return name;
    }

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


# 4 创建sql映射文件UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--
    namespace: 名称空间,接口的全限定名
-->
<mapper namespace="com.learn.mapper.UserMapper">

    <!--type映射方式,支持别名,id完成主键字段映射,result一般字段,完成字段与属性的映射-->
    <resultMap id="userResultMap" type="user">
        <!--数据库字段名和属性名不同-->
        <result column="name" property="userNmae" />
    </resultMap>

    <select id="selectUserAll" resultMap="userResultMap">
        select * from tt
    </select>

    <select id="selectById" resultType="user">
        <!--参数占位符,#防止sql注入,$不能防止sql注入,特殊符号<可以使用转义符或CDATA区(CD)-->
        select * from tt where id=#{id};
    </select>
</mapper>


# 5 查询

import com.learn.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisDemo {
    public static void main(String[] args) throws IOException {
        // 加载mybatis的核心配置文件,获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 执行sql
        List<User> users = sqlSession.selectList("test.selectUserAll");
        System.out.println(users);

        // 释放资源
        sqlSession.close();

    }
}

Mapper代理

/*
1 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下,即在resources中创建同名路径,将映射xml文件放到路径下,注意创建时,原多级目录中的"."需换成"/"才能体现为多级
2 设置sql映射文件的namespace属性为Mapper接口全限定名
3 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并且保存参数类型和返回值类型一致


// 1 定义接口
package com.learn.mapper;

import com.learn.pojo.User;

import java.util.List;

public interface UserMapper {
    // 方法名和映射文件UserMapper.xml中id一致,返回值类型一致
    List<User> selectUserAll();
}


// 2 修改UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    namespace: 名称空间,接口的全限定名
-->
<mapper namespace="com.learn.mapper.UserMapper">
    <select id="selectUserAll" resultType="com.learn.pojo.User">
        select * from tt
    </select>
</mapper>


// 3 UserMapper.xml放在resources的com/learn/mapper/UserMapper.xml路径下


// 4 mybatis-config.xml中加载映射文件的方法更改
<mappers>
     <!--Mapper代理,自动扫描-->
     <package name="com.learn.mapper"/>
</mappers>

*/


import com.learn.mapper.UserMapper;
import com.learn.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisDemo2 {
    public static void main(String[] args) throws IOException {
        // 加载mybatis的核心配置文件,获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 执行sql,使用getMapper,参数为接口.class
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.selectUserAll();
        System.out.println(users);
        
        // 提交事务,openSession默认开启事务
        // sqlSession.commit(); 

        // 释放资源
        sqlSession.close();

    }
}

查询参数与动态sql

/*
1 散装参数:多个参数时,接口中方法中参数定义时需要使用@Param("sql语句中占位符参数名")
	List<User> selectUsers(@Param("id") int id, @Param("name") String name){}
2 对象参数:数据封装成对象,属性名称和参数名称一样
3 Map参数: 键名称和参数名称一样
*/

/*
动态sql:

多条件动态查询
...
<where>
    <if test="id != null">
        id = #{id}
    </if>
    <if test="name != null">
        name = #{name}
    </if>
</where>

单条件动态查询
<where>
    <choose>  
        <when test="id != null">
            id = #{id}
        </when>
        <when test="name != null">
            name = #{name}
        </when>
    </choose>
</where>

*/

添加修改数据,且返回id

// xml配置
<mapper namespace="com.learn.mapper.UserMapper">
    
    <!--返回主键,并设置到对象的属性id上-->
    <insert id="add" useGeneratedKeys="true" keyProperty="id">
        insert into tt(name) values(#{userNmae});
    </insert>
    
    <update id="update">
        update tt set name=#{userNmae} where id=#{id};
    </update>
    
    <delete id="delById">
        delete from tt where id = #{id}
    </delete>
    
    <delete id="delbyIds">
        delete from tt where ids in #{ids}
    </delete>
    
    <!--批量删除-->
    <delete id="delbyIds">
        delete from tt where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

</mapper>


// 接口增加方法
void add(User user);
void update(User user);
void delById(int id);
// 批量删除,使用params改变map集合的默认key,否则xml中collection只能填array
void delbyIds(@Param("ids") int[] ids);  

注解,用于映射简单的sql语句

@Select("sql语句")
接口方法

@Insert
@Update
@Delete

@Select("select * from tt where id=#{id}")
User selectById(Int id);
5 spring

官网spring.io

5.1 IoC与DI
// 控制反转IOC:对象的创建控制权由程序转移到外部(IOC容器),在使用对象时,不要主动使用new来创建,被创建或管理的对象在IOC中统称为Bean。
// 依赖注入DI:在容器中建立bean与bean之间依赖关系的过程
5.2 入门案例

创建maven项目,pom.xml中导入坐标

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
</dependencies>

创建接口与类

// dao.BookDao接口
package com.self.dao;

public interface BookDao {
    public void save();
}


// dao.impl.BookDaoImpl类
package com.self.dao.impl;

import com.self.dao.BookDao;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("bookSave");
    }
}


// dao.service.BookService接口
package com.self.service;

public interface BookService {
    public void save();
}

// dao.service.impl.BookServiceImpl类
package com.self.service.impl;

import com.self.dao.BookDao;
import com.self.service.BookService;

public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    @Override
    public void save() {
        System.out.println("Book Service Save");
        bookDao.save();
    }
    
    /*用于给IoC容器来设置属性*/
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

创建配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <!--定义bean-->
    <bean id="bookDao" class="com.self.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.self.service.impl.BookServiceImpl">
        <!--配置bookService与bookDao的关系-->
        <!--property配置属性, name表示配置哪个属性,ref表示哪个bean-->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

启动main

import com.self.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
5.3 bean配置
<!--
    定义bean:
        id: 不能重复
        name: 定义别名,别名间可以使用空格或逗号隔开
        scope: 作用范围,默认为单例single,非单例prototype
		init-method: 初始化时调用方法
		destroy-method: 销毁时调用方法
		autoWrite: 自动装配属性,要求有set方法,byType按类型自动装配,类型匹配唯一
-->
<bean id="bookDao" name="dao" class="com.self.dao.impl.BookDaoImpl" scope="prototype" init-method="init" autoWrite="byType">
    <!-- 
	setter注入: property,可选依赖使用setter
		name: 属性名称
		value: 非引用类型的值,值类型不用管
		ref: bean类型的值
    -->
    <property name="database" value="mysql"/>
    
    <!--
	构造器注入: constructor-arg,强制依赖使用
		name: 参数名称
		ref: bean类型参数的值
	-->
    <constructor-arg name="bookDao" ref="bookDao"/>
    
    <!-- 注入数组 -->
    <property name="array1">
    	<array>
            <value>100</value>
            <value>200</value>
            
            <!--
			引用类型 <ref bean="beanId"/>
			-->
        </array>
    </property>
    
    <!-- 注入List -->
    <property name="list1">
    	<list>
            <value>a</value>
            <value>b</value>
        </list>
    </property>
    
    <!-- 注入Set -->
    <property name="set1">
    	<set>
            <value>a</value>
            <value>b</value>
        </set>
    </property>
    
    <!-- 注入Map -->
    <property name="map1">
    	<map>
            <entry key="a" value="1"/>
            <entry key="b" value="2"/>
        </map>
    </property>
    
    <!-- 注入Properties -->
    <property name="pro1">
    	<props>
            <prop key="count">1</prop>
            <prop key="age">2</prop>
        </props>
    </property>
</bean>
5.4 加载properties文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--使用context命名空间,加载指定properties文件,加载多个时使用逗号分割,或者使用*.properties加载所有properties-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--使用${}读取加载属性值,properties中定义jdbc.username-->
    <bean name="book" class="com.self.service.impl.BookServiceImpl">
        <property name="usernmae" value="${jdbc.username}"/>
    </bean>
</beans>
5.5 注解开发
// Component组件,相当于bean,括号中可以取名称即id
@Component
@Service  // 业务层,功能和@Component一样
@Repository  // 数据层,功能和@Component一样
@Controller  // 表现层,功能和@Component一样

// 配置类与扫描,加载配置属性
@Configuration
@ComponentScan("com.self")   // 扫描,括号中定义扫描路径,多个时使用数组形式{a, b}
@PropertySource("路径")  // 可以使用数组加载多个,且不支持通配符
public class SpringConfig{
    
}
// 启动函数改成使用读配置类来加载配置
public static void main(String[] args) {
    	// 读配置类
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    }

// 是否单例,默认单例
@Scope   // 可以加括号并指定参数,singleton单例,prototype非单例

// 构造方法后运行
@PostConstruct

// 销毁前运行
@PreDestory

// 自动装配,默认按类型装配,且不需要set方法,暴力反射可以访问私有属性
@Autowired 
// 指定装配名称,且依赖@Autowired
@Qualifier("名称")

// 简单类型注入,${}来指定配置值
@Value("${name}")

// 返回值是一个bean
@Bean

// 导入
@Import({其他类.class})
5.6 第三方bean管理
package com.self.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

public class JdbcConfig {
    // 定义一个方法获取要管理的对象,依赖注入简单类型使用成员变量,引用类型使用方法形参
    @Bean   // 添加@Bean标识当前方法的返回值是一个bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/tb");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

// 在SpringConfig类上加入注解@Import({JdbcConfig.class})导入配置类
5.7 整合mybatis

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.12</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
</dependencies>

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/tbr
jdbc.username=root
jdbc.password=root

config.JdbcConfig

package com.self.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class JdbcConfig {
    @Value("${jdbc.driver")
    private String driver;
    @Value("${jdbc.url")
    private String url;
    @Value("${jdbc.username")
    private String username;
    @Value("${jdbc.password")
    private String password;

    @Bean  // 管理第三方bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

config.MybatisConfig

package com.self.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;
import java.util.Map;

public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.self.domain");  // 扫描类型别名
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.self.dao");  // 扫描映射
        return msc;
    }
}

config.SpringConfig

package com.self.config;

import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.self")
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
public class SpringConfig {

}

domain.Account

package com.self.domain;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

dao.AccountDao接口

package com.self.dao;

import com.self.domain.Account;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface AccountDao {
    @Insert("insert into account(name, age) values(#{name}, #{age})")
    void save(Account account);

    @Select("select * from account")
    List<Account> findAll();

    @Select("select * from account where id=#{id}")
    Account findById(Integer id);
}

service.AccountService接口

package com.self.service;

public interface AccountService {
    public void findById(Integer id);
}

service.impl.AccountServiceImpl

package com.self.service.impl;

import com.self.dao.AccountDao;
import com.self.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl {
    @Autowired
    private AccountDao accountDao;

    public Account findById(Integer id){
        return accountDao.findById(id);
    }
}

App

import com.self.config.SpringConfig;
import com.self.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountService accountService = ctx.getBean(AccountService.class);
        accountService.findById(10);
    }
}
5.8 整合junit

pom.xml增加

test<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

test

package com.self.service;

import com.self.config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)  // 设置专用的类运行器
@ContextConfiguration(classes=SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById(){
        System.out.println(accountService.findById(1));
    }
}
5.9 AOP
// 面向切面编程:在不改动原始设计的基础上进行功能增强

pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

MyAdvice

// 定义通知类
package com.self.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component  
@Aspect  // 做Aop
public class MyAdrvice {
    // 定义切入点
    @Pointcut("execution(List<Account> com.self.dao.AccountDao.findAll())")
    private void pt(){}
	
    // 绑定切入点和通知
    @Before("pt()")
    public void method(JoinPoint jp){
        Object[] args = jp.getArgs();  // 获取参数
        System.out.println(System.currentTimeMillis());
    }
    
    // 环绕
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before");
        Object[] args = pjp.getArgs();  // 获取参数
        Object ret = pjp.proceed();
        System.out.println("after");
        Signature signature = pjp.getSignature();  // 签名信息
        System.out.println(signature.getDeclaringType());  // 类名称
        System.out.println(signature.getName());  // 方法名
        return ret;
    }
}

SpringConfig

package com.self.config;

import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.self")
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableAspectJAutoProxy  // 使用注解开发Aop
public class SpringConfig {

}
5.10 事务
// 1 开启事务,一般不会写在业务层的实现类上,而是写在业务层接口上
@Transactional    

// 2 设置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource ds){
    DataSourceTransactionManager ptm = new DataSourceTransactionManager();
    ptm.setDataSource(ds);
    return ptm;
}

// 3 开启注解式事务驱动 
@EnableTransactionManagement   // 加在SpringConfig类上
6 SpringMVC

基于MVC模型的轻量级Web框架

6.1 入门案例

pom.xml

<groupId>org.example</groupId>
<artifactId>rumen2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.1</version>
            <configuration>
                <port>80</port>
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

controller.UserController

package com.self.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller  // 表现层定义bean
@RequestMapping("/user")  // 请求前缀
public class UserController {
    @RequestMapping("/save")   // 绑定访问路径
    @ResponseBody   // 设置当前返回值类型
    public String save(){
        System.out.println("user save");
        return "{'info': 'user'}";
    }
}

config.SpringMvcConfig

package com.self.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.self.controller")
public class SpringMvcConfig {
}

config.InitConfig

package com.self.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

// 定义一个servlet容器启动的配置类,加载spring配置
public class InitConfig extends AbstractDispatcherServletInitializer {

    // 加载springMvc容器配置
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringConfig.class);
        return ctx;
    }

    // 设置哪些请求归属springMvc处理
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // 加载spring容器配置
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}

运行: 右上角配置 – Edit – 选择maven – Run配置tomcat7:run

config.SpringConfig

package com.self.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(value = "com.self",
        excludeFilters = @ComponentScan.Filter(   // 排除Controller注解的bean
                type = FilterType.ANNOTATION,
                classes = Controller.class
        ))
public class SpringConfig {
}

InitConfig

package com.self.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

// 定义一个servlet容器启动的配置类,加载spring配置
public class InitConfig extends AbstractDispatcherServletInitializer {

    // 加载springMvc容器配置
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }

    // 设置哪些请求归属springMvc处理
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // 加载spring容器配置
    protected WebApplicationContext createRootApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringConfig.class);
        return ctx;
    }
}
6.2 请求
/*
@RequestMapping(value="路径/{参数}", method=RequestMethod.PUT)  
加在类上表示请求前缀,加在方法上表示具体路径,不区分post还是get

普通参数传递,直接使用形参接收

@RequestParam("前端参数名") 类型 后端参数名
前后参数不一致时,绑定参数关系

形参为实体类时,请求参数名与对象属性名相同,可接收参数

集合对象做形参时,前面需要加上@RequestParam

json数据: @RequestBody
@EnableWebMvc  配置类注解

@DateTimeFormat(pattern="yyyy/MM/dd")
日期格式

@PathVariable 参数来自路径

@ResuController 包含 @Controller和@ResponseBody等

@PostMapping
@DeleteMapping("/{id}")
@PutMapping
@GetMapping("/{id}")
*/

json坐标

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
6.3 响应
/*
@ResponseBody  
pojo对象转json
*/

public class Result{
    private Object data;
    private Integer code;
    private String msg;
}
7 SpringBoot
7.1 入门案例
/*
社区版idea安装插件spring boot相关插件,选择new project创建新的项目,选择spring initializr,next后选择web选择spring web,创建项目后仅保留pom.xml和src文件,右键pom.xml文件,改为maven项目,然后编写controller表现层代码,直接运行Application进行启动

打包:点击maven中clean,然后点击package打包
运行:在打包文件目录执行: java -jar 打包好的jar文件

配置更改:
application.properties文件中更改server.port,还支持application.yml,application.yaml配置文件

yaml配置数据读取:
	// 读取一个数据
	@Value(${一级属性名.二级属性名...})
	// 使用环境变量bean读取全部数据
	@Autowired
	private Environment env;
	// 使用自定义对象封装数据
	@Component
	@ConfigurationProperties(prefix="属性前缀")
    public class 类名{
    	private 类型 属性名;
    }
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值