Java

进制的转化

// 二进制转成十进制
		// 规则: 从最低位(右边)开始,将每个位上的数提取出来,乘以2的(位数-1)次方然后求和

		// 案例 0b1011 转成十进制

		// 0b1011 = 1*2^0 + 1*2^1 + 0*2^2 + 1*2^3 = 11

		// 八进制转成十进制
        
        // 规则: 从最低位(右边)开始,将每个位上的数提取出来,乘以8的(位数-1)次方然后求和

        // 案例 0234 转成十进制

        // 4*8^0 + 3*8^1 + 2*8^2 + 0*8^3 = 156

        // 规则: 从最低位(右边)开始,将每个位上的数提取出来,乘以16的(位数-1)次方然后求和

        // 案例 0x23A 转成十进制 

        // 10*16^0 + 3*16^1 + 2*16^2 = 570

        // 规则: 将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制

        // 案例: 将34 转成二进制 0B00100010

        // 十进制转八进制

        // 案例 将 131 转成八进制 0203

        // 十进制转十六进制

        // 案例: 将237转成16进制  0xED


        // 二进制转换成八进制 

        // 规则: 从最低位开始将二进制数每三位一组,转成对应的八进制数即可

        // 案例: 请将0b11010101 转成八进制   0325


        // 二进制转成十六进制

        // 规则: 从最低位开始将二进制数每四位一组,转成对应的十六进制数即可

        // 案例: 请将0b11010101 转成十六进制 0xD5



        // 八进制转成二进制
        // 规则 : 将八进制数每一位,转成对应的一个3位数的二进制数即可

        // 案例: 将0237转成二进制    0b01001111

        // 十六进制转成二进制
        // 规则 : 将八进制数每一位,转成对应的一个4位数的二进制数即可

        // 案例: 将ox23B转成二进制    0b001000111

面向对象编程(初级)

冒泡排序

public class maopao {

	public static void main(String[] args) {

		// 从小到大

        int[] arry = {-1, 20, 90, 800, 300,20};
		int temp = 0;  // 用做中间变量
		for (int i = 0;i < arry.length - 1;i++) {
			for (int j = 0; j< arry.length - 1 - i; j++) {
				if (arry[j] >= arry[j+1]) {
					temp = arry[j];
					arry[j] = arry[j+1];
					arry[j+1] = temp;
				}
			}
		}
        for (int i = 0; i<arry.length;i++) {
			System.out.println(arry[i]);
		}
	}
}

递归

栈:后进先出(谁调用,就返回谁,一层一层返回,类似于堆木桶,最上面的木桶最后放的,最先拿下来)

// 阶乘
	public int factorial(int n) {
		if (n == 1) {
			return 1;
		}return factorial(n - 1)*n;
}

// 外面的调用
test f = new test();
f.factorial(5);  // 返回值为120
// 请使用递归的方式求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出它的值是多少
public int fibonacci(int n) {
	if(n >=1) {
		if(n==1||n==2) {
			return 1;
		} else {
			return fibonacci(n-1) + fibonacci(n - 2);
		}
	}else {
		System.out.println("你输入的数必须大于或等于1");
         return -1;  //等于是返回一个无效值
	}
}


//外层调用
T t = new T();
int res3 = t.fibonacci(8);
System.out.println(res3);     // 输出 21

29405856a2a65573fdb7b60af3aa53a.png

	// 猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
	// 以后每天猴子都吃其中的一半,然后再多吃一个。
	// 当到第10天时,想再吃时(即还没吃)发现只有1个桃子了。问题:最初共多少个桃子?

	// 规律 前一天的桃子 = (后一天的桃子 +1 ) *2
	public int MonkeyEatPeach(int days) {
		if ( days==10 ) {  //第十天只有一个桃子
			return 1;
		}else if ( days>=1 && days <=9 ) {
			return (MonkeyEatPeach(days + 1) + 1)*2;
		}else {
			System.out.println("day只能在第一到第十天");
			return -1;
		}
	}
}

// 返回值 1534
递归调用应用实例-迷宫问题
// 1.小球得到的路径,和程序员设置的找路策略有关即:找路的上下左右的顺序相关
// 2.再得到小球路径时,可以先使用(下右上左),再改成(上右下左),看看路径是不是有变化
// 3.测试回溯现象!
// 4.扩展思考: 如何求出最短路径?

public class Migong {

    public static void main(String[] args) {

        // <======================== 地图==========================>
        // 思路
        // 1.先创建迷宫,用二维数组表示 int [][] map = new int [8][7];
        // 2. 先规定 map 数组的元素值:0 表示可以走 1 :表示障碍物

        int[][] map = new int [8][7];
        // 3.将上下两行全部设置为1
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        // 将最右面的一列和最左边的一列,全部设置为1
        for (int i = 0; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        System.out.println("============当前地图=====================");
        for (int i = 0; i < map.length;i++) {
            for (int j = 0;j < map[i].length;j++) {
                System.out.print(map[i][j]+"\t");    // 输出一行
            }
            System.out.println();     // 换行
        }
        T t1 = new T();
        t1.findWay(map,1, 1);
        System.out.println("\n============找路情况如下================");
        for (int i = 0; i < map.length;i++) {
            for (int j = 0;j < map[i].length;j++) {
                System.out.print(map[i][j]+"\t");    // 输出一行
            }
            System.out.println();     // 换行
        }
    }
}


class T {

    // 1.findway方法就是专门用来找出迷宫的路径
    // 2.如果找到,就返回true,否则返回false
    // 3.map 就是二维数组,即表示迷宫
    // 4. i,j 就是老鼠的位置,初始化的位置为(1,10
    // 5.因为是递归找路,所以我们先规定 map 数组的各个值的含义
    //  0 表示可以走, 1 表示障碍物,2 表示可以走, 3表示走过,但是走不通
    // 6.当map[6][5]等于2 就说明找到通路,就可以结束,否则继续找
    // 7. 先定义老鼠找路的策略 下->右->上->左
    public boolean findWay(int[][]map, int i, int j) {
        if(map[6][5] ==2 ) {
            return true;
        }else {
            if(map[i][j] == 0) { // 当前这个位置0,说明表示可以走
                // 假定走通
                map[i][j] = 2;
                // 使用找路策略,来确定该位置是否真的可以走通
                // 下->右->上->左
                if (findWay(map,i+1, j)) {  // 向下
                    return true;
                }else if (findWay(map,i,j + 1)) {   // 向右
                    return true;
                }else if (findWay(map,i - 1, j)) {   // 向上
                    return true;
                }else if (findWay(map,i, j-1)) {    // 向左
                    return true;
                }else {
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }
        }
    }
}

// 运行结果如下:
=============当前地图===================
1	1	1	1	1	1	1	
1	0	0	0	0	0	1	
1	0	0	0	0	0	1	
1	1	1	0	0	0	1	
1	0	0	0	0	0	1	
1	0	0	0	0	0	1	
1	0	0	0	0	0	1	
1	1	1	1	1	1	1	

============找路情况如下================
1	1	1	1	1	1	1	
1	2	0	0	0	0	1	
1	2	2	2	0	0	1	
1	1	1	2	0	0	1	
1	0	0	2	0	0	1	
1	0	0	2	0	0	1	
1	0	0	2	2	2	1	
1	1	1	1	1	1	1	
//递归调用应用实例-汉诺塔
//汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智
//玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从
//下往上按照大小顺序摞着64片圆盘。大梵天命令婆罗门把圆盘从下面
// 开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不
//能放大圆盘,在三根柱子之间一次只能移动一个圆盘。


//假如每秒钟移动一次,共需多长时间呢?移完这些金片需要5845.54
//亿年以上,太阳系的预期寿命据说也就是数百亿年。真的过了
//5845.54亿年,地球上的一切生命,连同塔、庙宇等,都早已经灰
//飞烟灭


public class HanoiTower {
    public static void main(String[] args) {
        Tower t3 = new Tower();
        t3.move(5,'A','B','C');
    }
}
class Tower {
    // 方法
    // num 表示要移动的个数 a, b, c 表示三个塔
    public void move(int num, char a, char b, char c) {
        if(num == 1 ) {
            System.out.println(a + "->" + c);
        } else {
            // 如果有多个盘, 可以看成两个, 最下面的和上面的所有盘
            // 1.先移动上面的盘到 b, 借助 c
            move(num - 1, a, c, b);
            // 2.把最下面的盘,移动到c
            System.out.println(a + "->" + c);
            // 3.再把b塔的所有盘移动到c,借助a
            move(num - 1,b, a, c);

        }
    }
}

可变参数

public int Sum(int... nums) {       // ... 表示可接收的参数  可变参数的本质就是一个数组  和其他类型的参数放在一起时,可变参数一定要放在最后 可变参数只能有一个
  int res = 0;
  for (int i = 0;i < nums.length;i++) {
     .... // 可接收代码
  }
}

构造器

public class Constructor {
    public static void main(String[] args) {
        // 当我们new一个对象时, 直接通过构造器指定
        person p = new person(); // 这样就是使用默认的无参构造器; 在外面有了构造器的情况下,外面不能在用这样的方法去创建对象
        // 还想用的画,就要再显示创建一下这个构造器  Person() {}
        Person p1 = new Person("smith", 80);
        Person p2 = new Person("kenneth");
    }
}
// 在创建人类对象的时候,就直接指定这个对象的年龄和姓名

class Person {
    String name;
    int age;
    // 构造器
    // 没有返回值,不能写void
    // 构造器的名称和类名Person一样
    // (String pName, int pAge) 是构造器形参列表, 规则和成员方法一样

    // 第一个构造器
    public Person(String pName, int pAgae) {
        System.out.println("构造器被调用~~完成对象属性的初始化");
        name = pName;
        age = pAgae;

    }

    // 第二个构造器     也就是构造器的重载  这个只传入一个参数
    public Person(String pName) {
        name = pName;
    }
}
// 构造器细节
// 1.构造器的名字和类名要一样
// 2.构造器没有返回值
// 3.构造器是完成对象的初始化,并不是创建对象
// 4.在创建对象时,系统自动的调用该类的构造方法
// 5.当我们没有创建构造器的时候,系统会默认生成一个无参构造器   Person() {}

对象创建的流程分析

image-ydfy.pngthis在内存中的分布

image-sqhx.pngthis的分析

image-wyou.png

类与对象的随手练习



public class Homework {
    public static void main(String[] args) {
        int[] oldArr = {10, 20, 50};

         A03 A = new A03();

         int[] newArr = A.copyArr(oldArr);
       for (int i = 0; i < newArr.length; i++) {
           System.out.print(newArr[i] + "\t");
       }
    }
}



// 编写类A03 实现数组的复制功能copyArr, 输入旧数组,返回一个新的数组,元素和旧数组一样
class A03 {
    public int[] copyArr(int[] oldArr) {
        int[] newArr = new int[oldArr.length];

        for (int i = 0; i < oldArr.length; i++) {
            newArr[i] = oldArr[i];
        }
        return newArr;
    }
}


// 注意 Double 这个是一个包装类,允许返回空值

注意 Double 这个是一个包装类,允许返回空值!!!

匿名对象

 public static void main(String[] args) {

     new Test().count1();       // 这个是一个匿名对象,也就是说创建好匿名对象后,就调用用count1()方法,但是只能调用一次,调用完就销毁   有利于释放内存
     
}

一个相对综合的类与对象的应用

public class Homework {
    public static void main(String[] args) {
         
         new PassObject().printArea(new Circle(),5);

    }
}

// 定义一个 Circle 类,包含一个 double 型的radius 属性代表圆的半径,findArea()方法返回圆的面积
// 定义一个类PassObject, 在类中定义一个方法printArea(), 该方法的定义如下: public void printAreas(Circle c, int times)
// 在printAreas 方法中打印输出1到times之间的每个整数半径值,以及对应的面积。例如,times为5, 则输出半径1, 2, 3, 4, 5, 以及对应的圆面积
// 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。

class Circle {
    double radius;
    public double findArea() {  // 返回圆面积
        return Math.PI * radius * radius;
    }
    // 添加方法setRadius, 修改对象的半径值

    public void setRadius(double radius) {
        this.radius = radius;
    }
}

class PassObject {
    public void printArea(Circle c, int times) {
        for (int i = 1; i <= times; i++) {
            c.setRadius(i);   // 修改半径radius 的值
            System.out.println(i + "\t" +  c.findArea());
        }
    }
}

面向对象编程(中级)

键位

idea 快捷键 alt + enter (自动导入类)

ctrl + alt + l 快速格式化代码

shirt + F10 快速运行程序

alt + insert 生成构造器

查看一个类的层级关系 ctrl + H

ctrl + B 可以定位到该类的位置

自动分配变量名 通过在后面.var

模板 (设置中可以添加!)

sout 快速输出

fori 快速生成for 循环模板

mian 快速生成 main

包的本质就是创建不同的文件夹来保存类文件 示意图如下:

image-stft.png包的命名

命名规则:只能包含数字、字母、、下划线、小圆点.,但不能用数字开头,不能是关键字或保留字demo.class.exec1 //错误 class是关键字

demo.12a //错误:12a 是数字开头

demo.ab12.oa // 对

命名规范

一般是小写字母+小圆点一般是com.公司名.项目名.业务模块名比如:com.hspedu.oa.model; com.hspedu.oa.controller,

举例:

com.sina.crm.user //用户模块

com.sina.crm.order//订单模块

com.sina.crm.utils //工具类

常用的包

一个包下,包含很多的类,java中常用的包有:

.java.lang //lang包是基本包,默认引入,不需要再引入

.java.util.* //uti 包,系统提供的工具包,工具类 使用 Scanner

java.net.* //网络包 ,网络开发

java.awt.* //是做java的界面开发,GUI

注意事项和使用细节

1.package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package

2.import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。

访问修饰符

● 基本介绍

java提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围)

1、公开级别:用public 修饰,对外公开

2.受保护级别:用protected修饰,对子类和同一个包中的类公开

3.默认级别:没有修饰符号,向同一个包的类公开.

4.私有级别:用private修饰,只有类本身可以访问,不对外公开

image-elkv.png以上的方法和属性都适用

面向对象编程-封装

封装的实现步骤

1)将属性进行私有化private【不能直接修改属性)

2)提供一个公共的(public)set方法,用于对属性判断并赋值

public void setXxx(类型 参数名){

//加入数据验证的业务逻辑属性 = 参数名;

3)提供一个公共的get方法,用于获取属性的值public xX getXxx(){//杈限判断return xx;

面对对象封装

com.hspedu.encap:AccountTest.javaAccount.java

创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性

1.Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)密码(必须是六位),如果不满足,则给出提示信息,并给默认值

2.通过setXxx的方法给Account 的属性赋值

3.在AccountTest中测试

提示知识点:String name=""int len = name.length();

// 实现一个简单的表单校验                Account.java
package com.zfc.encap;

public class Account {
    // 为了封装,将属性修改为private
    private String name;
    private double balance;
    private String pwd;

    // 提供两个构造器

    public Account() {
        
    }

    public Account(String name, double balance, String pwd) {
        this.setBalance(name);
        this.setBalance(balance);
        this.setBalance(pwd);
    }

    public String getName() {
        return name;
    }

    // 姓名 (长度为2, 3, 4位)
    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 4) {
            this.name = name;
        } else {
            System.out.println("姓名要求长度为2位3位或4位");
            this.name = "zfc";
        }

    }

    public double getBalance() {
        return balance;
    }
    // 余额必须大于或等于20
    public void setBalance(double balance) {
        if (balance > 20) {
            this.balance = balance;
        } else {
            System.out.println("你输入的余额有误,请重新输入");
        }

    }

    public String getPwd() {
        return pwd;
    }
    // 密码必须是6位
    public void setPwd(String pwd) {
        if (pwd.length() == 6) {
            this.pwd = pwd;
        } else {
            System.out.println("密码必须是6位");
            this.pwd = "000000";
        }
    }
}

=====================================文件分割======================================
                                TestAccount.java
package com.zfc.encap;

public class Account {
    // 为了封装,将属性修改为private
    private String name;
    private double balance;
    private String pwd;

    // 提供两个构造器

    public Account() {
        
    }

    public Account(String name, double balance, String pwd) {
        this.setBalance(name);
        this.setBalance(balance);
        this.setBalance(pwd);
    }

    public String getName() {
        return name;
    }

    // 姓名 (长度为2, 3, 4位)
    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 4) {
            this.name = name;
        } else {
            System.out.println("姓名要求长度为2位3位或4位");
            this.name = "zfc";
        }

    }

    public double getBalance() {
        return balance;
    }
    // 余额必须大于或等于20
    public void setBalance(double balance) {
        if (balance > 20) {
            this.balance = balance;
        } else {
            System.out.println("你输入的余额有误,请重新输入");
        }

    }

    public String getPwd() {
        return pwd;
    }
    // 密码必须是6位
    public void setPwd(String pwd) {
        if (pwd.length() == 6) {
            this.pwd = pwd;
        } else {
            System.out.println("密码必须是6位");
            this.pwd = "000000";
        }
    }
}

继承

面向对象编程-继承

继承基本介绍和示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

image-udue.png

继承的基本语法

class 子类 extends 父类{

1)子类就会自动拥有父类定义的属性和方法

2)父类又叫 超类,基类。

3)子类又叫派生类。

以下是简单的类的继承用法

                               这个是公共类,也就是父类 Student
// 父类,是Pupil 和 Graduate 的父类
public class Student {
    // 共有属性
    public String name;
    public int age;
    private double score;
    //共有的方法
    public void setScore(double score) {
        this.score = score;
    }
    public void showInfo() {
        System.out.println("学生名字" + name + "年龄" + age + "成绩" + score);
    }
}
=====================================子类(大学生)===========================================
package com.zfc.extend_.improve_;

public class Graduate extends Student{
    public void testing() {
        System.out.println("大学生" + name +  "正在考大学数学");
    }
}

=====================================子类(小学生)===========================================
// 让Pupil 继承 student 类
public class Pupil extends Student{
    public void testing() {
        System.out.println("学生" + name +"正在考小学数学....");
    }
}

继承细节

1.子类继承了所有属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问, 要通过公共类的方法去访问

2.子类必须调用父类的构造器,完成父类的初始化

3.当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过[举例说明]

4.如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)

5.super在使用时,需要放在构造器第一行(super 只能在构造器中使用)

6.super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

7.子类最多只能继承一个父类(指直接继承), 即java是单继承机制。

8.不能滥用继承, 子类和父类之间必须满足 is-a 的逻辑关系

image-apoc.png

简单的类继承

======================================父类===================================================

public class computer {
   private String cpu;
   private int memory;
   private int disk;
    public computer(String cpu, int memory, int disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }
    //返回computer 全部的详细信息
    public String showInfo() {
        return ("=======电脑信息========\n"+ "cpu:" + "\t" +"内存:" + "\t" + "硬盘:\n" + cpu + "\t" + memory + "\t" + disk);
    }


    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getMemory() {
        return memory;
    }

    public void setMemory(int memory) {
        this.memory = memory;
    }

    public int getDisk() {
        return disk;
    }

    public void setDisk(int disk) {
        this.disk = disk;
    }
}
==========================================子类===============================================

public class PC extends computer {
    private String brank;

    public PC(String cpu, int memory, int disk, String brank) {
        super(cpu, memory, disk);
        this.brank = brank;
    }

    public String getBrank() {
        return brank;
    }

    public void setBrank(String brank) {
        this.brank = brank;
    }

    public void printInfo() {
        System.out.println(showInfo() + " \t" + "brand\n" + brank);
    }
}
==========================================main===============================================
//面向对象编程-继承
//案例3 ExtendsExercise.java编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
//编写PC子类,继承Computer类,添加特有属性【品牌brand】
//编写NotePad子类,继承Computer类,添加特有属性【color)
//编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息。
public class ExtendsExercise {
    public static void main(String[] args) {

        new PC("inter",16, 500, "lennob").printInfo();
    }

}

super()关键字

基本介绍super代表父类的引用,用于访问父类的属性、方法、构造器

● 基本语法

1、访问父类的属性,但不能访问父类的private属性[案例]super.属性名

2.访问父类的方法,不能访问父类的private方法super.方法名(参数列表)

3.访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句!

4.调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子1.类初始化)

5.当子类中有和父类中的成员“(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果

super 和 this 比较

image-gdrp.png

方法重写(override)

注意事项和使用细节

1.方法重写也叫方法覆盖,需要满足下面的条件

2.子类的方法的参数,方法名称,要和父类方法的参数,方法名称完金一样。子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类比如 父类 返回类型是 Object,子类方法返回类型是String

3.子类方法不能缩小父类方法的访问权限

方法重写和重载的比较

image-club.png

多态

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的

  1. 方法的多态(重写和重载就体现多态)

2.对象的多态:

1) 一个对象的 编译类型和运行类型可以不一致

2) 编译类型在定义对象时,就确定了,不能改变

3) 运行类型时可以变化的

4) 编译类型看定义时 = 号的左边, 运行类型看 = 号 右边

image-pvum.png

多态注意事项和细节讨论:

com.hspedu.poly .detail 包:PolyDetail.j多态的前提是:两个对象(类)存在继承关系多态的向上转型

1)本质:父类的引用指向了子类的对象

2)语法:父类类型引用名 = new 子类类型(),

3)特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限)不能调用子类中特有成员:最终运行效果看子类的具体实现!

多态的向上转型的规则:

//语法:父类类型引用名 = new 子类类型(),

//可以调用父类中的所有成员(需遵守访问权限)

//但是不能调用子类的特有的成员

//因为在编译阶段,能调用哪些成员,是由编译类型来决定的

//animal.catchMouse():错误

//最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法

//,然后调用,规则我前面我们讲的方法调用规则一致。

多态的向下转型:

1)语法:子类类型引用名= (子类类型)父类引用;

2)只能强转父类的引用,不能强转父类的对象

3)要求父类的引用必须指向的是当前目标类型的对象

4)当向下转型后可以调用子类类型中所有的成员

动态绑定机制(非常非常重要)

image-nzpk.pngimage-sagx.png

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

应用实例2:com.hspedu.poly .polyparameter 包PloyParameter.java定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法 [e.getAnnual()]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

==================================Employee===================================
public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    // 得到年工资的方法
    public double getAnnual() {
        return 12 * salary;
    }

===================================Manage====================================

public class Manager extends Employee{
    private  double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public void manage() {
        System.out.println("经理" + "\t"+ getName() + "is managing");
    }

    // 重写获取年薪的方法

    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

=================================Work=======================================
public class Work extends Employee{
    public Work(String name, double salary) {
        super(name, salary);
    }
    public void work() {
        System.out.println("普通员工" + "\t" + getName() + "is working");
    }
    @Override
    public double getSalary() {
        return super.getSalary();
    }
}

==================================Test=====================================
public class TestPloy {
    public static void main(String[] args) {
        Work tom = new Work("tom", 2200);
        Manager zfc = new Manager("zfc", 5000, 200000.0);
        TestPloy testPloy = new TestPloy();
        testPloy.showEmpAnnal(tom);
        testPloy.showEmpAnnal(zfc);
        testPloy.testWork(tom);
        testPloy.testWork(zfc);
    }
    // showEmpAnnal(Employee e)

    public void showEmpAnnal(Employee e) {
        System.out.println(e.getAnnual());  // 动态绑定
    }

    public void testWork(Employee e) {
        if (e instanceof Work) {
            ((Work) e).work();
        } else if(e instanceof Manager) {
            ((Manager) e).manage();
        }

    }
}

Object 类详解

hashCode()方法

1)提高具有哈希结构的容器的效率!

2)两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

3)两个引用,如果指向的是不同对象,则哈希值是不一样的

4)哈希值主要根据地址号来的!,不能完全将哈希值等价于地址,

5)案例演示[HashCode .java]: obj.hashCode()[测试:A obj1 = new A(); A obj2 = new AO); Aobi3 = obj1]

6)后面在集合,中hashcode 如果需要的话,也会重写

toString()方法

基本介绍

默认返回:全类名+@+哈希值的十六进制,【查看Object 的 toString() 方法】

子类往往重写 toString() 方法,用于返回对象的属性信息

重写 toString() 方法,打印对象或拼接对象时,都会自动调用该对象的 toString() 形式案例演示:Monster [name,job,sal]案例

当直接输出一个对象时,toString 方法会被默认的调用

finalize() 方法

1.当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法做一些释放资源的操作

2.什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。

3.垃圾回收机制的调用,是由系统来决定,也可以通过 System.gc() 主动触发垃圾回收机制,测试:Car [name]

在实际开发中几乎不会用

断点调试

1.在开发中,新手程序员在查找错误时,这时老程序员就会温馨提示,可以用断点调试-步一步的看源码执行的过程,从而发现错误所在。

2.重要提示: 在断点调试 过程中,是运行状态,是以对象的 运行类型来执行的

断点调试介绍

断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug断点调试是程序员必须掌握的技能。3.断点调试也能帮助我们查看iava底层源代码的执行过程,提高程序员的Java水平

快捷键

F7(跳入) F8(跳过) shirt+F8 (跳出) F9(resume,执行到下一个断点)

F7: 跳入方法内

F8: 逐行执行代码

shirt+F8 : 跳出方法

零钱通项目

package com.zfc.smallchange.oop;

// 该类是完成零钱通各个功能的类  将各个功能对应一个方法

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangeSystemOOP {
    // 属性...
    boolean loop  =true;

    Scanner scanner = new Scanner(System.in);
    String key = "";

    String detail = "=================零钱通明细===================";

    double money = 0;
    double blance = 0;

    Date date = null;
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    String note = "";

    // 先完成显示菜单,并可以选择
    public void mainMenu() {
        do{

            System.out.println("===================零钱通菜单====================");
            System.out.println("\t\t\t1. 零钱通明细");
            System.out.println("\t\t\t2. 收益入账");
            System.out.println("\t\t\t3. 消费情况");
            System.out.println("\t\t\t4. 退   出");
            System.out.println("================================================");
            System.out.println("请选择(1-4):");
            key = scanner.next();
            // 使用  switch 分支
            switch (key) {
                case "1":
                    this.detail();
                    break;
                case "2":
                    this.income();
                    break;
                case "3":
                    this.pay();
                    // 范围校验
                    break;
                case "4":
                    this.exit();
                    break;
                default:
                    System.out.println("输入有误,请重新选择");
            }
        }while (loop);
    }
    // 零钱通明细
    public void detail() {
        System.out.println(detail);
    }
    // 完成收益入账
    public void income() {
        System.out.println("收益入账金额:");
        money = scanner.nextDouble();
        // 数据校验
        if(money <= 0) {
            System.out.println("入账不能为负数");
            return;    // 退出方法,不再执行后面的代码
        }
        blance += money;
        // 拼接收益入账信息到 detail
        date = new Date();  // 获取到当前日期

        detail += "\n收益入账\t" + "+" + money + "\t" + simpleDateFormat.format(date) + "\t" + blance;

    }
    public void pay() {
        System.out.println("消费金额");
        money = scanner.nextDouble();
        if (money <= 0 || money > blance) {
            System.out.println("你的消费金额 应该在:0-" + blance);
            return;
        }
        System.out.println("消费说明:");
        note = scanner.next();
        blance -= money;
        date = new Date();
        detail += "\n" +  note + "\t-" + money + "\t" +simpleDateFormat.format(date) + "\t" + blance;

    }
    public void exit() {

        String choice = "";

        while (true) {
            System.out.println("输入y/n来退出程序");
            choice = scanner.next();
            if ("y".equals(choice) || "n".equals(choice)) {
                break;
            }
        }
        if (choice.equals("y")) {
            loop = false;
        }
    }
}



// 入口文件
package com.zfc.smallchange.oop;

// 程序入口
public class SmallChangeSystemApp {
    public static void main(String[] args) {

        new SmallChangeSystemOOP().mainMenu();

    }
}

房屋出租系统

image-gucf.png以下是项目源码(纯手敲)

houserent.zip

面向对象编程(高级)

类变量和类方法

main 方法

代码块

单例模式

饿汉式VS懒汉式

final关键字

抽象类

接口开发

在接口中,抽象方法可以省略 abstract 关键字

jdk8 后,可以有默认的实现方法,但是需要使用 default 关键字修饰,可以有静态的方法

注意事项:

  • 接口不能用于实例化对象。

  • 接口没有构造方法。

  • 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。

  • 接口不能包含成员变量,除了 static final 变量。

  • 接口不是被类继承了,而是要被类实现。

  • 接口支持多继承。

  • 抽象类实现接口的, 可以不用实现接口的方法

  • 接口中的所有方法都是 public 方法, 接口中的方法, 可以不用 abstract 修饰

  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

  • 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

  • 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法

alt + enter 可以快速实现接口方法的重写

接口和类的继承的区别: 类似于猴子生下来就继承了父母爬树的本领,但是它如果想像鱼儿一样游泳,像鸟儿一样飞翔,就要去学习,类似于实现某种功能,比如鸟儿(鱼儿)提供了一个训练的场地或者方法给猴子去实现。

当子类继承了父类, 就自动拥有父类的功能, 如果子类需要拓展功能, 可以通过实现接口的方式拓展

可以理解为,接口就是对类单继承的一种补充

继承是 is-a 的关系, 而接口类似于 like-a 的关系

内部类

基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class)嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

类的五大成员: 属性, 方法, 构造器, 代码块, 内部类

内部类的分类

定义在外部类局部位置上(比如方法内)

1)局部内部类(有类名)

2)匿名内部类(没有类名,重点!!!!!!)

定义在外部类的成员位置上:

1)成员内部类(没用 static 修饰)

2)静态内部类(使用 static 修饰)

局部内部类

如果外部类和局部类的成员重名时, 默认遵循就近原则, 如果像访问外部类的成员,使用 外部类名.this.成员 去访问

匿名内部类 (同时它还是一个对象)

简洁的写法

外部其他类, 使用成员内部类的三种方式

class Circle {
    private double radius = 0;
 
    public Circle(double radius) {
        this.radius = radius;
        getDrawInstance().drawSahpe();   //必须先创建成员内部类的对象,再进行访问
    }
     
    private Draw getDrawInstance() {
        return new Draw();
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
        }
    }
}

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必须通过Outter对象来创建
         
        //第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() {
         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {
             
        }
    }
}

内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。比如上面的例子,如果成员内部类 Inner 用 private 修饰,则只能在外部类的内部访问,如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被 public 和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        class Woman extends People{   //局部内部类
            int age =0;
        }
        return new Woman();
    }
}

注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。

匿名内部类

匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段 Android 事件监听代码:

scan_bt.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
});
 
history_bt.setOnClickListener(new OnClickListener() {
     
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
});

这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:

new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
}

就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。

private void setListener()
{
    scan_bt.setOnClickListener(new Listener1());       
    history_bt.setOnClickListener(new Listener2());
}
 
class Listener1 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
             
    }
}
 
class Listener2 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
             
    }
}

这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和 static 修饰符的。

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为 Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

.静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    public Outter() {
         
    }
     
    static class Inner {
        public Inner() {
             
        }
    }
}

内部类的使用场景和好处

为什么在 Java 中需要内部类?总结一下主要有以下四点:

  • 1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。

  • 2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

  • 3.方便编写事件驱动程序。

  • 4.方便编写线程代码。

个人觉得第一点是最重要的原因之一,内部类的存在使得Java的多继承机制变得更加完善。

四.常见的与内部类相关的笔试面试题

1.根据注释填写(1),(2),(3)处的代码

public class Test{
    public static void main(String[] args){
           // 初始化Bean1
           (1)
           bean1.I++;
           // 初始化Bean2
           (2)
           bean2.J++;
           //初始化Bean3
           (3)
           bean3.k++;
    }
    class Bean1{
           public int I = 0;
    }
 
    static class Bean2{
           public int J = 0;
    }
}
 
class Bean{
    class Bean3{
           public int k = 0;
    }
}

从前面可知,对于成员内部类,必须先产生外部类的实例化对象,才能产生内部类的实例化对象。而静态内部类不用产生外部类的实例化对象即可产生内部类的实例化对象。

创建静态内部类对象的一般形式为: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()

创建成员内部类对象的一般形式为: 外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()

因此,(1),(2),(3)处的代码分别为:

Test test = new Test();    
Test.Bean1 bean1 = test.new Bean1();
Test.Bean2 b2 = new Test.Bean2();
Bean bean = new Bean();     

Bean.Bean3 bean3 =  bean.new Bean3();

2.下面这段代码的输出结果是什么?

public class Test {
    public static void main(String[] args)  {
        Outter outter = new Outter();
        outter.new Inner().print();
    }
}
 
 
class Outter
{
    private int a = 1;
    class Inner {
        private int a = 2;
        public void print() {
            int a = 3;
            System.out.println("局部变量:" + a);
            System.out.println("内部类变量:" + this.a);
            System.out.println("外部类变量:" + Outter.this.a);
        }
    }
}
3
2
1

最后补充一点知识:关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:

  • 1)成员内部类的引用方式必须为 Outter.Inner

  • 2)构造器中必须有指向外部类对象的引用,并通过这个引用调用super()。

class WithInner {
    class Inner{
         
    }
}
class InheritInner extends WithInner.Inner {
      
    // InheritInner() 是不能通过编译的,一定要加上形参
    InheritInner(WithInner wi) {
        wi.super(); //必须有这句调用
    }
  
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner obj = new InheritInner(wi);
    }
}

枚举类

values(), ordinal() 和 valueOf() 方法

enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。

values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:

  • values() 返回枚举类中所有的值。

  • ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。

  • valueOf()方法返回指定字符串值的枚举常量。

enum Color
{
    RED, GREEN, BLUE;
}
 
public class Test
{
    public static void main(String[] args)
    {
        // 调用 values()
        Color[] arr = Color.values();
 
        // 迭代枚举
        for (Color col : arr)      //这样的方法就是增强for循环
        {
            // 查看索引
            System.out.println(col + " at index " + col.ordinal());
        }
 
        // 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
        System.out.println(Color.valueOf("RED"));
        // System.out.println(Color.valueOf("WHITE"));
    }
}

执行以上代码输出结果为:

RED at index 0
GREEN at index 1
BLUE at index 2
RED

枚举类成员

枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。

枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。

enum Color
{
    RED, GREEN, BLUE;
 
    // 构造函数
    private Color()
    {
        System.out.println("Constructor called for : " + this.toString());
    }
 
    public void colorInfo()
    {
        System.out.println("Universal Color");
    }
}
 
public class Test
{    
    // 输出
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
        c1.colorInfo();
    }
}

注解的理解

1)注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、,类、方法、属性、构造器、局部变量等数据信息。

2)和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当子嵌入在代码中的补充信息。

3)在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替iava EE日版中所遗留的繁冗代码和XML配置等。

基本的 Annotation介绍

使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素

>三个基本的 Annotation:

1)@Override: 限定某个方法,是重写父类方法,该注解只能用于方法

2)@Deprecated: 用于表示某个程序元素(类,方法等)已过时

3)@Suppress: Warnings: 抑制编译器警告

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解)是:

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

  • @Documented - 标记这些注解是否包含在用户文档中。

  • @Target - 标记这个注解应该是哪种 Java 成员。

  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

Annotation 架构

从中,我们可以看出:

(01) 1 个 Annotation 和 1 个 RetentionPolicy 关联。

可以理解为:每1个Annotation对象,都会有唯一的RetentionPolicy属性。

(02) 1 个 Annotation 和 1~n 个 ElementType 关联。

可以理解为:对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。

(03) Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等。

Annotation 的每一个实现类,都 "和 1 个 RetentionPolicy 关联" 并且 " 和 1~n 个 ElementType 关联"。

下面,我先介绍框架图的左半边(如下图),即 Annotation, RetentionPolicy, ElementType;然后在就 Annotation 的实现类进行举例说明。

异常处理

● 基本概念

Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

执行过程中所发生的异常事件可分为两类

1)Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out ofmemory),Error 是严重错误,程序会崩溃。

2)Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception 分为两大类:运行时异常和编译时异常[。

1.异常分为两大类,运行时异常和编译时异常.

2.运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该2避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常

3.对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响

4.编译时异常,是编译器要求必须处置的异常

常见的编译异常

SQLException //操作数据库时,查询表可能发生异常

IOException //操作文件时,发生的异常

FileNotFoundException //当操作一个不存在的文件时,发生异常

ClassNotFoundException //加载类,而该类不存在时,异常

EOFException // 操作文件,到文件末尾,发生异常

llegalArguementException //参数异常

ctrl + alt + t 快速生成try-catch 代码块

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。

try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型3 异常的变量名3){
  // 程序代码
}

上面的代码段包含了 3 个 catch块。

可以在 try 语句后面添加任意数量的 catch 块。

如果保护代码中发生异常,异常被抛给第一个 catch 块。

如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。

如果不匹配,它会被传递给第二个 catch 块。

如此,直到异常被捕获或者通过所有的 catch 块。

throws/throw 关键字

在Java中, throwthrows 关键字是用于处理异常的。

throw 关键字用于在代码中抛出异常,而 throws 关键字用于在方法声明中指定可能会抛出的异常类型。

throw 关键字

throw 关键字用于在当前方法中抛出一个异常。

通常情况下,当代码执行到某个条件下无法继续正常执行时,可以使用 throw 关键字抛出异常,以告知调用者当前代码的执行状态。

例如,下面的代码中,在方法中判断 num 是否小于 0,如果是,则抛出一个 IllegalArgumentException 异常

public void checkNumber(int num) {
  if (num < 0) {
    throw new IllegalArgumentException("Number must be positive");
  }
}

throws 关键字用于在方法声明中指定该方法可能抛出的异常。当方法内部抛出指定类型的异常时,该异常会被传递给调用该方法的代码,并在该代码中处理异常。

例如,下面的代码中,当 readFile 方法内部发生 IOException 异常时,会将该异常传递给调用该方法的代码。在调用该方法的代码中,必须捕获或声明处理 IOException 异常。

public void readFile(String filePath) throws IOException {
  BufferedReader reader = new BufferedReader(new FileReader(filePath));
  String line = reader.readLine();
  while (line != null) {
    System.out.println(line);
    line = reader.readLine();
  }
  reader.close();
}

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

import java.io.*;
public class className
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
       // Method implementation
   }
   //Remainder of class definition
}

finally 关键字用来创建在 try 代码块后面执行的代码块。

无论是否发生异常,finally 代码块中的代码总会被执行。

在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。

finally 代码块出现在 catch 代码块最后,语法如下

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}
public class ExcepTest{
  public static void main(String args[]){
    int a[] = new int[2];
    try{
       System.out.println("Access element three :" + a[3]);
    }catch(ArrayIndexOutOfBoundsException e){
       System.out.println("Exception thrown  :" + e);
    }
    finally{
       a[0] = 6;
       System.out.println("First element value: " +a[0]);
       System.out.println("The finally statement is executed");
    }
  }
}


========================结果如下===========================

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

注意下面事项:

  • catch 不能独立于 try 存在。

  • 在 try/catch 后面添加 finally 块并非强制性要求的。

  • try 代码后不能既没 catch 块也没 finally 块。

  • try, catch, finally 块之间不能添加任何代码

try-with-resources

JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。

try-with-resources 是一种异常处理机制,它可以简化资源管理代码的编写。

JDK7 之前所有被打开的系统资源,比如流、文件或者 Socket 连接等,都需要被开发者手动关闭,否则将会造成资源泄露。

try (resource declaration) {
  // 使用的资源
} catch (ExceptionType e1) {
  // 异常块
}

以上的语法中 try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。

import java.io.*;

public class RunoobTest {

    public static void main(String[] args) {
    String line;
        try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            while ((line = br.readLine()) != null) {
                System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        }
    }
}
IOException in try block =>test.txt (No such file or directory)

在 try-with-resources 语句中声明和实例化 BufferedReader 对象,执行完毕后实例资源,不需要考虑 try 语句是正常执行还是抛出异常。

如果发生异常,可以使用 catch 来处理异常。

再看下不使用 try-with-resources 而改成 finally 来关闭资源,整体代码量多了很多,而且更复杂繁琐了

import java.io.*;

class RunoobTest {
    public static void main(String[] args) {
        BufferedReader br = null;
        String line;

        try {
            System.out.println("Entering try block");
            br = new BufferedReader(new FileReader("test.txt"));
            while ((line = br.readLine()) != null) {
            System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        } finally {
            System.out.println("Entering finally block");
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                System.out.println("IOException in finally block =>"+e.getMessage());
            }
        }
    }
}
Entering try block
IOException in try block =>test.txt (No such file or directory)
Entering finally block

try-with-resources 处理多个资源

try-with-resources 语句中可以声明多个资源,方法是使用分号 ; 分隔各个资源:

import java.io.*;
import java.util.*;
class RunoobTest {
    public static void main(String[] args) throws IOException{
        try (Scanner scanner = new Scanner(new File("testRead.txt")); 
            PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            while (scanner.hasNext()) {
                writer.print(scanner.nextLine());
            }
        }
    }
}

声明自定义异常

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

  • 所有异常都必须是 Throwable 的子类。

  • 如果希望写一个检查性异常类,则需要继承 Exception 类。

  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

class MyException extends Exception{

class MyException extends Exception{
}

只继承Exception 类来创建的异常类是检查性异常类。

下面的 InsufficientFundsException 类是用户定义的异常类,它继承自 Exception。

一个异常类和其它任何类一样,包含有变量和方法。

// 文件名InsufficientFundsException.java
import java.io.*;
 
//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{
  //此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
  private double amount;
  public InsufficientFundsException(double amount)
  {
    this.amount = amount;
  } 
  public double getAmount()
  {
    return amount;
  }
}

为了展示如何使用我们自定义的异常类,

在下面的 CheckingAccount 类中包含一个 withdraw() 方法抛出一个 InsufficientFundsException 异常。

// 文件名称 CheckingAccount.java
import java.io.*;
 
//此类模拟银行账户
public class CheckingAccount
{
  //balance为余额,number为卡号
   private double balance;
   private int number;
   public CheckingAccount(int number)
   {
      this.number = number;
   }
  //方法:存钱
   public void deposit(double amount)
   {
      balance += amount;
   }
  //方法:取钱
   public void withdraw(double amount) throws
                              InsufficientFundsException
   {
      if(amount <= balance)
      {
         balance -= amount;
      }
      else
      {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
  //方法:返回余额
   public double getBalance()
   {
      return balance;
   }
  //方法:返回卡号
   public int getNumber()
   {
      return number;
   }
}

下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw() 方法。

//文件名称 BankDemo.java
public class BankDemo
{
   public static void main(String [] args)
   {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      try
      {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      }catch(InsufficientFundsException e)
      {
         System.out.println("Sorry, but you are short $"
                                  + e.getAmount());
         e.printStackTrace();
      }
    }
}
Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
        at CheckingAccount.withdraw(CheckingAccount.java:25)
        at BankDemo.main(BankDemo.java:13)

常用类

以上就是继承关系

String 类

可串行化就是可以在网络中传输,也可以保存在文件中

字符串的格式化

Arrays类

System类

BigInteger和BigDecimal类

Date类

开发中尽量使用第三代日期

一个简易的注册系统(勿喷)

    public static void register(String user, String pwd, String email) {

        int userlength = user.length();
        if (!( userlength >= 2 && userlength <= 4)) {
            throw new RuntimeException("用户名长度必须在2到4之间");
        }
        if (!(pwd.length() == 6 && isDigital(pwd))){
            throw new RuntimeException("密码必须全部是数字");
        }
        int i = email.indexOf('@');
        int j = email.indexOf('.');
        if (!(i > 0 && j > i)) {
            throw new RuntimeException("格式错误");

        }
        System.out.println("恭喜你注册成功!!!");
    }

    public static boolean isDigital(String str) {
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] < '0' || chars[i] > '9') {
                return false;
            }
        }
        return true;
    }


test
        try {
            register("zfc","123456","2284897846@qq.com");
        } catch (Exception e) {
            System.out.println(e.getMessage());;
        }	

集合

数组

1}长度开始时必须指定,而且一旦指定,不能更改

2)保存的必须为同一类型的元素

3)使用数组进行增加元素的示意代码 -比较麻烦

集合

1)可以动态保存任意多个对象,使用比较方便!

2)提供了一系列方便的操作对象的方法:add、remove、set、get等

3)使用集合添加,删除新元素的示意代码-简洁了

体系图

Colection接口和常用方法

Collection接口实现类的特点

public interface Collection<E> extends iterable<E>

1)collection实现子类可以存放多个元素,每个元素可以是Obiect

2)有些Collection的实现类,可以存放重复的元素,有些不可以

3)有些Colection的实现类,有些是有序的(List),有些不是有序(Set)

4)Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的

常用方法

    public static void main(String[] args) {
        List list = new ArrayList();
        // 添加单个元素  add
        list.add("zfc");
        list.add(100);
        System.out.println(list);
        // remove
        list.remove(0); // 删除第一个元素
        // contians 查找某个元素是否存在
        list.contains("zfc");
        // size:获取元素个数
        list.size();
        // isEmpty: 判断是否为空
        list.isEmpty();
        // clear() 清空
        // addAll  添加多个元素
       // contiansAll 判断多个呀元素是否存在
        // removeAll 删除多个元素

Collection接口遍历元素方式1-使用iterator(迭代器)

基本介绍

1)lterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。

2)所有实现了Collection接囗的集合类都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个迭代器。

3)lterator 的结构.[图:]4)lterator 仅用于遍历集合,lterator 本身并不存放对象,

迭代器的执行原理

>

Iterator iterator = coll.iterator();//得到一个集合的迭代器

//hasNext():判断是否还有下一个元素

while(iterator.hasNext()){//next():①指针下移 ②将下移以后集合位置上的元素返回

System.out.printIn(iteratornext())

在调用iterator.next()方法之前必须要调用iterator.hasNext()

进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。

Collection接口遍历对象方式2-for循环增强

增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator本质一样。只能用于遍历集合或数组,

>基本语法

for(元素类型 元素名:集合名或数组名){

}

/**
 * @author zfc
 * @version 1.0
 */
public class Collection_ {
    public static void main(String[] args) {
        @SuppressWarnings({"all"})
        Collection col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 10.1F));
        col.add(new Book("红楼梦", "曹雪芹", 19.1F));
        col.add(new Book("小李飞刀", "古龙", 20.1F));



        // 使用增强For 循环     底层仍然是迭代器
        for (Object book : col) {
            System.out.println("book=" + book);
        }

    }

}
class  Book {
    public String name;
    public String author;

    public float price;

    public Book(String name, String author, float price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

        // 省略其他部分
    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';

    }

}

增强for循环的 快捷键 大写字母 I

List 接口和常用方法

遍历方式

根据价格进行排序

    public static void main(String[] args) {

        List col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 10.1F));
        col.add(new Book("红楼梦", "曹雪芹", 19.1F));
        col.add(new Book("小李飞刀", "古龙", 20.1F));

        // 使用增强For 循环     底层仍然是迭代器
        for (Object book : col) {
            System.out.println("book=" + book);
        }
        // 按照价格进行排序

        int listSize = col.size();
        for (int i = 0; i < listSize - 1; i++){
            for (int j = 0; j < listSize - 1 - i; j++ ) {
                // 取出Book对象,向下转型
                Book book1 = (Book)col.get(j);
                Book book2 = (Book)col.get(j + 1);
                if (book1.getPrice() > book2.getPrice()) {
                    col.set(j, book2);
                    col.set(j + 1, book1);
                }

            }
        }

ArrayList 线程不安全

transient 表示瞬间,短暂的, 表示改属性不会被序列化

Vector的基本介绍

LinkedList

单链表

package com.zfc.smallchange.oop;

public class LinkedList {
    public static void main(String[] args) {
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node zfc = new Node("zfc");

        jack.next = tom;
        tom.pre = jack; // 修正前驱节点的链接
        tom.next = zfc;
        zfc.pre = tom;

//        Node first = jack;
//        // 打印链表
//        while (first != null) {
//            System.out.println(first.string()); // 调用 Node 类中的 string 方法打印节点数据
//            first = first.next;
//        }
        System.out.println("=======从尾部开始遍历链表======");
        // 从尾部开始遍历链表
        Node last = zfc;
        while (last != null) {
            System.out.println(last.string());
            last = last.pre;
        }

        // 添加节点 在tom节点后面添加一个节点
        Node smith = new Node("smith");
        smith.next = zfc;
        smith.pre = tom;
        tom.next = smith;
        zfc.pre = smith;

        // 打印链表
        System.out.println("=======打印链表======");
        Node first = jack;
        while (first != null) {
            System.out.println(first.string()); // 调用 Node 类中的 string 方法打印节点数据
            first = first.next;
        }
    }

    static class Node {
        public Object data;
        public Node pre;
        public Node next;

        public Node(Object data) {
            this.data = data;
        }

        // 新增方法 string
        public String string() {
            return "Node data: " + data;
        }
    }
}

Set 接口

HashSet

import java.util.HashSet;
import java.util.Objects;

public class Employee {
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    public static void main(String[] args) {
        HashSet<Employee> employees = new HashSet<>();

        // 创建3个Employee对象并放入HashSet中
        employees.add(new Employee("Alice", 50000, new MyDate(1990, 5, 15)));
        employees.add(new Employee("Bob", 60000, new MyDate(1987, 9, 8)));
        employees.add(new Employee("Alice", 50000, new MyDate(1990, 5, 15))); // 重复的员工,不应该被添加

        // 打印HashSet中的员工
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee employee = (Employee) o;
        return Objects.equals(name, employee.name) &&
                Objects.equals(birthday, employee.birthday);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}

class MyDate {
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

LinkedHashSet

Map接口

package com.zfc.smallchange.oop;

import java.util.*;

/**
 * @author zfc
 * @version 1.0
 */
public class Map_ {
    public static void main(String[] args) {
        Map map = new HashMap();

        map.put("N01", "张三");

        map.put("NO2", "李四");

        // 有相同的K值,会覆盖
        map.put("NO2", "李四");

        System.out.println(map);
        map.remove(null);
        map.containsKey("N01");

        Set set = map.keySet();
        // 遍历
        for (Object key : set) {
            System.out.println(key + " : " + map.get(key));
        }
        //迭代器取出
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            System.out.println(key + " : " + map.get(key));
        }
        // 取出所有值
        Collection collection = map.values();    // 取出所有值
        for (Object value : collection) {
            System.out.println(value);
        }

        // 通过EntrySet取出所有键值对
        Set entrySet = map.entrySet();
        for (Object entry : entrySet) {
            Map.Entry mapEntry = (Map.Entry) entry;  // 向下转型 才有getKey()和getValue()方法
            System.out.println(mapEntry.getKey() + " : " + mapEntry.getValue());
        }

    }

}

HashMap

Hashtable

TreeSet

package com.zfc.smallchange.oop;

import java.util.Comparator;
import java.util.TreeSet;

@SuppressWarnings("all")
public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();

        treeSet.add("apple");
        treeSet.add("banana");
        treeSet.add("orange");
        treeSet.add("mary");

        // 遍历原始 TreeSSet
        System.out.println("原始 TreeSet: " + treeSet);

        // 从小到大排序
        TreeSet<String> compare = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        // 将元素添加到 compare
        compare.addAll(treeSet);

        // 逆序排序
        TreeSet<String> reverseSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        // 将元素添加到 reverseSet
        reverseSet.addAll(treeSet);

        // 遍历 compare
        System.out.println("从小到大排序: " + compare);

        // 遍历逆序排序的 set
        System.out.println("从大到小排序: " + reverseSet);

        // 遍历原始 treeSet
        System.out.println("原始 TreeSet: " + treeSet);
    }
}

总结

Collections 工具类

Collections工具类介绍

1)Collections 是一个操作 Set、List 和 Map 等集合的工具类

2)Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

排序操作:(均为static方法)

1)reverse(List):反转 List 中元素的顺顺序

2)shuffle(List):对 List 集合元素进行随机排序

3)sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序

4)sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序

5)swap(List,int, int):将指定 list 集合中的i处元素和j处元素进行交换

常用方法

List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(9);

Collections.sort(numbers);



List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Carol");

Collections.reverse(names);



List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);

Collections.shuffle(numbers);



List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Carol");
names.add("Dave");

int index = Collections.binarySearch(names, "Bob");



List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(9);

Integer maxNumber = Collections.max(numbers);




List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(9);

Integer minNumber = Collections.min(numbers);


int frequency(Collection,0bject):返回指定集合中指定元素的出现次数

void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,0bject oldVal,0bject newVal):使用新值替换 List 对象的所有旧值

//为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样

public class News {
    private String title;    // 新闻标题
    private String content;  // 新闻内容

    public News(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "News{" +
                "title='" + title + '\'' +
                '}';
    }
}



public class testNews {
    public static void main(String[] args) {
        News news = new News("新冠确诊病例超千万,数百万印度教信徒赴恒河'圣浴',引起民众担忧");
        News news1 = new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生");
        ArrayList arrayList = new ArrayList();
        arrayList.add(news);
        arrayList.add(news1);
        // 倒序遍历
        for (int i = arrayList.size() - 1; i >= 0; i--) {
            System.out.println(arrayList.get(i));
        }
        System.out.println(processTitle(news.getTitle()));
        System.out.println();
        System.out.println(processTitle(news1.getTitle()));

    }
    public static String processTitle(String title) {
        if (title == null) {     // 标题为空
            return "";
        }
        if (title.length() > 15) {  // 标题长度大于10
            return title.substring(0, 15) + "...";
        }
        return title;
    }
}

泛型

泛型的理解

int a = 10:

泛型=>Integer, String,Dog

1)泛型又称参数化类型,是Jdk5.0 出现的新特性,解决数据类型的安全性问题

2)在类声明或实例化时只要指定好需要的具体的类型即可。

3)Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮

4)泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。

泛型的声明

nterface 接口<T>{} 和 class 类<K,V>{}

//比如:List , ArrayList

说明:1)其中,T,K,V不代表值,而是表示类型。

2)任意字母都可以。常用T表示,是Type的缩写

myDate.java

@SuppressWarnings({"all"})
public class myDate implements Comparable<myDate> {
    private int year;
    private int month;
    private int day;

    public myDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public String toString() {
        return "myDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    @Override
    public int compareTo(myDate o) {
        int yearMinus = this.year - o.getYear();
        if (yearMinus!= 0){

            return yearMinus;
        } // 同年龄按月份排序
        int monthMinus = this.month - o.getMonth();
        if (monthMinus!= 0){
            return monthMinus;
        } // 同年月份按日排序
        int dayMinus = this.day - o.getDay();

        return dayMinus;
    }
}


Employee.java
@SuppressWarnings({"all"})
public class Employee {
    private String name;
    private double salary;

    private myDate birthDate;

    public Employee(String name, double salary, myDate birthDate) {
        this.name = name;
        this.salary = salary;
        this.birthDate = birthDate;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public myDate getBirthDate() {
        return birthDate;
    }

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

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public void setBirthDate(myDate birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "\nEmployee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", birthDate=" + birthDate +
                '}';
    }
}


testEmployee.java
@SuppressWarnings({"all"})
public class testEmployee {
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();

        employees.add(new Employee("John", 70000,new myDate(2000, 1, 1)));
        employees.add(new Employee("Jane", 50000, new myDate(2001, 2, 2)));
        employees.add(new Employee("Bob", 40000, new myDate(2002, 3, 3)));

        System.out.println(employees);
        // 对员工进行排序
        employees.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                // 先按name排序
                    int result = o1.getName().compareTo(o2.getName());
                    if (result != 0){
                        return result;
                    }
                    return o1.getBirthDate().compareTo(o2.getBirthDate());
            }
        });
        System.out.println();
        System.out.println(employees);
    }
}

这道题非常经典, 耐人寻味.....

注意细节:

1.普通成员可以使用泛型 (属性、方法)

2.使用泛型的数组,不能初始化

3.静态方法中不能使用类的泛型

4.泛型类的类型,是在创建对象时确定的(因为创建对象时需要指定确定类型)

5.如果在创建对象时,没有指定类型,默认为Object

自定义泛型

> 注意细节

1)接口中,静态成员也不能使用泛型(这个和泛型类规定一样)

2)泛型接口的类型,在继承接口或者实现接口时确定

3)没有指定类型,默认为Object

注意细节泛型方法

1.可以定义在普通类中,也可以定义在泛型类

2.当泛型方法被调用时,类型会确定

3.public void eat(E e){},修饰符后没有<T,R..>eat方法不是泛型方法,而是使用了泛型

JUnit

为什么需要JUnit

1.一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中

2.如果有多个功能代码测试,就需要来回注销,切换很麻烦

3.如果可以直接运行一个方法,就方便很多,并且可以给出相关信息,就好了->JUnit

package com.zfc.smallchange.oop;

import org.junit.jupiter.api.Test;

import java.util.*;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class generate {
    public static void main(String[] args) {


    }

    @Test
    public void testList() {
        DAO<User> userDAO = new DAO<>();
        userDAO.save("001", new User("1", "Tom", 20));
        userDAO.save("002", new User("2", "Jerry", 25));
        userDAO.save("003", new User("3", "Mary", 30));

        userDAO.delete("002");
        userDAO.update("003", new User("3", "Mary", 35));

        List<User> list = userDAO.list();
        for (User user : list) {
            System.out.println(user.getId() + " " + user.getName() + " " + user.getAge());
        }
    }

    @SuppressWarnings({"all"})
// 自定义一个泛型类
    class DAO<T> {
        private Map<String, T> map = new HashMap<>();

        @Test
        public void save(String id, T entity) {
            map.put(id, entity);

        }

        @Test
        public T get(String id) {
            return map.get(id);
        }

        public void delete(String id) {
            map.remove(id);

        }

        public void update(String id, T entity) {
            map.put(id, entity);

        }
        // 自定义一个方法,保存一个集合
        public List<T> list() {

            List<T> list = new ArrayList<>();
            // 遍历map
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                list.add(map.get(key));
            }
            return list;
        }
    }

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

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

        public String getId() {
            return id;
        }

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

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

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

Java绘图坐标体系

坐标体系-像素

1.绘图还必须要搞清一个非常重要的概念-像素 一个像素等于多少厘米?

2.计算机在屏幕上显示的内容都是由屏幕上的每 一个像素组成的。例如,计算机显示器的分辨率是800x600,表示计算机屏幕上的每一行由800个点组成,共有600行整个计算机屏幕共有480 000个像素。像素是一个密度单位,而厘米是长度单位,两者无法比较

package com.zfc.smallchange.oop;

import javax.swing.*;
import java.awt.*;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class Drawcircle extends JFrame {
    private MyCircle panel = null; // 定义一个JPanel对象作为容器
    public static void main(String[] args) {
        new Drawcircle(); // 创建JFrame对象


    }
    public Drawcircle() {
        panel = new MyCircle();
        this.add(panel); // 将JPanel添加到JFrame中
        this.setSize(300, 300); //
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭窗口时的操作  
        this.setVisible(true); // 创建JPanel对象

    }
}

class MyCircle extends JPanel{
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.drawOval(10, 10, 100, 100);
    }
}

java事件处理机制

iava事件处理是采取"委派事件模型"。当事件发生时,产生事件的对象,会把此"信息"传递给“事件的监听者”处理,这里所说的"信息"实际上就是 java.awt.event 事件类库里某个类所创建的对象,把它称为"事件的对象"。

Java多线程

线程相关概念

进程

1.就启动了一个进程,操作系统就会为进程是指运行中的程序,比如我们使用QQ,该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。

2.进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的2产生、存在和消亡的过程

线程

并发和执行

创建线程

线程的生命周期

线程同步机制

class SellTicket02 implements Runnable {

    private  int ticketNum = 100;

    private boolean loop = true;
    public synchronized void m() {
        int ticketNum = 100;
        while (loop) {
            if (ticketNum <= 0) {
                System.out.println("" + Thread.currentThread().getName() + " 票已售完");
                loop = false;
                return;
            }
            // 休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口" + Thread.currentThread().getName() + " 售出一张票:" + "剩余票数" + --ticketNum);
        }
    }
    @Override
    public void run() {
        m();

    }
}


===========================================在代码块上枷锁=======================================


class SellTicket02 implements Runnable {

    private  int ticketNum = 100;

    private boolean loop = true;
    public /*synchronized*/ void m() {
        int ticketNum = 100;
        // 在代码块上加锁
        synchronized (this) {
            while (loop) {
                if (ticketNum <= 0) {
                    System.out.println("" + Thread.currentThread().getName() + " 票已售完");
                    loop = false;
                    return;
                }
                // 休眠50ms
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口" + Thread.currentThread().getName() + " 售出一张票:" + "剩余票数" + --ticketNum);
            }
        }

    }
    @Override
    public void run() {
        m();

    }
}

线程死锁

多个线程都占用了对方的锁资源,

但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.

应用案例妈妈:你先完成作业,才让你玩手机小明:你先让我玩手机,我才完成作业

释放锁

IO流

package com.zfc.File;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;

/**
 * @author zfc
 * @version 1.0
 */

@SuppressWarnings({"all"})
public class FileCreate {
    public static void main(String[] args) {

    }
    @Test
    public void createFile() {

        String filePath = "C:/Users/lenovo/Desktop/1.txt";
        File file = new File(filePath);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 创建方式二
    }
    @Test

    public void createFile2() {
        File parentFile = new File("C:/Users/lenovo/Desktop");
        String fileName = "2.txt";
        File file = new File(parentFile, fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 创建方式三

    public void createFile3() {
        String parentPath = "C:/Users/lenovo/Desktop";
        String fileName = "3.txt";
        File file = new File(parentPath, fileName);
        try {
            if (!file.exists()) {
                file.createNewFile();
                System.out.println("文件创建成功");
            } else {
                System.out.println("文件已存在");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

package com.zfc.File;

import org.junit.jupiter.api.Test;

import java.io.File;

/**
 * @author zfc
 * @version 1.0
 */

@SuppressWarnings({"all"})
public class FileInformation {
    // 获取文件的信息
    public static void main(String[] args) {

    }
    @Test
    public void info() {
        File file = new File("C:/Users/lenovo/Desktop/1.txt");
        System.out.println();
        System.out.println("文件名:" + file.getName());
        System.out.println("绝对路径:" + file.getAbsolutePath());
        System.out.println("文件大小:" + file.length() + "字节");
        System.out.println("是否存在:" + file.exists());
        System.out.println("是否是文件:" + file.isFile());
        System.out.println("是否是目录:" + file.isDirectory());
        System.out.println("最后修改时间:" + file.lastModified());

    }

}

Java lO流原理

1.I/0是Input/0utput的缩写,I/0技术是非常实用的技术,用于处理数据传输如读/写文件,网络通讯等。

2.Java程序中,对于数据的输入/输出操作以”流(stream)”的方式进行

3.java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据

4.输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

5.输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类

文件输出流的操作

package com.zfc.inputStream;

import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author zfc
 * @version 1.0
 */

@SuppressWarnings({"all"})
public class FileInputStream_ {
    public static void main(String[] args) {

    }

    @Test
    public void readFile() {
        String filePath = "C:/Users/lenovo/Desktop/1.txt";
        int readData = 0;
        // 定义一个字节数组
        byte[] buffer = new byte[8];
        int readLength = 0;
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(filePath);
            while (( readLength= fileInputStream.read(buffer)) != -1) {
                System.out.print(new String(buffer,0, readLength));  // 转化为char类型输出
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            System.out.println("文件读取完毕");
        }

    }
}

节点流,和处理流

1.节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter

2.处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter

ObjectInputStream 和 ObjectOutputStream

细节

标准输入输出流

Properties 类

网络编程

域名和端口

网络通信协议

TCP/UDP

TCP协议的举例

UDP协议举例

对比

InetAddress类

package com.zfc.Network.api;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author zfc
 * @version 1.0
 * 演示InetAddress类
 */

@SuppressWarnings("all")
public class API_ {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress loscalhost = InetAddress.getLocalHost();
        System.out.println(loscalhost);

        //根据指定主机名获取InetAddress对象
        InetAddress host1 = InetAddress.getByName("Kenneth");
        System.out.println(host1);

        // 根据域名来获取InetAddress对象
        InetAddress host2 = InetAddress.getByName("www.gampablog.top");
        System.out.println(host2);


        // 通过 InetAddress 对象获取对应的地址
        System.out.println(host2.getHostAddress());
        System.out.println(host2.getHostName());
    }

}

Socket

server 端

package com.zfc.Network.socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author zfc
 * @version 1.0
 * 服务端
 */
@SuppressWarnings({"all"})
public class Server {
    public static void main(String[] args) {
        // 监听9999端口
        int port = 9999;

        // 创建ServerSocket对象,监听端口
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("服务正在启动中..." + port + "端口已监听");
            // 等待客户端连接,如果又客户端连接,则会返回一个Socket对象,程序继续执行
            Socket socket = serverSocket.accept();
            System.out.println("服务器启动,监听端口:" + port);
            InputStream inputstream = socket.getInputStream();
            // IO流读取数据

//            byte[] buffer = new byte[1024];
//            int len = 0;
//            while ((len = inputstream.read(buffer))!= -1) {
//                System.out.println(new String(buffer, 0, len));
//            }

            // 使用字符流来读取
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputstream));
            String s = bufferedReader.readLine();
            System.out.println(s);

            // 获取输出流
            OutputStream outputstream = socket.getOutputStream();

            // 使用字符输出流的方式回复信息
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputstream));
            bufferedWriter.write("Hello,客户端");
            bufferedWriter.newLine();
            bufferedWriter.flush();


//            outputstream.write("Hello,客户端".getBytes());

            socket.shutdownOutput();

            // 关闭资源
            bufferedWriter.close();
            bufferedReader.close();
            inputstream.close();
            outputstream.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }



    }

}

client 端

package com.zfc.Network.socket;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

/**
 * @author zfc
 * @version 1.0
 * 客户端
 */
@SuppressWarnings("ALL")

public class client {
    public static void main(String[] args) {
        // 连接到服务器
        try {
            Socket socket = new Socket(InetAddress.getLocalHost(),9999);
            System.out.println("客户端已连接到服务器");
            // 发送数据
            OutputStream outputStream = socket.getOutputStream();
            // 字节流
            //outputStream.write("Hello,Server!".getBytes());
            // 使用字符流
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("Hello,Server!");
            bufferedWriter.newLine();   // 插入一个换行符, 表示写入内容结束
            bufferedWriter.flush();     // 如果使用的字符流, 需要手动刷新, 否则数据不会写入数据通道

            // 写入结束标记
            // socket.shutdownOutput();
            // 关闭连接
            InputStream inputStream = socket.getInputStream();
//            byte[] bytes = new byte[1024];
//            int len = 0;
//            while ((len = inputStream.read(bytes)) != -1){
//                System.out.println("接收到服务器的消息:" + new String(bytes, 0, len));
//
//            }

            // 使用字符流接收数据
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = bufferedReader.readLine();
            System.out.println(line);

//            inputStream.close();
            bufferedWriter.close();    // 关闭外层流
            bufferedReader.close();    // 关闭外层流

//            outputStream.close();
            socket.close();

            System.out.println("客户端已断开连接....");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

网络上传文件

思路分析

server端

package com.zfc.Network.socket.TCPFileUploadServer;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class server {
    public static void main(String[] args) {
        int port = 8888;
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("Server is running on port: "+ port+"...");
            // 循环监听客户端连接
            Socket client_accept = serverSocket.accept();
            System.out.println("客户端连接成功!");

            // 读取客户端发送的数据
            BufferedInputStream bis = new BufferedInputStream(client_accept.getInputStream());
            try {
                byte[] data = com.zfc.Network.socket.StreamUtils.StreamUtils.streamToByteArray(bis);
                // 将得到的 byte[] 数组写入到文件中
                String output_path = "src/com/zfc/Network/socket/img/output.jpg";;
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(output_path));
                bos.write(data);
                bis.close();
                bos.close();
                client_accept.close();
                serverSocket.close();
                System.out.println("文件上传成功!");

                // 向客户端回复收到图片
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client_accept.getOutputStream()));
                writer.write("已经收到图片!");
                writer.flush();
                client_accept.shutdownInput();
                writer.close();

            } catch (Exception e) {
                throw new RuntimeException(e);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }
}

client端

package com.zfc.Network.socket.TCPFileUploadServer;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

@SuppressWarnings({"all"})
public class client {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
            // 创建读取磁盘文件的输入流

            String file_path = "src/com/zfc/Network/socket/img/1.jpg";
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file_path));
            try {
                byte[] data = com.zfc.Network.socket.StreamUtils.StreamUtils.streamToByteArray(bis);
                // 发送文件数据
                // 得到输出流对象
                BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
                bos.write(data); // 将文件对应的字节数组的内容写入到数据通道

                bis.close(); // 关闭输入流
                bos.flush(); // 刷新输出流

                socket.shutdownOutput(); // 告诉服务端数据发送完毕

                bos.close(); // 关闭输出流
                socket.close(); // 关闭socket连接

                // 获取 服务端返回的结果

                InputStream inputStream = socket.getInputStream();
                // 使用StreamUtils工具类,将字节流转为字符串
                String result = com.zfc.Network.socket.StreamUtils.StreamUtils.streamToString(inputStream);
                System.out.println(result); // 输出服务端返回的结果
                inputStream.close(); // 关闭输入流

            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                socket.close(); // 关闭socket连接
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

转换工具类

package com.zfc.Network.socket.StreamUtils;

import java.io.*;

/**
 * 此类用于演示关于流的读写方法
 */
@SuppressWarnings({"all"})
public class StreamUtils {
    /**
     * 将输入流转换成 byte[]
     *
     * @param is 输入流
     * @return 转换后的字节数组
     * @throws Exception 如果转换过程中发生异常
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len;
        while ((len = is.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        byte[] array = bos.toByteArray();
        bos.close();
        return array;
    }

    /**
     * 将 InputStream 转换成 String
     *
     * @param is 输入流
     * @return 转换后的字符串
     * @throws Exception 如果转换过程中发生异常
     */
    public static String streamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        reader.close();
        return sb.toString();
    }

    /**
     * 将 String 写入到 OutputStream
     *
     * @param str  要写入的字符串
     * @param os 输出流
     * @throws Exception 如果写入过程中发生异常
     */
    public static void stringToStream(String str, OutputStream os) throws Exception {
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
        writer.write(str);
        writer.flush();
        writer.close();
    }
}

UDP编程

UDP接收端

package com.zfc.Network.socket.UDP;

import java.io.IOException;
import java.net.*;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class UDPReceiverA {
    public static void main(String[] args) {
        // 创建一个 DatagramSocket 对象,用于接收 UDP 数据报
        try {
            DatagramSocket socket = new DatagramSocket(9999);
            // 构建一个DatagramPacket对象,用于存储接收到的数据
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            // 准备接受数据

            try {
                System.out.println("等待接收数据...");
                socket.receive(packet);
                int length = packet.getLength(); // 获得数据长度
                byte[] data = packet.getData(); // 获得数据
                String message = new String(data, 0, length); // 将字节数组转为字符串
                System.out.println();
                System.out.println("接收到的数据为: " + message);
                // 进行拆包, 取出数据

                data = "好的,明天见!".getBytes(); // 构造数据
                packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 9998); // 构建新的 DatagramPacket 对象
                socket.send(packet); // 发送数据
                 // 输出发送的数据

                socket.close();
                System.out.println("A端退出...");

            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        } catch (SocketException e) {
            throw new RuntimeException(e);
        }
        // 创建一个 byte 数组,用于存储接收到的数据

    }
}

UDP发送端

package com.zfc.Network.socket.UDP;

import java.io.IOException;
import java.net.*;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"ALL"})
public class UDPSenderB {
    public static void main(String[] args) {
        try {
            DatagramSocket Socket = new DatagramSocket(9998);
            // 将需要发送的数据封装到DatagramPacket中
            byte[] data = "Hello, UDP!".getBytes();
            DatagramPacket packet = 
new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 9999);
            try {
                Socket.send(packet);

                System.out.println("B端发送数据成功!已退出!");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            // 接收服务器端返回的数据


            byte[] buffer = new byte[1024];
            packet = new DatagramPacket(buffer, buffer.length);
            try {
                Socket.receive(packet);
                int length = packet.getLength();
                String s = new String(packet.getData(), 0, length);
                System.out.println("B端接收到的回复:" + s);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            Socket.close();
        } catch (SocketException e) {
            throw new RuntimeException(e);

        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }

    }
}

项目开发的流程

多用户通讯系统

分析

反射

分析

package com.hspedu.reflection;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class Reflection01 {

    public static void main(String[] args) throws Exception {


        //1. 使用Properties 类, 可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
        String methodName = properties.get("method").toString();//"hi"


        //2. 使用反射机制解决
        //(1) 加载类, 返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
        //(2) 通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass()); //运行类型
        //(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi"  的方法对象
        //    即:在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
        //(4) 通过method1 调用方法: 即通过方法对象来实现调用方法
        System.out.println("=============================");
        method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)

        //java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量
        //得到name字段
        //getField不能得到私有的属性
        Field nameField = cls.getField("age"); //
        System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 :  成员变量对象.get(对象)

        //java.lang.reflect.Constructor: 代表类的构造方法, Constructor对象表示构造器
        Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
        System.out.println(constructor);//Cat()



        Constructor constructor2 = cls.getConstructor(String.class); //这里老师传入的 String.class 就是String类的Class对象
        System.out.println(constructor2);//Cat(String name)
    }
}

Class 类

`

package com.hspedu.reflection.class_;

import com.hspedu.Car;

import java.lang.reflect.Field;

/**
 * @author 周富城
 * @version 1.0
 * 演示Class类的常用方法
 */
public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {

        String classAllPath = "com.hspedu.Car";
        //1 . 获取到Car类 对应的 Class对象
        //<?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //2. 输出cls
        System.out.println(cls); //显示cls对象, 是哪个类的Class对象 com.hspedu.Car
        System.out.println(cls.getClass());//输出cls运行类型 java.lang.Class
        //3. 得到包名
        System.out.println(cls.getPackage().getName());//包名
        //4. 得到全类名
        System.out.println(cls.getName());
        //5. 通过cls创建对象实例
        Car car = (Car) cls.newInstance();
        System.out.println(car);//car.toString()
        //6. 通过反射获取属性 brand
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));//宝马
        //7. 通过反射给属性赋值
        brand.set(car, "奔驰");
        System.out.println(brand.get(car));//奔驰
        //8 我希望大家可以得到所有的属性(字段)
        System.out.println("=======所有的字段属性====");
        Field[] fields = cls.getFields();
        for (Field f : fields) {
            System.out.println(f.getName());//名称
        }


    }
}

类加载




通过反射创建对象

package com.zfc.Reflection;

import java.lang.reflect.Constructor;

/**
 * @author zfc
 * @version 1.0
 * 演示通过反射机制,创建实例
 */
@SuppressWarnings({"all"})
public class RefecCreateInstance {
    public static void main(String[] args) throws Exception {
        // 获取User的Class对象
        Class<?> userClass = Class.forName("com.zfc.Reflection.User");

        //1. 通过Class对象的无参构造器创建实例

        Object o = userClass.getDeclaredConstructor().newInstance();
        System.out.println(o);

        //2. 通过Class对象public有参构造器创建实例
        Constructor<?> constructor = userClass.getConstructor(String.class);
        Object zfc = constructor.newInstance("zfc");
        System.out.println(zfc);

        //3. 通过Class对象public有无参构造器创建实例

        //4.通过非public有参构造器创建实例
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(String.class, int.class);
        Object zfc1 = constructor1.newInstance("zfc", 18);
        System.out.println(zfc1);

    }

}
@SuppressWarnings({"all"})

class User {
    private String name;
    private int age;

    public User() {

    }

    public User(String name) {
        this.name = name;
    }

    private User(String name, int age) {


        this.name = name;
        this.age = age;

    }

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

}

JDBC和数据库连接池

mysql-connector-java 版本选择下载地址: MySQL :: Download MySQL Connector/J (Archived Versions)

简单的连接

package com.zfc.mysql;

import com.mysql.cj.jdbc.Driver;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class Jdb {
    public static void main(String[] args) throws SQLException {
        // 注册驱动
        Driver driver = new Driver();

        //得到连接
        String url = "jdbc:mysql://localhost:3306/zfc_db01";
        Properties properties = new Properties();
        properties.setProperty("user", "root");
        properties.setProperty("password", "123456");
        Connection connection = driver.connect(url, properties);
        // 执行sql
        String sql = "INSERT INTO user1 VALUES (2, '***', '123456', '1998-01-01')";

        Statement statement = connection.createStatement();

        int rows = statement.executeUpdate(sql);
        System.out.println(rows >0 ? "插入成功" : "插入失败");
        // 关闭连接
        statement.close();
        connection.close();

    }
}

ResultSet(结果集)

package com.zfc.mysql;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class ResultSet {
    public static void main(String[] args) throws ClassNotFoundException, SQLException, IOException {
        // 获取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/mysql.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        // 加载驱动
        Class.forName(driver);

        // 获取连接
        Connection connection = DriverManager.getConnection(url, user, password);

        // 得到statement对象
        Statement statement = connection.createStatement();
        String sql = "SELECT * FROM user1";

        // 执行查询并获取结果集
        java.sql.ResultSet resultSet = statement.executeQuery(sql);


        // 读取结果集数据

        while (resultSet.next()) {
            System.out.println(resultSet.getString("id") + " " + resultSet.getString("name"));
        }
        // 关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

SQL注入

预处理查询(PrepareStatement)

Jdbc API

封装JdbcUtils

package com.zfc.mysql;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.sql.ResultSet;
/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings("all")
public class JDBCUtils {
    // 因为只需要一份,所以所有的方法都为static
    public static  String user;  //用户名

    public static  String password; // 密码

    public static  String url;  // 数据库连接地址

    public static  String driver; // 数据库驱动类名

    // 在静态代码块初始化
    static {


        try {
            Properties properties = new Properties();// 读取配置文件
            properties.load(new FileInputStream("src/mysql.properties"));
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            driver = properties.getProperty("driver");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    // 连接数据库
    public static  Connection getConnection() {

        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    // 关闭相关资源

    public static void close(ResultSet rs, Statement statement, Connection conn ) {
          if (rs != null) {
              try {
                  rs.close();
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              }
          }
          if (statement != null) {
              try {
                  statement.close();
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              }
          }
          if (conn != null) {
              try {
                  conn.close();
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              }
          }
    }

}

事务

package com.hspedu.jdbc.transaction_;

import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author zfc
 * @version 1.0
 * 演示jdbc 中如何使用事务
 */
public class Transaction_ {

    //没有使用事务.
    @Test
    public void noTransaction() {

        //操作转账的业务
        //1. 得到连接
        Connection connection = null;
        //2. 组织一个sql
        String sql = "update account set balance = balance - 100 where id = 1";
        String sql2 = "update account set balance = balance + 100 where id = 2";
        PreparedStatement preparedStatement = null;
        //3. 创建PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection(); // 在默认情况下,connection是默认自动提交
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第1条sql

            int i = 1 / 0; //抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第3条sql


        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }

    //事务来解决
    @Test
    public void useTransaction() {

        //操作转账的业务
        //1. 得到连接
        Connection connection = null;
        //2. 组织一个sql
        String sql = "update account set balance = balance - 100 where id = 1";
        String sql2 = "update account set balance = balance + 100 where id = 2";
        PreparedStatement preparedStatement = null;
        //3. 创建PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection(); // 在默认情况下,connection是默认自动提交
            //将 connection 设置为不自动提交
            connection.setAutoCommit(false); //开启了事务
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第1条sql

            int i = 1 / 0; //抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第3条sql

            //这里提交事务
            connection.commit();

        } catch (SQLException e) {
            //这里我们可以进行回滚,即撤销执行的SQL
            //默认回滚到事务开始的状态.
            System.out.println("执行发生了异常,撤销执行的sql");
            try {
                connection.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}

批处理

package com.hspedu.jdbc.batch_;

import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author zfc
 * @version 1.0
 * 演示java的批处理
 */
public class Batch_ {

    //传统方法,添加5000条数据到admin2

    @Test
    public void noBatch() throws Exception {

        Connection connection = JDBCUtils.getConnection();
        String sql = "insert into admin2 values(null, ?, ?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        System.out.println("开始执行");
        long start = System.currentTimeMillis();//开始时间
        for (int i = 0; i < 5000; i++) {//5000执行
            preparedStatement.setString(1, "jack" + i);
            preparedStatement.setString(2, "666");
            preparedStatement.executeUpdate();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统的方式 耗时=" + (end - start));//传统的方式 耗时=10702
        //关闭连接
        JDBCUtils.close(null, preparedStatement, connection);
    }

    //使用批量方式添加数据
    @Test
    public void batch() throws Exception {

        Connection connection = JDBCUtils.getConnection();
        String sql = "insert into admin2 values(null, ?, ?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        System.out.println("开始执行");
        long start = System.currentTimeMillis();//开始时间
        for (int i = 0; i < 5000; i++) {//5000执行
            preparedStatement.setString(1, "jack" + i);
            preparedStatement.setString(2, "666");
            //将sql 语句加入到批处理包中 -> 看源码
            /*
            //1. //第一就创建 ArrayList - elementData => Object[]
            //2. elementData => Object[] 就会存放我们预处理的sql语句
            //3. 当elementData满后,就按照1.5扩容
            //4. 当添加到指定的值后,就executeBatch
            //5. 批量处理会减少我们发送sql语句的网络开销,而且减少编译次数,因此效率提高
            public void addBatch() throws SQLException {
                synchronized(this.checkClosed().getConnectionMutex()) {
                    if (this.batchedArgs == null) {

                        this.batchedArgs = new ArrayList();
                    }

                    for(int i = 0; i < this.parameterValues.length; ++i) {
                        this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
                    }

                    this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues,          this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
                }
            }

             */
            preparedStatement.addBatch();
            //当有1000条记录时,在批量执行
            if((i + 1) % 1000 == 0) {//满1000条sql
                preparedStatement.executeBatch();
                //清空一把
                preparedStatement.clearBatch();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("批量方式 耗时=" + (end - start));//批量方式 耗时=108
        //关闭连接
        JDBCUtils.close(null, preparedStatement, connection);
    }
}

传统获取Connection问题分析

传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频容易造成服务器崩溃。繁的进行数据库连接操作将占用很多的系统资源,每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。

传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)

数据库连接池

1.数据库连接池基本介绍预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从““缓冲池”中取出一个,使用完毕之后再放回去。

2.数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

3.当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中

1.JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现

2.C3P0 数据库连接池,速度相对较慢,稳定性不错(hibernate, spring)

3.DBCP数据库连接池,速度相对c3p0较快,但不稳定

4.Proxool数据库连接池,有监控连接池状态的功能,稳定性较

5.c3p0差一点BoneCP 数据库连接池,速度快

6.Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxoo优点于一身的数据库连接池

package com.zfc.mysql;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class C3p0 {
    @Test
    public void testC3P0_01() throws Exception {

        //1. 创建一个数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //2. 通过配置文件mysql.properties 获取相关连接的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //读取相关的属性值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        //给数据源 comboPooledDataSource 设置相关的参数
        //注意:连接管理是由 comboPooledDataSource 来管理
        comboPooledDataSource.setDriverClass(driver);
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);

        //设置初始化连接数
        comboPooledDataSource.setInitialPoolSize(10);
        //最大连接数
        comboPooledDataSource.setMaxPoolSize(50);
        //测试连接池的效率, 测试对mysql 5000次操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            Connection connection = comboPooledDataSource.getConnection(); //这个方法就是从 DataSource 接口实现的
            //System.out.println("连接OK");
            connection.close();
        }
        long end = System.currentTimeMillis();
        //c3p0 5000连接mysql 耗时=391
        System.out.println("c3p0 5000连接mysql 耗时=" + (end - start));

    }

    //第二种方式 使用配置文件模板来完成

    //1. 将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下
    //2. 该文件指定了连接数据库和连接池的相关参数
    @Test
    public void testC3P0_02() throws SQLException {

        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_edu");

        //测试5000次连接mysql
        long start = System.currentTimeMillis();
        System.out.println("开始执行....");
        for (int i = 0; i < 500000; i++) {
            Connection connection = comboPooledDataSource.getConnection();
            //System.out.println("连接OK~");
            connection.close();
        }
        long end = System.currentTimeMillis();
        //c3p0的第二种方式 耗时=413
        System.out.println("c3p0的第二种方式(500000) 耗时=" + (end - start));//1917

    }

}

c3p0-config.xml

<c3p0-config>
    <!-- 数据源名称代表连接池 -->
  <named-config name="zfc">
<!-- 驱动类 -->
  <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
  <!-- url-->
  	<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/zfc_db01</property>
  <!-- 用户名 -->
  		<property name="user">root</property>
  		<!-- 密码 -->
  	<property name="password">123456</property>
  	<!-- 每次增长的连接数-->
    <property name="acquireIncrement">5</property>
    <!-- 初始的连接数 -->
    <property name="initialPoolSize">10</property>
    <!-- 最小连接数 -->
    <property name="minPoolSize">5</property>
   <!-- 最大连接数 -->
    <property name="maxPoolSize">50</property>

	<!-- 可连接的最多的命令对象数 -->
    <property name="maxStatements">5</property> 
    
    <!-- 每个连接对象可连接的最多的命令对象数 -->
    <property name="maxStatementsPerConnection">2</property>
  </named-config>
</c3p0-config>

德鲁伊连接池

package com.zfc.mysql;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.jupiter.api.Test;

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

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class Druid_ {
    @Test
    public void testDruid() throws Exception {
        //1. 加入 Druid jar包
        //2. 加入 配置文件 druid.properties , 将该文件拷贝项目的src目录
        //3. 创建Properties对象, 读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));

        //4. 创建一个指定参数的数据库连接池, Druid连接池
        DataSource dataSource =
                DruidDataSourceFactory.createDataSource(properties);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            Connection connection = dataSource.getConnection();
            System.out.println(connection.getClass());
            //System.out.println("连接成功!");
            connection.close();
        }
        long end = System.currentTimeMillis();
        //druid连接池 操作5000 耗时=412
        System.out.println("druid连接池 操作500000 耗时=" + (end - start));//539

    }
}

druid.properties

#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/zfc_db01?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/girls
username=root
password=123456
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000

JDBCUtilsByDruid

package com.zfc.mysql;

import com.alibaba.druid.pool.DruidDataSourceFactory;

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

/**
 * @author zfc
 * @version 1.0
 * 基于druid数据库连接池的工具类
 */
@SuppressWarnings({"all"})
public class JDBCUtilsByDruid {

    private static DataSource ds;

    //在静态代码块完成 ds初始化
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src\\druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    //关闭连接, 老师再次强调: 在数据库连接池技术中,close 不是真的断掉连接
    //而是把使用的Connection对象放回连接池
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {

        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Apache-Dbutils

package com.hspedu.jdbc.datasource;


import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.jupiter.api.Test;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zfc
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class DBUtils_USE {

    //使用apache-DBUtils 工具类 + druid 完成对表的crud操作
    @Test
    public void testQueryMany() throws SQLException { //返回结果是多行的情况

        //1. 得到 连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project
        //3. 创建 QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //4. 就可以执行相关的方法,返回ArrayList 结果集
        //String sql = "select * from actor where id >= ?";
        //   注意: sql 语句也可以查询部分列
        String sql = "select id, name from actor where id >= ?";
        // 
        //(1) query 方法就是执行sql 语句,得到resultset ---封装到 --> ArrayList 集合中
        //(2) 返回集合
        //(3) connection: 连接
        //(4) sql : 执行的sql语句
        //(5) new BeanListHandler<>(Actor.class): 在将resultset -> Actor 对象 -> 封装到 ArrayList
        //    底层使用反射机制 去获取Actor 类的属性,然后进行封装
        //(6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数Object... params
        //(7) 底层得到的resultset ,会在query 关闭, 关闭PreparedStatment
        /**
         * 分析 queryRunner.query方法:
         * public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
         *         PreparedStatement stmt = null;//定义PreparedStatement
         *         ResultSet rs = null;//接收返回的 ResultSet
         *         Object result = null;//返回ArrayList
         *
         *         try {
         *             stmt = this.prepareStatement(conn, sql);//创建PreparedStatement
         *             this.fillStatement(stmt, params);//对sql 进行 ? 赋值
         *             rs = this.wrap(stmt.executeQuery());//执行sql,返回resultset
         *             result = rsh.handle(rs);//返回的resultset --> arrayList[result] [使用到反射,对传入class对象处理]
         *         } catch (SQLException var33) {
         *             this.rethrow(var33, sql, params);
         *         } finally {
         *             try {
         *                 this.close(rs);//关闭resultset
         *             } finally {
         *                 this.close((Statement)stmt);//关闭preparedstatement对象
         *             }
         *         }
         *
         *         return result;
         *     }
         */
        List<Actor> list =
                queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
        System.out.println("输出集合的信息");
        for (Actor actor : list) {
            System.out.print(actor);
        }


        //释放资源
        JDBCUtilsByDruid.close(null, null, connection);

    }

    //演示 apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
    @Test
    public void testQuerySingle() throws SQLException {

        //1. 得到 连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project
        //3. 创建 QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //4. 就可以执行相关的方法,返回单个对象
        String sql = "select * from actor where id = ?";
       
        // 因为我们返回的单行记录<--->单个对象 , 使用的Hander 是 BeanHandler
        Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 10);
        System.out.println(actor);

        // 释放资源
        JDBCUtilsByDruid.close(null, null, connection);

    }

    //演示apache-dbutils + druid 完成查询结果是单行单列-返回的就是object
    @Test
    public void testScalar() throws SQLException {

        //1. 得到 连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project
        //3. 创建 QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        //4. 就可以执行相关的方法,返回单行单列 , 返回的就是Object
        String sql = "select name from actor where id = ?";
        //老师解读: 因为返回的是一个对象, 使用的handler 就是 ScalarHandler
        Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 4);
        System.out.println(obj);

        // 释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }

    //演示apache-dbutils + druid 完成 dml (update, insert ,delete)
    @Test
    public void testDML() throws SQLException {

        //1. 得到 连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2. 使用 DBUtils 类和接口 , 先引入DBUtils 相关的jar , 加入到本Project
        //3. 创建 QueryRunner
        QueryRunner queryRunner = new QueryRunner();

        //4. 这里组织sql 完成 update, insert delete
        //String sql = "update actor set name = ? where id = ?";
        //String sql = "insert into actor values(null, ?, ?, ?, ?)";
        String sql = "delete from actor where id = ?";

        //解读
        //(1) 执行dml 操作是 queryRunner.update()
        //(2) 返回的值是受影响的行数 (affected: 受影响)
        //int affectedRow = queryRunner.update(connection, sql, "林青霞", "女", "1966-10-10", "116");
        int affectedRow = queryRunner.update(connection, sql, 1000 );
        System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到表");

        // 释放资源
        JDBCUtilsByDruid.close(null, null, connection);

    }
}

BasicDao

1.apache-dbutils+Druid 简化了JDBC开发,但还有不足:SQL语句是固定,不能通过参数传入,通用性不好,需要改进行改进,更方便执行 增删改查

2.对于select 操作,如果有返回值,返回类型不能固定,需要使用泛型

3.将来的表很多,业务需求复杂,不可能只靠一个Java类完成

这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作

在BaiscDao 的基础上,实现一张表 对应一个Dao,更好的完成功能,比如 Customer表

正则表达式