Java编程基础

1,基本概念

(1)JDK、JRE、JVM的关系:

  • JDK:Java Development Kit,Java开发工具包
  • JRE: Java Runtime Environment,Java运行环境
  • JVM:Java Virtual Machine,Java虚拟机
  • JDK包含JRE,JRE包含JVM

(2)JDK版本选择

  • 目前JDK1.8(也叫JDK8,注意不是JDK18)用得最多

(3)Java代码的编译运行流程

(4)JSE、JEE、JME的区别

  • JSE: Java Standard Edition,标准版
  • JEE:Java Enterprise Edition,企业版
  • JME: Java Mirco Edition,移动版
  • Spring是JEE的轻量级替代品
  • SpringBoot是Spring + 自动化配置

2,环境搭建

参考安装JDK

3,Java基础语法

  • 单行注释 
    //这是单行注释
  • 多行注释
/*
这是
多行注释
*/
  • 特殊注释
/**
*可以用来
*自动创建文档的注释
*@author wyf
*/

详情可参考javadoc生成文档        使用IDEA生成javadoc文档

(1)数据类型

   a,基本数据类型

  • 整数类型:byte,short,int,long

        各种整型能表示的最大范围如下:

        byte:-128 ~ 127

        short: -32768 ~ 32767

        int: -2147483648 ~ 2147483647

        long: -9223372036854775808 ~ 9223372036854775807

//定义整型
public class Main {
    public static void main(String[] args) {
        byte b = 123;
		System.out.println(b);  //输出123
        short s = 12345;
		System.out.println(s);  //输出12345
        int i = 2147483647;   
		System.out.println(i);  //输出2147483647 
        int i2 = -2147483648;  
		System.out.println(i2);  //输出-2147483648
        int i3 = 2_000_000_000; // 加下划线更容易识别
		System.out.println(i3);  //输出2000000000
        int i4 = 0xff0000; // 十六进制表示的16711680
		System.out.println(i4);  //输出16711680
        int i5 = 0b1000000000; // 二进制表示的512
		System.out.println(i5);  //输出512
        long l = 9000000000000000000L; // long型的结尾需要加L
		System.out.println(l);  //输出9000000000000000000
//特别注意:同一个数的不同进制的表示是完全相同的,例如15=0xf=0b1111
    }
}
  • 浮点数类型:float,double

        各种浮点数能表示的最大范围如下:

        float:3.4x10^{38}

        double:1.79x10^{308}

//对于float类型,需要加上f后缀
float f1 = 3.14f;
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324
double d4 = 3.73d;  //也可以在最后加上后缀d
  • 字符类型:char

//字符类型char表示一个字符。Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符
public class Main {
    public static void main(String[] args) {
        char a = 'A';
        char zh = '中';
        System.out.println(a);
        System.out.println(zh);
        //要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:
        int n1 = 'A';
        int n2 = '中';
        //还可以直接用转义字符\u+Unicode编码来表示一个字符:
        // 注意是十六进制:
        char c3 = '\u0041';  // 'A',因为十六进制0041 = 十进制65
        char c4 = '\u4e2d';  // '中',因为十六进制4e2d = 十进制20013
    }
}
//注意char类型使用单引号',且仅有一个字符,要和双引号"的字符串类型区分开。
  • 布尔类型:boolean 

//布尔类型boolean只有true和false两个值,布尔类型总是关系运算的计算结果
boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 计算结果为true
int age = 12;
boolean isAdult = age >= 18; // 计算结果为false
//Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数。

    字节间的换算

类型字节数
byte

1

short2
int4
long8
float4
double8
char

2

boolean1

   b,引用类型

   详情参考9,常用类 String

   c,常量

//定义变量的时候,如果加上final修饰符,这个变量就变成了常量
final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
//常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。
//常量的作用是用有意义的变量名来避免魔术数字(Magic number),例如,不要在代码中到处写3.14,而是定义一个常量。如果将来需要提高计算精度,我们只需要在常量的定义处修改,例如,改成3.1416,而不必在所有地方替换3.14。
//根据习惯,常量名通常全部大写。

   d,var关键字

//有些时候,类型的名字太长,写起来比较麻烦。例如:
StringBuilder sb = new StringBuilder();
//这个时候,如果想省略变量类型,可以使用var关键字:
//var sb = new StringBuilder();
//编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder。对编译器来说,语句:
var sb = new StringBuilder();
//实际上会自动变成:
StringBuilder sb = new StringBuilder();
//因此,使用var定义变量,仅仅是少写了变量类型而已。相当于C中的auto

(2)运算

  基本和C差不多的,可以参考  

   整数运算

   浮点数运算

   布尔运算 

//类型转化
int x = (int)'A';  //显式转化(强转)
double x = 12, y = 4 * 3.3;  //隐式转化(自动转化)
//表达式
//与C++、Python3类似:
int a = 1, b = 2, c = 3;
int x = (a + b) * c;
x ++;

(3)流程控制

   a,输入

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 创建Scanner对象
        System.out.print("Input your name: "); // 打印提示
        String name = scanner.nextLine(); // 读取一行输入并获取字符串
        /*
        String name = scanner.next();  //读取下一个字符串(即遇空格自动结束)
        */
        System.out.print("Input your age: "); // 打印提示
        int age = scanner.nextInt(); // 读取一行输入并获取整数
        System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
        /*
        float y = sc.nextFloat();  // 读入下一个单精度浮点数
        double z = sc.nextDouble();  // 读入下一个双精度浮点数
        */
    }
}

Scanner一点补充

   b,输出

System.out.println(123);  // 输出整数 + 换行
System.out.println("Hello World");  // 输出字符串 + 换行
System.out.print(123);  // 输出整数
System.out.print("yxc\n");  // 输出字符串
System.out.printf("%04d %.2f\n", 4, 123.456D);  // 格式化输出,float与double都用%f输出
System.out.println();  //实行换行,类似于\n和endl;

占位符

    c,if判断

注意字符串相等比较以及浮点数判断大小

    d,switch多重选择 

注意新增内容:switch表达式(对版本有要求)

    e,while循环

注意新增的内容:java中的int有最大值,死循环也会结束

     f,do while循环

do while循环

     g,for循环 

尤其注意for each循环

     h,break和continue

break和continue

4,数组

(1)初始化

int[] a = new int[5];  //初始化长度为5的int数组,初始值为0
int n = 10;
float[] b = new float[n];
char[] c = {'a', 'b', 'c'};  //初始化长度为3的char数组,初始值为:'a', 'b', 'c'
char[] d = c;  //d与c地址相同,更改c中的元素,d中的元素也会改变
int ns = new int[] { 68, 79, 91, 85, 62 };

 (2)遍历数组

 (3)数组排序

 (4)多维数组

int[][] a = new int[2][3];
a[1][2] = 1;
int[][] b = {
        {1, 2, 3},
        {4, 5, 6},
};

三维数组

 (5)命令行参数

命令行参数

 (6)常用API

  • 属性length:返回数组长度,注意不加小括号
  • Arrays.toString():将数组转化为字符串 
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] ns = { 1, 1, 2, 3, 5, 8 };
        System.out.println(Arrays.toString(ns));  //可快速打印数组内容
    }
}
  • Arrays.sort():数组排序
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
        Arrays.sort(ns);
        System.out.println(Arrays.toString(ns));
    }
}
  •  Arrays.deepToString():将多维数组转化为字符串
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] ns = {
            { 1, 2, 3, 4 },
            { 5, 6, 7, 8 },
            { 9, 10, 11, 12 }
        };
        System.out.println(Arrays.deepToString(ns));
    }
}
  • Arrays.fill(int[] a, int val):填充数组 
import java.util.Arrays;  //注意哪个包
  
public class FillTest {
    public static void main(String args[]) {
        int array[] = new int[6];
        Arrays.fill(array, 100);  //全部赋值
        for (int i = 0, n = array.length; i < n; i++) {
            System.out.println(array[i]);
        }
 
        System.out.println("***********");
        
        Arrays.fill(array, 3, 6, 50);  //左闭右开
        for (int i = 0, n = array.length; i < n; i++) {
            System.out.println(array[i]);
        }
    }
}
  • 数组不可变长

5,面向对象

重点理解public private的区别

  • 方法

注意private和public方法的调用方法以及String...的用法和数组的可变性(参数绑定)

    a,构造方法

    为什么有构造方法及用法

  • 重载

什么是方法的重载及使用规范

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        Person hong = new Person();
        ming.setName("Xiao Ming");
        // TODO: 给Person增加重载方法setName(String, String):
        hong.setName("Xiao", "Hong");
        System.out.println(ming.getName());
        System.out.println(hong.getName());
    }
}

class Person {
    private String name;

    public String getName() {
        return name;
    }

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

    public void setName(String firstname,String lastname){
        name = firstname + " " + lastname;
    }
}
  • 封装(作用域)

   封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装是一种信息隐藏技术,在java中通过关键字private,protected和public实现封装。什么是封装?封装把对象的所有组成部分组合在一起,封装定义程序如何引用对象的数据,封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。 适当的封装可以让程式码更容易理解和维护,也加强了程式码的安全性。

  封装

  • 继承

protected super的用法及原理,什么是上下转型  继承与组合,了解阻止继承

弄清上下转型最主要的是弄清什么是变量什么是实例,以及变量指向实例,方法是变量的方法,类似于自动转换和强制转换  

向上转型和向下转型的意义

  • 多态

什么是重写,什么是多态(注意税务的例子),final  super的作用

为了规范,要重写统一注释@Override 

6,抽象类

什么是抽象类,为什么要用抽象类

7,接口

接口的实现意义,接口和抽象类的区别,接口的继承   default方法

8,函数(静态方法) 

静态字段和静态方法,规范写法

静态方法常用于工具类和辅助方法 

9,枚举

主要注意各种用法和toString在enum中的特殊性

10,常用类

(1)String

   属于引用类型

  a,初始化操作

String s = "hello";  //引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置
//特殊符号需要借助转义字符
String s = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z
String str = String.format("My age is %d", 18);  // 格式化字符串,类似于C++中的sprintf
String money_str = "123.45";
double money = Double.parseDouble(money_str);  // String转double

  b, 常见的转义字符

\""
\''
\\\
\n换行
\r回车
\tTab
\u####表示一个Unicode编码的字符

  c,字符串连接

//Java的编译器对字符串做了特殊照顾,可以使用+连接任意字符串和其他数据类型,这样极大地方便了字符串的处理
public class Main {
    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "world";
        String s = s1 + " " + s2 + "!";
        System.out.println(s);
    }
}
//如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接
public class Main {
    public static void main(String[] args) {
        int age = 25;
        String s = "age is " + age;  //注意至少一项是字符串类型的才能转换
        System.out.println(s);
    }
}


//错误:

public class Main {
    public static void main(String[] args) {
        // 请将下面一组int值视为字符的Unicode码,把它们拼成一个字符串:
        int a = 72;
        int b = 105;
        int c = 65281;
        // FIXME:
        String s = a + b + c;
        // FIX:
        String s = "" + a + b + c;
        System.out.println(s);
    }
}

  d,多行字符串 

了解即可

  e,不可变特性 

   f,空值null

//引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象
String s1 = null; // s1是null
String s2; // 没有赋初值值,s2也是null
String s3 = s1; // s3也是null
String s4 = ""; // s4指向空字符串,不是null
//注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null

    g,访问String中的字符

String str = "Hello World";
for (int i = 0; i < str.length(); i ++ ) {
    System.out.print(str.charAt(i));  // 只能读取,不能写入
}
//charAt(int index)方法是一个能够用来检索特定索引下的字符的String实例的方法。
//charAt()方法返回指定索引位置的char值。索引范围为0~length()-1,如: str.charAt(0)检索str中的第一个字符,str.charAt(str.length()-1)检索最后一个字符。

     h,常用API

  • equals():判断两个字符串是否相等,注意不能直接用== 
  • 要忽略大小写比较,使用equalsIgnoreCase()方法 
  • // 是否包含子串:
    "Hello".contains("ll"); // true
  • indexOf(char c)indexOf(String str):查找,找不到返回-1       
"Hello".indexOf("l"); // 2
"Hello".lastIndexOf("l"); // 3
/*
int indexOf(int ch):根据字符的Unicode码查找;

int indexOf(String str):根据字符串查找;

int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;

int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。
*/

  • "Hello".lastIndexOf("l"); // 3
  • startsWith():判断是否以某个前缀开头
"Hello".startsWith("He"); // true
  • endsWith():判断是否以某个后缀结尾
"Hello".endsWith("lo"); // true
  • substring(int beginIndex, int endIndex):返回[beginIndex, endIndex)中的子串
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); "ll"
//注意索引号是从0开始的
  • toLowerCase():全部用小写字符
  • toUpperCase():全部用大写字符
  • trim():去掉首尾的空白字符
//使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n
"  \tHello\r\n ".trim(); // "Hello"
//注意:trim()并没有改变字符串的内容,而是返回了一个新字符串
  • //另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是,类似中文的空格字符\u3000也会被移除
    "\u3000Hello\u3000".strip(); // "Hello"
    " Hello ".stripLeading(); // "Hello "   仅删除字符串开头所有空格
    " Hello ".stripTrailing(); // " Hello"   仅删除字符串结尾所有空格
  • //String还提供了isEmpty()和isBlank()来判断字符串是否为空和空白字符串
    "".isEmpty(); // true,因为字符串长度为0
    "  ".isEmpty(); // false,因为字符串长度不为0
    "  \n".isBlank(); // true,因为只包含空白字符
    " Hello ".isBlank(); // false,因为包含非空白字符
  • replace(char oldChar, char newChar):替换字符
  • replace(String oldRegex, String newRegex):替换字符串
//根据字符或字符串替换
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"
//通过正则表达式替换
String s = "A,,B;C ,D";
s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"
  • split(String regex):分割字符串
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}

split函数用法

  • 拼接字符串 
//拼接字符串使用静态方法join(),它用指定的字符串连接字符串数组:
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"
  • 格式化字符串 
//字符串提供了formatted()方法和format()静态方法,可以传入其他参数,替换占位符,然后生成新的字符串
public class Main {
    public static void main(String[] args) {
        String s = "Hi %s, your score is %d!";
        System.out.println(s.formatted("Alice", 80));
        System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5));
    }
}
  • 类型转换
//要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()。这是一个重载方法,编译器会根据参数自动选择合适的方法
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c
//把字符串转换为int类型
int n1 = Integer.parseInt("123"); // 123
int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255
//把字符串转换为boolean类型
boolean b1 = Boolean.parseBoolean("true"); // true
boolean b2 = Boolean.parseBoolean("FALSE"); // false
//要特别注意,Integer有个getInteger(String)方法,它不是将字符串转换为int,而是把该字符串对应的系统变量转换为Integer
Integer.getInteger("java.version"); // 版本号,11
  • 转换为char[]
//String和char[]类型可以互相转换
char[] cs = "Hello".toCharArray(); // String -> char[]
String s = new String(cs); // char[] -> String
//如果修改了char[]数组,String并不会改变
public class Main {
    public static void main(String[] args) {
        char[] cs = "Hello".toCharArray();
        String s = new String(cs);
        System.out.println(s);
        cs[0] = 'X';
        System.out.println(s);
    }
}

和数组.length区分,一个有括号一个没有

  • compareTo():判断两个字符串的字典序大小,负数表示小于,0表示相等,正数表示大于

(2)StringBuilder

String不能被修改,如果打算修改字符串,可以使用StringBuilder和StringBuffer。

StringBuffer线程安全,速度较慢;StringBuilder线程不安全,速度较快。

StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。

StringBuilder sb = new StringBuilder("Hello ");  // 初始化
sb.append("World");  // 拼接字符串
System.out.println(sb);

for (int i = 0; i < sb.length(); i ++ ) {
    sb.setCharAt(i, (char)(sb.charAt(i) + 1));  // 读取和写入字符
}

System.out.println(sb);

StringBuilder应用

import java.util.*;


public class Main {
    public static void main(String[] args) {
        String[] fields = {"name", "position", "salary"};
        String table = "employee";
        String insert = buildInsertSql(table, fields);
        System.out.println(insert);
        String s = "INSERT INTO employee (name, position, salary) VALUES (?, ?, ?)";
        System.out.println(s.equals(insert) ? "测试成功" : "测试失败");
    }
    static String buildInsertSql(String table, String[] fields) {
        StringBuilder sql = new StringBuilder(1024);
        String[] values = new String[fields.length];
        Arrays.fill(values, "?");

        sql.append("INSERT INTO ")
                .append(table)
                .append(" (")
                .append(String.join(", ", fields))
                .append(") VALUES (")
                .append(String.join(", ", values))
                .append(")");

        return sql.toString();
    }
}

常用字符串

  • reverse():翻转字符串

(2)常用工具类

   a, Math

//求绝对值
Math.abs(-100); // 100
Math.abs(-7.8); // 7.8
//取最大或最小值
Math.max(100, 99); // 100
Math.min(1.2, 2.3); // 1.2
//计算xy次方
Math.pow(2, 10); // 2的10次方=1024
//计算√x
Math.sqrt(2); // 1.414...
//计算ex次方
Math.exp(2); // 7.389...
//计算以e为底的对数
Math.log(4); // 1.386...
//计算以10为底的对数
Math.log10(100); // 2
//三角函数
Math.sin(3.14); // 0.00159...
Math.cos(3.14); // -0.9999...
Math.tan(3.14); // -0.0015...
Math.asin(1.0); // 1.57079...
Math.acos(1.0); // 0.0
//Math还提供了几个数学常量
double pi = Math.PI; // 3.14159...
double e = Math.E; // 2.7182818...
Math.sin(Math.PI / 6); // sin(π/6) = 0.5
//生成一个随机数x,x的范围是0 <= x < 1
Math.random(); // 0.53907... 每次都不一样
//如果我们要生成一个区间在[MIN, MAX)的随机数,可以借助Math.random()实现,计算如下:
//区间在[MIN, MAX)的随机数
public class Main {
    public static void main(String[] args) {
        double x = Math.random(); // x的范围是[0,1)
        double min = 10;
        double max = 50;
        double y = x * (max - min) + min; // y的范围是[10,50)
        long n = (long) y; // n的范围是[10,50)的整数
        System.out.println(y);
        System.out.println(n);
    }
}

   b,random

//Random用来创建伪随机数。所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的
//要生成一个随机数,可以使用nextInt()、nextLong()、nextFloat()、nextDouble()
Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
//每次运行程序,生成的随机数都是不同的,没看出伪随机数的特性来
//这是因为我们创建Random实例时,如果不给定种子,就使用系统当前时间戳作为种子,因此每次运行时,种子不同,得到的伪随机数序列就不同。
//如果我们在创建Random实例时指定一个种子,就会得到完全确定的随机数序列:
import java.util.Random;
public class Main {
    public static void main(String[] args) {
        Random r = new Random(12345);
        for (int i = 0; i < 10; i++) {
            System.out.println(r.nextInt(100));
        }
        // 51, 80, 41, 28, 55...
    }
}
//前面我们使用的Math.random()实际上内部调用了Random类,所以它也是伪随机数,只是我们无法指定种子

   c,SecureRandom

 生成安全的随机数,密码学

(3)日期时间 

获取时间戳

   a,Date(旧的时间API)

//Date基本用法
import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        System.out.println(date.getYear() + 1900); // 必须加上1900
        System.out.println(date.getMonth() + 1); // 0~11,必须加上1
        System.out.println(date.getDate()); // 1~31,不能加1
        // 转换为String:
        System.out.println(date.toString());
        // 转换为GMT时区:
        System.out.println(date.toGMTString());
        // 转换为本地时区:
        System.out.println(date.toLocaleString());
    }
}
/*
输出:
2022
7
20
Wed Jul 20 07:58:42 UTC 2022
20 Jul 2022 07:58:42 GMT
Jul 20, 2022, 7:58:42 AM
*/

  打印本地时区表示的日期和时间时,不同的计算机可能会有不同的结果。如果我们想要针对用户的偏好精确地控制日期和时间的格式,就可以使用SimpleDateFormat对一个Date进行转换。它用预定义的字符串表示格式化

yyyy

MM
dd
HH小时
mm分钟
ss
import java.text.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(date));
    }
}

/*
输出:
2022-07-20 08:05:07
*/
//Java的格式化预定义了许多不同的格式,我们以MMM和E为例
import java.text.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        var sdf = new SimpleDateFormat("E MMM dd, yyyy");  //E是显示星期几
        System.out.println(sdf.format(date));
    }
}
/*
输出:
Wed Jul 20, 2022
*/

  上述代码在不同的语言环境会打印出类似Sun Sep 15, 2019这样的日期。可以从JDK文档查看详细的格式说明。一般来说,字母越长,输出越长。以M为例,假设当前月份是9月:

  • M:输出9
  • MM:输出09
  • MMM:输出Sep
  • MMMM:输出September

 Date对象有几个严重的问题:它不能转换时区,除了toGMTString()可以按GMT+0:00输出外,Date总是以当前计算机系统的默认时区为基础进行输出。此外,我们也很难对日期和时间进行加减,计算两个日期相差多少天,计算某个月第一个星期一的日期等

   b,Calendar

11,集合类

  左边是单例集合,右边是双例集合

1,Collection方法

a, Collection接口和常用方法

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String args[]) {
        List list = new ArrayList();
        //add : 添加单个元素
        list.add("jack");
        list.add(10);
        list.add(true);
        System.out.println("list=" + list);  //list.add(new Integer(10))
        //remove : 删除指定元素
        //list.remove(0);  //删除第一个元素
        list.remove("jack");  //指定删除某个元素
        list.remove(true);
        System.out.println("list=" + list);
        //contains : 查找元素是否存在
        System.out.println(list.contains("jack"));
        //size : 获取元素个数
        System.out.println(list.size());
        //isEmpty : 判断是否为空
        System.out.println(list.isEmpty());
        //clear : 清空
        list.clear();
        System.out.println("list=" + list);
        //addAll : 添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("红楼梦");
        list2.add("三国演义");
        list.addAll(list2);
        System.out.println("list=" + list);
        //containsAll : 查找多个元素是否都存在
        System.out.println(list.containsAll(list2));
        //removeAll : 删除多个元素
        list.add("聊斋");
        list.removeAll(list2);
        System.out.println("list=" + list);
        //说明 : 以ArrayList实现类来演示
    }
}

b, 迭代器遍历

  

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

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

       Collection col = new ArrayList();

       col.add(new Book("三国演义","罗贯中",10.1));
       col.add(new Book("小李飞刀","古龙",5.1));
       col.add(new Book("红楼梦","曹雪芹",34.6));

       System.out.println("col=" + col);
        //现在希望遍历col集合
        //1,先得到col对应的迭代器
        Iterator iterator = col.iterator();
        //2,使用while循环遍历
        while(iterator.hasNext()) {  //判断是否还有数据
            //返回下一个元素,类型是Object
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }
        //3,当退出while循环后,这时iterator迭代器,指向最后的元素
        //iterator.next();  //NoSuchElementException
        //4,如果希望再次遍历,需要重置我们的迭代器
        iterator = col.iterator();
        System.out.println("===第二次遍历===");
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }
    }
}
class Book{
    private String name;
    private String author;
    private double price;

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

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

c, 集合增强for

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

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

       Collection col = new ArrayList();

       col.add(new Book("三国演义","罗贯中",10.1));
       col.add(new Book("小李飞刀","古龙",5.1));
       col.add(new Book("红楼梦","曹雪芹",34.6));

        //1,使用增强for,在Collection集合
        //2,增强for,底层仍是迭代器
        //3,增强for可以理解成就是简化版本的 迭代器遍历
        for (Object book : col){
            System.out.println("book=" + book);
        }
        //增强for,也可以直接在数组使用
        int[] nums = {1,8,10,90};
        for (int i : nums){
            System.out.println("i=" + i);
        }
    }
}
class Book{
    private String name;
    private String author;
    private double price;

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

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

测试题:

//1,创建3个Dog{name,age}对象,放入到ArrayList中,赋给List引用
//2,用迭代器和增强for循环两种方式来遍历
//3,重写Dog的toString方法,输出name和age

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {
    public static void main(String args[]) {
        List list = new ArrayList();
        list.add(new Dog("狗",1));
        list.add(new Dog("猫",2));
        list.add(new Dog("龟",5));
        System.out.println("===迭代器遍历===");
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
        System.out.println("===增强for循环遍历===");
        for (Object obj : list)  System.out.println(obj);
    }
}
class Dog{
    private String name;
    private int age;

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

    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;
    }

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

 2,List接口方法

a, List接口和常用方法

//List接口和常用方法

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String args[]) {
        List list = new ArrayList();
        list.add("张三丰");
        list.add("贾宝玉");
        //void add(int index, Object ele):在index位置插入ele元素
        //在index = 1的位置插入一个对象
        list.add(1,"啦啦啦");
        System.out.println("list=" + list);
        //boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
        List list2 = new ArrayList();
        list2.add("jack");
        list2.add("tom");
        list.addAll(1,list2);
        System.out.println("list=" + list);
        //Object get(int index):获取指定index位置的元素
        //int indexOf(Object obj):返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("tom"));  //2
        //int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
        list.add("啦啦啦");
        System.out.println("list=" + list);
        System.out.println(list.lastIndexOf("啦啦啦"));
        //Object remove(int index):移除指定index位置的元素,并返回此元素
        list.remove(0);
        System.out.println("list=" + list);
        //Object set(int index, Object ele):设置指定index位置的元素为ele。相当于是替换
        list.set(1,"玛丽");
        System.out.println("list=" + list);
        //List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
        //注意返回的子集合左闭右开
        List returnlist = list.subList(0,2);
        System.out.println("returnlist=" + returnlist);
    }
}
//添加10个以上的元素(比如String "hello"),在2号位插入一个元素"啦啦啦",获得第5个元素,删除第6个元素,修改第7个元素,再使用迭代器遍历集合,要求:使用List的实现类ArrayList完成

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {
    public static void main(String args[]) {
        List list = new ArrayList();
        list.add("hello");
        list.add("hi");
        list.add("hhh");
        list.add("eee");
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        list.add("fff");
        list.add("ggg");
        list.add("iii");
        list.add(2,"啦啦啦");
        System.out.println("这是第5个元素:" + list.get(4));
        list.remove(5);
        list.set(6,"已修改");
        Iterator iterator = list.iterator();
        while(iterator.hasNext())
        {
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}

import java.util.ArrayList;
import java.util.List;

//使用List的实现类添加三本图书,并遍历,打印如下效果
/*
名称: xx              价格: xx              作者:xx
名称: xx              价格: xx              作者:xx
名称: xx              价格: xx              作者:xx
 */
//要求
//1)按价格排序,从低到高(使用冒泡法)
//2)要求使用ArrayList,LinkedList和Vector三种集合实现

public class Main{
    public static void main(String args[]){
        List list = new ArrayList();
        list.add(new Book("西游记",20.6,"吴承恩"));
        list.add(new Book("红楼梦",13.9,"曹雪芹"));
        list.add(new Book("水浒传",24.6,"施耐庵"));
        for (int i=0;i<list.size()-1;i++)
        {
            for (int j=i+1;j<list.size();j++)
            {
                //注意要匹配类型,强转,不可以直接写list.get(i)   Object类,后面方法实现不了
                Book book1 = (Book)list.get(i);
                Book book2 = (Book)list.get(j);
                if (book1.getPrice() > book2.getPrice())
                {
                    //Book book = book1;  可以不用写,因为book1本来就是提取的,不是原本的
                    list.set(i,book2);
                    list.set(j,book1);
                }
            }
        }
        for (Object obj : list)  System.out.println(obj);
    }
}
class Book{
    private String name;
    private double price;
    private String author;

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

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "名称: " + name + "\t\t\t" +
                "价格: " + price + "\t\t\t" +
                "作者: " + author;
    }
}

 3,ArrayList注意事项

a, 扩容机制 

debug看源码(ctrl+左键)

ArrayList扩容机制底层源码

4,Vector注意事项

  5,LinkedList

public class Main{
    public static void main(String args[]){
        //模拟一个简单的双向链表
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node mary = new Node("mary");

        //连接三个结点,形成双向链表
        //jack -> tom -> mary
        jack.next = tom;
        tom.next = mary;
        //mary -> tom -> jack
        mary.pre = tom;
        tom.pre = jack;

        Node first = jack;  //让first引用指向jack,就是双向链表的头结点
        Node last = mary;  //让last引用指向mary,就是双向链表的尾结点

        //演示:从头到尾进行遍历
        System.out.println("====从头到尾的遍历====");
        while(true){
            if(first == null){
                break;
            }
            //输出first 信息
            System.out.println(first);
            first = first.next;
        }

        //演示,从尾到头的遍历
        System.out.println("====从尾到头的遍历====");
        while(true){
            if (last == null){
                break;
            }
            //输出last信息
            System.out.println(last);
            last = last.pre;
        }

        //演示链表的添加对象/数据,是多么的方便
        //要求,是在tom ---------- mary之间,插入一个对象 smith
        //1,先创建一个 Node 结点,name 就是 smith
        Node smith = new Node("smith");
        //下面就把smith加入到了双向链表了
        smith.next = mary;
        smith.pre = tom;
        mary.pre = smith;
        tom.next = smith;

        //让first再次指向jack
        first = jack;  //让first引用指向jack,就是双向链表的头结点

        System.out.println("===从头到尾进行遍历===");
        while(true){
            if (first == null){
                break;
            }
            //输出first信息
            System.out.println(first);
            first = first.next;
        }
    }
}

//定义一个Node类,Node对象表示双向链表的一个结点
class Node{
    public Object item;  //真正存放数据
    public Node next;  //指向后一个结点
    public Node pre;  //指向前一个结点
    public Node(Object name){
        this.item = name;
    }
    public String toString(){
        return "Node name=" + item;
    }
}
//LinkedList增删改查

import java.util.LinkedList;

public class Main{
    public static void main(String args[]){
        LinkedList linkedList = new LinkedList();
        for (int i=1;i<=2;i++){
            linkedList.add(i);
        }
        linkedList.add(50);
        linkedList.add(100);
        for (Object object : linkedList){
            System.out.println(object);
        }
        linkedList.remove(0);

        linkedList.set(0,"啦啦啦");
        System.out.println("===");
        for (Object object : linkedList){
            System.out.println(object);
        }
        Object object = linkedList.get(0);
        System.out.println("object=" + object);
        System.out.println(linkedList.getFirst());
        System.out.println(linkedList.getLast());
    }
}

 6, Set接口方法

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main{
    public static void main(String args[]){
        //1,以Set接口的实现类HashSet来讲解Set接口的方法
        //2,set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
        //3,set接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
        //4,注意:取出的顺序的顺序虽然不是添加的顺序,但是固定
        Set set = new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");  //重复
        set.add("jack");
        set.add(null);
        set.add(null);  //再次添加null
        for (int i=0;i<10;i++){
            System.out.println("set=" + set);
        }

        //遍历
        //方式1:使用迭代器
        System.out.println("====使用迭代器====");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }

        //方式2:增强for
        System.out.println("====增强for====");
        for (Object o : set){
            System.out.println("o=" + o);
        }

        //set接口对象,不能通过索引来获取(没有get()方法)
    }
}

7,HashSet全面说明

import java.util.HashSet;

public class Main{
    public static void main(String args[]){
        HashSet set = new HashSet();

        //说明
        //1,在执行add方法后,会返回一个boolean值
        //2,如果添加成功,返回true,否则返回false
        System.out.println(set.add("john"));  //T
        System.out.println(set.add("lucy"));  //T
        System.out.println(set.add("john"));  //F
        System.out.println(set.add("jack"));  //T
        System.out.println(set.add("Rose"));  //T

        set.remove("john");
        System.out.println("set=" + set);  //3个

        //
        set = new HashSet();
        System.out.println("set=" + set);  //0
        set.add("lucy");  //添加成功
        set.add("lucy");  //加入不了
        //地址不同
        set.add(new Dog("tom"));  //OK
        set.add(new Dog("tom"));  //OK
        System.out.println("set=" + set);

        //再加深一下,非常经典的面试题
        set.add(new String("wyf"));  //ok
        set.add(new String("wyf"));  //加入不了
        System.out.println("set=" + set);
    }
}
class Dog{  //定义了Dog类
    private String name;

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

 8,数组链表模拟 

public class Main{
    public static void main(String args[]){
        //模拟一个HashSet的底层(HashMap的底层结构)

        //1,创建一个数组,数组的类型是Node[]
        //2,有些人,直接把Node[]数组称为表
        Node[] table = new Node[16];
        System.out.println("table=" + table);
        //3,创建结点
        Node john = new Node("john",null);

        table[2] = john;
        Node jack = new Node("jack",null);
        john.next = jack;  //将jack结点挂载到john
        Node rose = new Node("Rose",null);
        jack.next = rose;  //将rose结点挂载到jack

        Node lucy = new Node("lucy",null);
        table[3] = lucy;  //把lucy放到table表的索引为3的位置
        System.out.println("table=" + table);
    }
}
class Node{  //结点,存储数据,可以指向下一个结点,从而形成链表
    Object item;  //存放数据
    Node next;  //指向下一个结点

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

 9,HashSet扩容机制

10,HashSet源码解读

 

//定义一个Employee类,该类包含:private成员属性name,age要求:
//1,创建3个Employee放入HashSet中
//2,当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中

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

public class Main{
    public static void main(String args[]){
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("Mary",23));//OK
        hashSet.add(new Employee("jack",22));//OK
        hashSet.add(new Employee("Mary",23));//NO
        System.out.println("hastSet=" + hashSet);
    }
}
class Employee{
    private String name;
    private int age;

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

    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;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //如果name 和 age 值相同,则返回相同的hash值

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

    @Override
    //如果hashcode相同但内容不同,相当于链表形式,而不是添加不进去
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
/*
定义一个Employee类,该类包含:private成员属性name,sal,birthday(MyDate类型),其中birthday为MyDate类型(属性包括year,month,day),要求:
1,创建3个Employee放入HashSet中
2,当name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中
 */

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

public class Main{
    public static void main(String args[]){
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("Mary",8456.24,new MyDate(2002,4,6)));
        hashSet.add(new Employee("Jack",8675.23,new MyDate(2002,6,4)));
        hashSet.add(new Employee("Mary",8599.45,new MyDate(2002,4,6)));
        System.out.println("hashSet=" + hashSet);
    }
}
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 +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
}
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;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) 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);
    }
}

 11,LinkedHashSet介绍

 

/*
Car类(属性:name,price),如果name和price一样,则认为是相同元素,就不能添加
 */

import java.util.LinkedHashSet;
import java.util.Objects;


public class Main{
    public static void main(String args[]){
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥拓",1000));
        linkedHashSet.add(new Car("奥迪",300000));
        linkedHashSet.add(new Car("法拉利",10000000));
        linkedHashSet.add(new Car("奥迪",300000));
        linkedHashSet.add(new Car("保时捷",70000000));
        linkedHashSet.add(new Car("奥迪",300000));
        System.out.println("linkedHashSet=" + linkedHashSet);
    }
}
class Car{
    private String name;
    private int price;

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return price == car.price && Objects.equals(name, car.name);
    }

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

 12,Map接口特点

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String args[]) {
        //Map接口实现类的特点,使用实现类HashMap
        //1,Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
        //2,Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
        //3,Map中的key不允许重复,原因和HashSet一样,前面分析过源码
        //4,Map中的value可以重复
        //5,Map的key可以为null,value也可以为null,注意key为null,只能有一个,value为null,可以多个
        //6,常用String类作为Map的key
        //7,key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
        Map map = new HashMap();
        map.put("no1", "啦啦啦");  //k-v
        map.put("no2", "张无忌");  //k-v
        map.put("no1", "张三丰");  //当有相同的k,就等价于替换
        map.put("no3", "张三丰");  //k-v
        map.put(null, null);  //k-v
        map.put(null, "abc");  //等价替换
        map.put("no4", null);  //k-v
        map.put("no5", null);  //k-v
        map.put(1, "赵敏");  //k-v
        map.put(new Object(), "金毛狮王");  //k-v

        System.out.println("map=" + map);
        //通过get方法,传入key,会返回对应的value
        System.out.println(map.get("no2"));
    }
}

  

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Main{
    public static void main(String args[]){
        Map map = new HashMap();
        map.put("no1","啦啦啦");  //k-v
        map.put("no2","张无忌");  //k-v
        map.put(new Car(), new Person());  //k-v

        //1,k-v最后是HashMap$Node node = newNode(hash, key, value, null)
        //2,k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型Entry,而一个Entry对象就有k,v EntrySet<Entry<K,V>>即: transient Set<Map.Entry<K,V>> entrySet;
        //3,entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node
        //这时因为static class Node<K,V> implements Map.Entry<K,V>
        //4,当把 HashMap$Node对象存放到entrySet就方便我们的遍历,因为Map.Entry提供了重要方法  ---> K getKey(); V getValue();

        Set set = map.entrySet();
        System.out.println(set.getClass());  //HashMap$EntrySet
        for (Object obj : set){
            //System.out.println(entry.getClass());  //HashMap$Node
            //为了从HashMap$Node取出k-v
            //1,先做一个向下转型
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }

        Set set1 = map.keySet();
        System.out.println(set1.getClass());
        Collection values = map.values();
        System.out.println(values.getClass());
    }
}
class Car{
    
}
class Person{
    
}

13,Map接口

 

import java.util.HashMap;
import java.util.Map;

public class Main{
    public static void main(String args[]){
        //演示map接口常用方法

        Map map = new HashMap();
        map.put("邓超",new Book("",100));
        map.put("邓超","孙俪");  //替换
        map.put("王宝强","马蓉");  //OK
        map.put("宋喆","马蓉");  //OK
        map.put("刘令博",null);  //OK
        map.put(null,"刘亦菲");  //OK
        map.put("鹿晗","关晓彤");  //OK
        map.put("wyf","wyf的老婆");

        System.out.println("map=" + map);

        //remove:根据键删除映射关系
        map.remove(null);
        System.out.println("map=" + map);
        //get:根据键获取值
        Object val = map.get("鹿晗");
        System.out.println("val=" + val);
        //size:获取元素个数
        System.out.println("k-v=" + map.size());
        //isEmpty:判断个数是否为0
        System.out.println(map.isEmpty());  //F
        //clear:清除k-v
        map.clear();
        System.out.println("map=" + map);
        //contains:查找键是否存在
        System.out.println("结果=" + map.containsKey("wyf"));  //F,前面clear了
    }
}

class Book{
    private String name;
    private int num;

    public Book(String name, int num) {
        this.name = name;
        this.num = num;
    }
}

14,Map六大遍历方式

import java.util.*;

public class Main{
    public static void main(String args[]){
        Map map = new HashMap();
        map.put("邓超","孙俪");
        map.put("王宝强","马蓉");
        map.put("宋喆","马蓉");
        map.put("刘令博",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");

        //第一组:先取出所有的Key,通过Key取出对应的Value
        Set keyset = map.keySet();
        //(1)增强for
        System.out.println("------第一种方式------");
        for (Object key : keyset){
            System.out.println(key + "-" + map.get(key));
        }
        //(2)迭代器
        System.out.println("------第二种方式------");
        Iterator iterator = keyset.iterator();
        while(iterator.hasNext()){
            Object key = iterator.next();
            System.out.println(key + "-" + map.get(key));
        }

        //第二组:把所有的values取出
        Collection values = map.values();
        //这里可以使用所有的Collections使用的遍历方法
        //(1)增强for
        System.out.println("------取出所有的value,增强for------");
        for (Object value : values){
            System.out.println(value);
        }
        //(2)迭代器
        System.out.println("------取出所有的value,迭代器------");
        Iterator iterator2 = values.iterator();
        while(iterator2.hasNext()){
            Object value = iterator2.next();
            System.out.println(value);
        }

        //第三组:通过EntrySet来获取k-v
        Set entrySet = map.entrySet();  //EntrySet<Map.Entry<K,V>>
        //(1)增强for
        System.out.println("------使用EntrySet的for增强(第3种)------");
        for (Object entry : entrySet){
            //将entry转成Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2)迭代器
        System.out.println("------使用EntrySet的迭代器(第4种)------");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()){
            Object entry = iterator3.next();
            //System.out.println(next.getClass());  //HashMap$Node -实现-> Map.Entry(getKey,getValue)
            //向下转型 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}
/*
使用HashMap添加3个员工对象,要求
键:员工id
值:员工对象

并遍历显示工资>18000的员工(遍历方式最少两种)
员工类:姓名、工资、员工id
 */

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Main{
    public static void main(String args[]){
        Map hashMap = new HashMap();
        hashMap.put(1,new Employee("jack",8000,1));
        hashMap.put(2,new Employee("Mary",9000,2));
        hashMap.put(3,new Employee("Gary",18888,3));
        //第一种方式
        System.out.println("======第一种方式======");
        Set set = hashMap.entrySet();
        for (Object entry : set){
            Map.Entry m = (Map.Entry) entry;
            Employee employee = (Employee) m.getValue();  //一定要学会怎么在一个类中再用另一个类的方法!!!(重点!)
            if (employee.getSalary()>18000){
                System.out.println(employee.getName());
            }
        }
        //第二种方式
        System.out.println("======第二种方式======");
        Collection value = hashMap.values();
        for (Object obj : value){
            Employee employee = (Employee) obj;
            if (employee.getSalary()>18000){
                System.out.println(employee.getName());
            }
        }
    }
}
class Employee{
    private String name;
    private int salary;
    private int id;

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

    public String getName() {
        return name;
    }

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

    public int getSalary() {
        return salary;
    }

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

    public int getId() {
        return id;
    }

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

 15,HMap阶段小结

 16,HMap底层机制和扩容机制

 17,HMap扩容树化触发

//模拟HashMap触发扩容、树化情况,并Debug验证


import java.util.HashMap;

public class Main{
    public static void main(String args[]){
        HashMap hashMap = new HashMap();
        for (int i=1;i<=22;i++){
            hashMap.put(new A(i),"hello");
        }

        System.out.println("hashMap=" + hashMap);  //12个k-v(equals没有重写)
    }
}

class A {
    private int num;

    public A(int num){
        this.num = num;
    }

    //所有的A对象的hashCode都是100
    @Override
    public int hashCode() {
        return 100;
    }

    @Override
    public String toString() {
        return "A{" +
                "num=" + num +
                '}';
    }
}

18,Hashtable使用和扩容

import java.util.Hashtable;

public class Main{
    public static void main(String args[]){
        Hashtable table = new Hashtable();  //OK
        table.put("john",100);  //OK
        //table.put(null,100);  //异常 NullPointerException
        //table.put("john",null);  //异常 NullPointerException
        table.put("lucy",100);  //ok
        table.put("lic",100);  //ok
        table.put("lic",88);  //替换
        table.put("hello1",1);
        table.put("hello2",1);
        table.put("hello3",1);
        table.put("hello4",1);
        table.put("hello5",1);
        table.put("hello6",1);
        System.out.println(table);
    }

    //Hashtable的底层
    //1,底层有数组Hashtable$Entry[] 初始化大小为11
    //2,临界值threshold 8 = 11 * 0.75
    //3,扩容: 按照自己的扩容机制来进行即可。
    //4,执行 方法 addEntry(hash, key, value, index); 添加K-V 封装到Entry
    //5,当if(count >= threshold) 满足时,就进行扩容
    //5,按照 int newCapacity = (oldCapacity << 1) + 1;的大小扩容
}

19,Properties

 

import java.util.Properties;

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

        //1,Properties继承Hashtable
        //2,可以通过k-v存放数据,当然key和value不能为null
        //增加
        Properties properties = new Properties();
        //properties.put(null,"abc");  //抛出空指针异常
        //properties.put("abc",null);  //抛出空指针异常
        properties.put("john",100);  //k-v
        properties.put("lucy",100);
        properties.put("lic",100);
        properties.put("lic",88);  //如果有相同的key,value被替换

        System.out.println("properties=" + properties);

        //通过k 获取对应值
        System.out.println(properties.get("lic"));  //88

        //删除
        properties.remove("lic");
        System.out.println("properties=" + properties);

        //修改
        properties.put("john","约翰");
        System.out.println("properties=" + properties);
    }
}

 20,集合选型规则

21,TreeSet

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

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

        //1,当我们使用无参构造器,创建TreeSet时,仍然是无序的
        //2,希望添加的元素,按照字符串大小来排序
        //3,使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)并指定排序规则

        //TreeSet treeSet = new TreeSet();
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //下面调用String的compareTo方法进行字符串大小比较
                //从小到大排
                //return ((String)o1).compareTo((String)o2);
                //从大到小排
                //return ((String)o2).compareTo((String)o1);
                //如果要求加入的元素按照长度大小排序
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
        //treeSet.add("tom");
        treeSet.add("abc");  //如果按照长度来排,加不进去
        System.out.println("treeSet=" + treeSet);

        /*
        1,构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator

        public TreeMap(Comparator<? super K> comparator){
               this.comparator = comparator;
           }
        2,在调用treeSet.add("tom"),在底层会执行到
          if (cpr != null){//cpr就是我们的匿名内部类(对象)
             do {
                 parent = t;
                 //动态绑定到我们的匿名内部类(对象)compare
                 cmp = cpr.compare(key, t.key);
                 if (cmp < 0)
                     t = t.left;
                 else if (cmp > 0)
                     t = t.right;
                 else//如果相等,即返回0,这个Key就没有加入
                     return t.setValue(value);
              }while (t != null);
           }
         */
    }
}

22,TreeMap 

import java.util.Comparator;
import java.util.TreeMap;

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

        //使用默认的构造器,创建TreeMap,是无序的(也没有排序)
        //TreeMap treeMap = new TreeMap();
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //按照传入的k(String)的大小进行排序(从小到大)
                //return ((String)o1).compareTo((String)o2);
                //按照k(String)的长度大小排序
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        treeMap.put("lll","啦啦啦");  //如果按照k(String)的长度大小排序,value会替换,key还是之前那个

        System.out.println("treemap=" + treeMap);
    }
}

23,Collections工具类

import java.util.*;
import java.util.concurrent.Callable;

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

        //创建ArrayList集合,用于测试
        List list = new ArrayList();
        list.add("tom");
        list.add("smith");
        list.add("king");
        list.add("milan");
        list.add("tom");

        //reverse(List):反转List中元素的顺序
        Collections.reverse(list);
        System.out.println("list=" + list);
        //shuffle(List):对List集合元素进行随机排序
        for (int i=0;i<5;i++){//做一个抽奖游戏
            Collections.shuffle(list);
            System.out.println("list=" + list);
        }
        //sort(List):根据元素的自然顺序对指定List集合元素按升序排序
        Collections.sort(list);
        System.out.println("自然排序后");
        System.out.println("list=" + list);
        //sort(List, Comparator):根据特定的Comparator产生的顺序对List集合元素排序
        //希望按照字符串的长度大小排序
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2){
                //可以加入校验代码
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("字符串长度大小排序=" + list);
        //swap(list,int,int):将指定list集合中的i处元素和j处元素进行交换
        //比如
        Collections.swap(list,0,1);
        System.out.println("交换后的情况");
        System.out.println("list=" + list);
        //Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println("自然顺序最大元素=" + Collections.max(list));
        //Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
        //比如,我们要返回长度最大的元素\
        Object maxObject = Collections.max(list, new Comparator() {
            @Override
            public int compare(Object o1,Object o2){
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最大的元素=" + maxObject);
        //Object min(Collection)
        //Object min(Collection,Comparator)
        //int frequency(Collection,Object):返回指定集合中指定元素的出现次数
        System.out.println("tom出现的次数=" + Collections.frequency(list,"tom"));
        //void copy(List dest,List src):将src中的内容复制到dest中
        ArrayList dest = new ArrayList();
        //为了完成一个完整拷贝,我们需要先给dest赋值,大小和list.size()一样
        for (int i=0;i<list.size();i++){
            dest.add("");
        }
        //拷贝
        Collections.copy(dest,list);
        System.out.println("dest=" + dest);
        //boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值
        //如果list中,有tom就替换成汤姆
        Collections.replaceAll(list,"tom","汤姆");
        System.out.println("list替换后=" + list);

    }
}

相关练习:

/*
按要求实现:
(1)封装一个新闻类,包含标题和内容属性,提供get、set方法,重写toString方法,打印对象时只打印标题;
(2)只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河“圣浴”引民众担忧
新闻二:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生
(3)将新闻对象添加到ArrayList集合中,并且进行倒序遍历
(4)在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加“...”
(5)在控制台打印遍历出经过处理的新闻标题;
 */

import java.util.ArrayList;
import java.util.Collections;

public class Main{
    public static void main(String agrs[]){
        ArrayList arrayList = new ArrayList();
        arrayList.add(new New("新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河\"圣浴\"引民众担忧"));
        arrayList.add(new New("新闻二:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生"));
        Collections.reverse(arrayList);  //也可以用Size写
        for (Object obj : arrayList){
            New news = (New)obj;  //不可以写new,是关键字
            System.out.println(processTitle(news.getTitle()));
        }
    }
    //专门写一个方法,处理现实新闻标题 precess处理
    public static String processTitle(String title){
        if (title == null){
            return "";
        }
        if (title.length() > 15){  //切割
            return title.substring(0,15) + "...";  //[0,15)
        }else{
            return title;
        }
    }
}
class New{
    private String title;
    private String state;

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

    public String getTitle() {
        return title;
    }

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

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    @Override
    public String toString() {
        return "New{" +
                "title='" + title + '\'' +
                '}';
    }
}
//使用ArrayList完成对对象Car{name,price}的各种操作

import java.util.ArrayList;
import java.util.Iterator;

public class Main{
    public static void main(String args[]){
        Car car = new Car("宝马",400000);
        Car car2 = new Car("宾利",5000000);
        ArrayList arrayList = new ArrayList();
        //add:添加单个元素
        System.out.println("===添加单个元素===");
        arrayList.add(car);
        arrayList.add(car2);
        System.out.println(arrayList);
        //remove:删除指定元素
        System.out.println("===删除指定元素===");
        arrayList.remove(1);
        System.out.println(arrayList);
        //contains:查找元素是否存在
        System.out.println("===查找元素是否存在===");
        System.out.println(arrayList.contains(car2));
        System.out.println(arrayList.contains(car));
        //size:获取元素个数
        System.out.println("===获取元素个数===");
        System.out.println(arrayList.size());
        //isEmpty:判断是否为空
        System.out.println("===判断是否为空===");
        System.out.println(arrayList.isEmpty());
        //clear:清空
        //System.out.println("===清空===");
        //arrayList.clear();
        //System.out.println(arrayList);
        //addAll:添加多个元素
        System.out.println("===添加多个元素===");
        arrayList.addAll(arrayList);
        System.out.println(arrayList);
        //removeAll:删除多个元素
        //System.out.println(“===s删除多个元素===");
        //arrayList.removeAll(arrayList);  //相当于清空
        //System.out.println(arrayList);
        //使用增强for和迭代器来遍历所有的car,需要重写Car的toString方法
        System.out.println("===使用增强for===");
        for (Object obj : arrayList){
            System.out.println(obj);
        }
        System.out.println("===使用迭代器===");
        Iterator iterator = arrayList.iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}
class Car{
    private String name;
    private int price;

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
/*
按要求完成下列任务
1)使用HashMap类实例化一个Map类型的对象m,键(String)和值(int)分别用于存储员工的姓名和工资
存入数据如下:    jack - 650元;    tom - 1200元;      smith - 2900元;
2)将jack的工资更改为2600元
3)为所有员工工资加薪100元
4)遍历集合中所有的员工
5)遍历集合中所有的工资
 */

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Main{
    public static void main(String agrs[]){
        HashMap hashMap = new HashMap();
        hashMap.put("jack",650);
        hashMap.put("tom",1200);
        hashMap.put("smith",2900);
        hashMap.put("jack",2600);
        Set keySet = hashMap.keySet();
        for (Object key : keySet){
            hashMap.put(key,(Integer)hashMap.get(key) + 100);   //要向下转型一下
        }
        Set entrySet = hashMap.entrySet();
        for (Object obj : entrySet){
            Map.Entry m = (Map.Entry) obj;
            System.out.print(m.getKey() + " ");
        }
        System.out.println();
        for (Object obj : entrySet){
            Map.Entry m = (Map.Entry) obj;
            System.out.print(m.getValue() + " ");
        }
    }
}

12,包

mark一下包机制

作用域

13,泛型

(1)泛型

/*
1)请编写程序,在ArrayList中,添加3个Dog对象
2)Dog对象含有name和age,并输出name和age(要求使用getXxx())
 */

//原始方法

import java.util.ArrayList;

public class Main{
    public static void main(String args[]){
        ArrayList arrayList = new ArrayList();
        arrayList.add(new Dog("Jack",3));
        arrayList.add(new Dog("Tom",2));
        arrayList.add(new Dog("Kevin",4));

        //如果程序员不小心加了一只猫
        arrayList.add(new Cat("Mary",1));
        //后面for循环会报错

        for (Object obj : arrayList){
            Dog dog = (Dog) obj;
            System.out.println(dog.getName() + " " +  dog.getAge());
        }
    }
}
class Dog{
    private String name;
    private int age;

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

    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;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class Cat{
    private String name;
    private int age;

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

    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;
    }

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

/*
1)请编写程序,在ArrayList中,添加3个Dog对象
2)Dog对象含有name和age,并输出name和age(要求使用getXxx())
 */

//泛型

import java.util.ArrayList;

public class Main{
    public static void main(String args[]){
        //1,当我们ArrayList<Dog> 表示存放到ArrayList集合中的元素是Dog类型
        //2,如果编译器发现添加的类型不满足要求,就会报错
        //3,在遍历的时候,可以直接取出Dog类型而不是Object
        //4,public class ArrayList<E> {}   E称为泛型,那么 Dog -> E
        ArrayList<Dog> arrayList = new ArrayList<Dog>();
        arrayList.add(new Dog("Jack",3));
        arrayList.add(new Dog("Tom",2));
        arrayList.add(new Dog("Kevin",4));

        System.out.println("===使用泛型===");
        for (Dog dog : arrayList){
            System.out.println(dog.getName() + " " +  dog.getAge());
        }
    }
}
class Dog{
    private String name;
    private int age;

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

    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;
    }

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

public class Main{
    public static void main(String args[]){
        //注意,特别强调:E具体的数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
        Person<String> person = new Person<String>("lalala");
        Person<Integer> persono2 = new Person<>(100);

    }
}

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

class Person<E>{
    E s;  //E表示s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型

    public Person(E s) {  //E也可以是参数类型
        this.s = s;
    }

    public E f(){  //返回类型使用E
        return s;
    }

    public void show(){
        System.out.println(s.getClass());  //显示s的运行类型
    }
}

/*
1,创建3个学生对象
2,放入到HashSet中学生对象,使用
3,放入到HashMap中,要求Key是String name,Value就是学生对象
4,使用两种方式遍历
 */

import java.util.*;

public class Main{
    public static void main(String args[]){
        //使用泛型方式给HashSet放入3个学生对象
        HashSet<Student> students = new HashSet<>();
        students.add(new Student("jack",18));
        students.add(new Student("tom",28));
        students.add(new Student("mary",19));

        //遍历
        for (Student student : students){
            System.out.println(student);
        }

        //使用泛型方式给HashMap放入3个学生对象
        HashMap<String, Student> hm = new HashMap<String, Student>();
        hm.put("milan",new Student("milan",38));
        hm.put("smith",new Student("smith",48));
        hm.put("lll",new Student("lll",28));

        //迭代器EntrySet
        Set<Map.Entry<String, Student>> entries = hm.entrySet();
        Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, Student> next = iterator.next();
            System.out.println(next.getKey() + "-" + next.getValue());
        }
    }
}
class Student{
    private String name;
    private int age;

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

    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;
    }

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

/*
定义Employee类
1)该类包含: private成员变量name,sal,birthday, 其中birthday为MyDate类的对象;
2)为每一个属性定义getter,setter方法;
3)重写toString方法输出name,sal,birthday
4)MyDate类包含:private成员变量month,day,year; 并为每一个属性定义getter,setter方法
5)创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
排序方式:调用ArrayList的sort方法,传入Comparator对象[使用泛型],先按照name排序,如果name相同,则按生日日期的先后排序。
 */

import java.util.ArrayList;
import java.util.Comparator;

public class Main{
    public static void main(String args[]){
        ArrayList<Employee> arrayList = new ArrayList<>();
        arrayList.add(new Employee("Mary",9200,new MyDate(12,2,2001)));
        arrayList.add(new Employee("Gary",8400,new MyDate(6,7,2000)));
        arrayList.add(new Employee("Harry",9500,new MyDate(4,3,2001)));
        arrayList.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee emp1, Employee emp2) {
                if (!(emp1 instanceof  Employee && emp2 instanceof Employee)){
                    System.out.println("类型不正确..");
                    return 0;
                }
                //比较name
                int i = emp1.getName().compareTo(emp2.getName());
                if (i != 0){
                    return i;
                }
                //如果name相同,就比较birthday - year
                int yearMinus = emp1.getBirthday().getYear() - emp2.getBirthday().getYear();
                if (yearMinus != 0){
                    return yearMinus;
                }
                //如果year相同,就比较month
                int monthMinus = emp1.getBirthday().getMonth() - emp2.getBirthday().getMonth();
                if (monthMinus != 0){
                    return monthMinus;
                }
                //如果year和month
                return emp1.getBirthday().getDay() - emp2.getBirthday().getDay();
            }
        });
        System.out.println(arrayList);
    }
}
class MyDate{
    private int month;
    private int day;
    private int year;

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

    public int getMonth() {
        return month;
    }

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

    public int getDay() {
        return day;
    }

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

    public int getYear() {
        return year;
    }

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

    @Override
    public String toString() {
        return "MyDate{" +
                "month=" + month +
                ", day=" + day +
                ", year=" + year +
                '}';
    }
}
class Employee{
    private String name;
    private int sal;
    private MyDate birthday;

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

    public String getName() {
        return name;
    }

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

    public int getSal() {
        return sal;
    }

    public void setSal(int sal) {
        this.sal = sal;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

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

(2)自定义泛型

public class Main{
    public static void main(String args[]){
        //T=Double, R=String, M=Integer
        Tiger<Double,String,Integer> g = new Tiger<>("john");
        
    }
}
//1,Tiger后面泛型,所以我们把Tiger就称为自定义泛型类
//2,T,R,M泛型的标识符,一般是单个大写字母
//3,泛型标识符可以有多个
//4,普通成员可以使用泛型(属性、方法)
//5,使用泛型的数组,不能初始化
//6,静态方法不能使用类的泛型

class Tiger<T,R,M>{
    String name;
    R r;  //属性使用到泛型
    M m;
    T t;
    //因为数组在new不能确定T的类型,就无法在内存开空间
    T[] ts;

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

    public Tiger(String name, R r, M m, T t) {//构造器使用泛型
        this.name = name;
        this.r = r;
        this.m = m;
        this.t = t;
    }

    //因为静态是和类相关的,在类加载时,对象还没有创建
    //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
    //static R r2;
    //public static void m1(M m){
    //
    //}

    public String getName() {
        return name;
    }

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

    public R getR() {
        return r;
    }

    public void setR(R r) {//方法使用到泛型
        this.r = r;
    }

    public M getM() {//返回类型使用泛型
        return m;
    }

    public void setM(M m) {
        this.m = m;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

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

    }
}

/*
*泛型接口使用的说明
* 1,接口中,静态成员也不能使用泛型
* 2,泛型接口的类型,在继承接口或者实现接口时确定
* 3, 没有指定类型,默认为Object
 */

//在继承接口 指定泛型接口的类型
interface IA extends IUsb<String, Double>{

}
//当我们去实现IA接口时,因为IA在继承IUsb接口时,指定了U为String R为Double
//,在实现IUsb接口的方法时,使用String替换U,是Double替换R
class AA implements IA{
    @Override
    public Double get(String s) {
        return null;
    }

    @Override
    public void hi(Double aDouble) {

    }

    @Override
    public void run(Double r1, Double r2, String u1, String u2) {

    }
}

//实现接口时,直接指定泛型接口的类型
//给U指定Integer给R指定了Float
class BB implements IUsb<Integer, Float>{
    @Override
    public Float get(Integer integer) {
        return null;
    }

    @Override
    public void hi(Float aFloat) {

    }

    @Override
    public void run(Float r1, Float r2, Integer u1, Integer u2) {

    }
}
//没有指定类型,默认为Object
class CC implements  IUsb{//等价 class CC implements IUsb<Object,Object>

    @Override
    public Object get(Object o) {
        return null;
    }

    @Override
    public void hi(Object o) {

    }

    @Override
    public void run(Object r1, Object r2, Object u1, Object u2) {

    }
}

interface IUsb<U, R>{
    int n = 10;
    //U name;  不能这样使用

    //普通方法中,可以使用接口泛型
    R get(U u);

    void hi(R r);

    void run(R r1, R r2, U u1, U u2);

    //在jdk8中,可以在接口中,使用默认方法,也是可以使用泛型
    default R method(U u){
        return null;
    }
}

import java.util.ArrayList;

public class Main{
    public static void main(String args[]){
        Car car = new Car();
        car.fly("宝马",100);//当调用方法时,传入参数,编译器,就会确定类型
        System.out.println("======");
        car.fly(300,100);//当调用方法时,传入参数,编译器,就会确定类型
        System.out.println("======");

        //测试
        //T->String, R->ArrayList
        Fish<String, ArrayList> fish = new Fish<>();
        fish.hello(new ArrayList(),11.3f);
    }
}
//泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car{//普通类
    public void run(){//普通方法
    }
    //说明
    //1, <T,R> 就是泛型
    //2, 是提供给fly使用的
    public <T, R> void fly(T t,R r){//泛型方法
        System.out.println(t.getClass());//String
        System.out.println(r.getClass());//Integer
    }
}
class Fish<T,R> {//泛型类
    public void run(){//普通方法
    }
    public <U,M> void eat(U u, M m){//泛型方法

    }
    //说明
    //1,下面hi方法不是泛型方法
    //2,是hi方法使用了类声明的泛型
    public void hi(T t){
    }
    //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
    public <K> void hello(R r, K k){
        System.out.println(r.getClass());//ArrayList
        System.out.println(k.getClass());//Float
    }
}

//泛型的继承和通配符

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String args[]) {
        Object o = new String("xx");
        //泛型没有继承性
        //List<Object> list = new ArrayList<String>();

        //举例说明下面三个方法的使用
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();

        //如果是List<?> c,可以接受任意的泛型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);

        //List<? extends AA> c : 表示上限,可以接受AA或者AA子类
        //printCollection2(list1); //false
        //printCollection2(list2); //false
        //printCollection2(list3); //true
        //printCollection2(list4); //true
        //printCollection2(list5); //true

        //List<? super AA> c : 支持AA类以及AA类的父类,不限于直接父类
        //printCollection3(list1); //true
        //printCollection3(list2); //false
        //printCollection3(list3); //true
        //printCollection3(list4); //false
        //printCollection3(list5); //false
    }

    //说明:List<?> 表示任意的泛型类型都可以接受
    public static void printCollection1(List<?> c){
        for (Object object : c){//通配符,取出时,就是Object
            System.out.println(object);
        }
    }
    // ? extends AA 表示上限,可以接受AA或者AA子类
    public static void printColletion2(List<? extends AA> c){
        for (Object object : c){
            System.out.println(object);
        }
    }
    // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类
    //规定了泛型的下限
    public static void printCollection3(List<? super AA> c){
        for (Object object : c){
            System.out.println(object);
        }
    }
}

class AA{
}

class BB extends AA{
}

class CC extends BB{
}

 (3)练习

/*
定义个泛型类DAO<T>,在其中定义一个Map成员变量,Map的键为String类型,值为T类型。

分别创建以下方法:
(1)public void save(String id,T entity):保存T类型的对象到Map成员变量中
(2)public T get(String id):从map中获取id对应的对象
(3)public void update(String id,T entity):替换map中key为id的内容,改为entity对象
(4)public List<T> list():返回map中存放的所有T对象
(5)public void delete(String id):删除指定id对象

定义一个User类:
该类 包含:private成员变量(int类型)id,age; (String类型)name

创建DAO类的对象,分别调用其save、get、update、list、delete方法来操作User对象,
使用Junit单元测试类进行测试
 */

import org.junit.jupiter.api.Test;

import java.util.*;

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

    }

    @Test
    public void testList(){
        //说明
        //这里我们给T指定类型是User
        DAO<User> dao = new DAO<>();
        dao.save("001",new User(1,10,"jack"));
        dao.save("002",new User(2,18,"king"));
        dao.save("003",new User(3,38,"smith"));

        List<User> list = dao.list();

        System.out.println("list=" + list);

        dao.update("003",new User(3,58,"milan"));
        dao.delete("001");  //删除
        System.out.println("===修改后===");
        list = dao.list();
        System.out.println("list=" + list);

        System.out.println("id=003 " + dao.get("003"));  //milan
    }
}

class DAO<T>{
    Map<String, T> map = new HashMap<>();  //弄清泛型格式

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

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

    public void update(String id,T entity){
        map.put(id,entity);  //根据map性质,会覆盖,相当于替换
    }
    //返回map中存放的所有T对象
    //遍历map[k-v],将map的所有value(T entity),封装到ArrayList返回即可
    public List<T> list(){
        //创建Arraylist
        List<T> list = new ArrayList<>();
        //遍历map
        Set<String> keySet = map.keySet();
        for (String key : keySet){
            list.add(map.get(key));
        }
        return list;
    }

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

}

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

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

14,注解

15,异常处理 

如果程序员认为一段代码可能出现异常/问题,可以使用try-catch异常处理机制来解决,从而保证程序健壮性(不在中途退出)

使用快捷键的方法:将认为可能出现异常的代码块->选中->ctrl+alt+t->选中try-catch

(1)常见的运行时异常举例

1) NullPointerException空指针异常

当应用程序试图在需要对象的地方使用null时,抛出该异常 

public class Main {
    public static void main(String args[]) {
        String name = null;
        System.out.println(name.length());
    }
}

//运行:
Exception in thread "main" java.lang.NullPointerException

2) ArithmeticException数学运算异常

当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例

3) ArrayIndexOutOfBoundsException数组下标越界异常

用非法索引访问数组时抛出的异常。如果索引为负或者大于等于数组的大小,则该索引为非法索引

public class Main {
    public static void main(String args[]) {
        int[] arr = {1,2,4};
        for (int i=0;i<=arr.length;i++)  System.out.println(arr[i]);
    }
}

//运行:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3

4) ClassCastException类型转换异常

当试图将对象强制转换为不是实例的子类时,抛出该异常。


public class Main {
    public static void main(String args[]) {
        A b = new B();  //向上转型
        B b1 = (B)b;  //向下转型
        C c = (C)b;  //抛出异常
    }
}
class A{}
class B extends A{}
class C extends A{}

//运行:
Exception in thread "main" java.lang.ClassCastException: B cannot be cast to C

5) NumberFormatException数字格式不正确异常

当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常->使用异常我们可以确保输入是满足条件数字 

public class Main {
    public static void main(String args[]) {
        String name = "啦啦啦";
        //将String转成int
        int num = Integer.parseInt(name);  //抛出异常
    }
}

运行:
Exception in thread "main" java.lang.NumberFormatException: For input string: "啦啦啦"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)

 

(2)异常处理机制     二选一即可

如果两个都没写,默认是throws

1) try - catch - finally处理机制

//程序
try{
            代码/可能有异常
        }catch(Exception e){
            //捕获到异常
            //1,当异常发生时
            //2,系统将异常封装成Exception对象e,传递给catch
            //3,得到异常对象后,程序员自己处理
            //4,注意,如果没有发生异常catch代码块不执行
        }finally {  //没有finally也是可以通过的
            //不管try代码块是否有异常发生,始终要执行finally
        }

2) throws处理机制

  

//如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
//1,
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入一个整数:");
        int n = 0;
        while(true)
        {
            try {
                n = scanner.nextInt();
                System.out.println("输入的整数为:" + n);
                break;
            } catch (InputMismatchException e) {
                System.out.print("输入错误,请重新输入!");
                //原理是Scanner先读缓冲区,所以要加scanner.next()将上面输入的回车给清掉
                //不然会陷入报错死循环
                scanner.next();  
            }
        }
    }
}

//2,
import java.util.Scanner;

public class Main {
    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        int num = 0;
        String inputStr = "";
        while(true)
        {
            System.out.println("请输入一个整数:");
            inputStr = scanner.next();
            try{
                num = Integer.parseInt(inputStr);
                break;
            }catch(NumberFormatException e){
                System.out.println("你输入的不是一个整数:");
            }
        }
        System.out.println("你输入的值是=" + num);
    }
}

3)当要调用的函数中抛出编译异常的时候,调用会报错,需要在要调用方法的方法中写throws

当要调用的函数中抛出运行异常的时候,不会报错,因为运行异常有默认处理 

import java.io.FileInputStream;
import java.io.FileNotFoundException;

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

    }
    public void f2() throws FileNotFoundException,NullPointerException{
        //创建了一个文件流对象
        //1,这里的异常是一个FileNotFoundException编译异常
        //2,使用前面讲过的try-catch-finally
        //3,使用throws抛出异常,让调用f2方法的调用者(方法)处理
        //4,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
        //5,throws关键字后也可以是异常列表,即可以抛出多个异常
        FileInputStream fis = new FileInputStream("d://aa.txt");
    }
}

public class Main {
    public static void main(String args[]) {
        int age = 80;
        //要求范围在18-120之间,否则抛出一个自定义异常
        if (!(age >= 18 && age <= 120)) {
            throw new AgeException("年龄需要在18-120之间");
        }
        System.out.println("你的年龄范围正确");
    }
}
    //自定义一个异常
    //1,一般情况下,我们自定义异常是继承RuntimeException
    //2,即把自定义异常做成运行时异常,好处是我们可以使用默认的处理机制
    //3,即比较方便
    class AgeException extends RuntimeException{
        public AgeException(String message){  //构造器
            super(message);
        }
    }

//编写程序,接收命令行的两个参数(整数),计算两数相除
//计算两个数相除,要求使用方法cal(int n1, int n2)
//对数据格式不正确(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException)、除0(ArithmeticException)进行异常处理
public class Main {
    public static void main(String args[]) {
        try {
            //先验证输入的参数的个数是否正确  两个参数
            if (args.length != 2){
                throw new ArrayIndexOutOfBoundsException("参数个数不对");
            }

            //先把接收到的参数,转成整数
            int n1 = Integer.parseInt(args[0]);
            int n2 = Integer.parseInt(args[1]);

            double res = cal(n1,n2);  //该方法可以抛出ArithmeticException
            System.out.println("计算结果是=" + res);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
        }catch (NumberFormatException e){
            System.out.println("参数格式不正确,需要输出整数");
        }catch (ArithmeticException e){
            System.out.println("出现了除0的异常");
        }
    }
    public static double cal(int n1, int n2){
        return n1/n2;
    }
}

16,JUnit

import org.junit.jupiter.api.Test;

public class Main {
    public static void main(String args[]) {
        //传统方式
        //new Main().m1();
        //new Main().m2();
    }

    @Test
    public void m1(){
        System.out.println("m1方法被调用");
    }
    @Test
    public void m2(){
        System.out.println("m2方法被调用");
    }
}

17,多线程

(1)线程相关概念

 

(2)继承Thread创建线程

//演示通过继承Thread类创建线程

public class Main {
    public static void main(String args[]){
        //创建Cat对象,可以当作线程使用
        Cat cat = new Cat();
        cat.start();  //启动线程
    }
}

//1,当一个类继承了Thread类,该类就可以当作线程使用
//2,我们会重写run方法,写上自己的业务代码
//3,run Thread类实现了Runnable接口的run方法
class Cat extends Thread{
    int times = 0;
    @Override
    public void run() {//重写run方法,写上自己的业务逻辑
        while (times<80) {
            //该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”
            System.out.println("喵喵,我是小猫咪" + (++times));
            //让该线程休眠1秒 ctrl+alt+t打开快捷键
            try {
                Thread.sleep(1000);  //注意单位是毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)多线程机制

//演示通过继承Thread类创建线程

public class Main {
    public static void main(String args[]) throws  InterruptedException{
        //创建Cat对象,可以当作线程使用
        Cat cat = new Cat();
        cat.start();  //启动线程->最终会执行cat的run方法
        //说明:"main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行"
        //这时,主线程和子线程是交替执行...
        System.out.println("主线程继续执行" + Thread.currentThread().getName());  //名字main
        for (int i=0;i<10;i++){
            System.out.println("主线程i=" + i);
            //让主线程休眠
            Thread.sleep(1000);
        }
    }
}

//1,当一个类继承了Thread类,该类就可以当作线程使用
//2,我们会重写run方法,写上自己的业务代码
//3,run Thread类实现了Runnable接口的run方法
class Cat extends Thread{
    int times = 0;
    @Override
    public void run() {//重写run方法,写上自己的业务逻辑
        while (times<5) {
            //该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”
            System.out.println("喵喵,我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
            //让该线程休眠1秒 ctrl+alt+t打开快捷键
            try {
                Thread.sleep(1000);  //注意单位是毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(4)为什么是start

真正的线程启动是start()而不是run(),run()只是一个普通的方法

(5)Runnable创建线程

//通过实现接口Runnable来开发线程

public class Main{
    public static void main(String args[]){
        Dog dog = new Dog();
        //dog.start();这里不能调用start
        //创建了Thread对象,把dog对象(实现Runnable),放入Thread
        Thread thread = new Thread(dog);
        thread.start();
    }
}

class Dog implements Runnable{//通过实现Runnable接口,开发线程
    int count = 0;
    @Override
    public void run(){
        while(true){
            System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
            //休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 (6)多个子线程案例

//main线程启动两个子线程

public class Main{
    public static void main(String args[]){
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();  //启动t1线程
        thread2.start();  //启动t2线程
    }
}

class T1 implements Runnable{
    int count = 0;
    @Override
    public void run(){
        while(true) {
            //每隔1秒输出"hello,world",输出10次
            System.out.println("hello,world " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count==10){
                break;
            }
        }
    }
}

class T2 implements Runnable{
    int count = 0;
    @Override
    public void run(){
        while(true) {
            //每隔1秒输出"hi",输出5次
            System.out.println("hi " + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count==5){
                break;
            }
        }
    }
}

 

(7)多线程售票问题

//使用多线程,模拟三个窗口同时售票100张

public class Main{
    public static void main(String agrs[]){
        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //这里我们会出现超卖..
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

        System.out.println("=====使用实现接口方式来售票=====");
        SellTicket02 sellTicket02 = new SellTicket02();

        new Thread(sellTicket02).start();//第1个线程-窗口
        new Thread(sellTicket02).start();//第2个线程-窗口
        new Thread(sellTicket02).start();//第3个线程-窗口
    }
}

//使用Thread方式

class SellTicket01 extends Thread{
    private static int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

//实现接口方式
class SellTicket02 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

(8)通知线程退出

public class Main{
    public static void main(String args[]) throws InterruptedException {
        T t1 = new T();
        t1.start();
        //如果希望main线程去控制t1线程的终止,必须可以修改loop
        //让t1退出run方法,从而终止t1线程->通知方式
        //让主线程休眠10秒,再通知t1线程退出
        System.out.println("main线程休眠10s...");
        Thread.sleep(10*1000);
        t1.setLoop(false);
    }
}

class T extends Thread{
    private int count = 0;
    //设置一个控制变量
    private boolean loop = true;
    @Override
    public void run(){
        while(loop){
            try{
                Thread.sleep(50);  //让当前线程休眠50ms
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("T 运行中...." + (++count));
        }
    }

    public void setLoop(boolean loop){
        this.loop = loop;
    }
}

(9)线程常用方法

import java.util.concurrent.ThreadLocalRandom;

public class Main{
    public static void main(String args[]) throws InterruptedException {
        T t = new T();
        t.setName("啦啦啦");
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();

        //主线程打印5 hi,然后我就中断子线程的休眠
        for (int i=0;i<5;i++){
            Thread.sleep(1000);
            System.out.println("hi " + i);
        }

        System.out.println(t.getName() + "线程的优先级=" + t.getPriority());  //1

        t.interrupt();
    }
}

class T extends Thread{//自定义的线程类
    @Override
    public void run() {
        while (true) {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " 吃包子" + i);
            }
            try {
                System.out.println(Thread.currentThread().getName() + " 休眠中");
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                //当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                //InterruptedException是捕获到一个中断异常
                System.out.println(Thread.currentThread().getName() + "被interrupt了");
            }
        }
    }
}

public class Main{
    public static void main(String args[]) throws InterruptedException {
        T2 t2 = new T2();
        t2.start();
        for (int i=1;i<=20;i++){
            Thread.sleep(1000);
            System.out.println("主线程(小弟)吃了" +i + "包子");
            if (i==5){
                System.out.println("主线程(小弟)让子线程(老大)先吃");
                //join,线程插队
                //t2.join();//这里相当于让t2线程先执行完毕
                Thread.yield();//礼让,不一定成功..
                System.out.println("线程(老大)吃完了,主线程(小弟)接着吃..");
            }
        }
    }
}

class T2 extends Thread{
    @Override
    public void run(){
        for (int i=1;i<=20;i++){
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("子线程(老大)吃了" +i + "包子");
        }
    }
}

/*
1,主线程每隔1s,输出hi,一共10次
2,当输出到hi 5时,启动一个子线程(要求实现Runnable),每隔1s输出hello.等该线程输出10次hello后,退出
3,主线程继续输出hi,直到主线程退出
 */

public class Main{
    public static void main(String args[]) throws InterruptedException {
        for (int i=1;i<=10;i++){
            Thread.sleep(1000);
            System.out.println("主线程输出hi " + i);
            if (i==5){
                System.out.println("主线程暂停输出,子线程开始输出");
                Thread01 thread01 = new Thread01();
                Thread thread = new Thread(thread01);
                thread.start();  //必须要start一下
                thread.join();
                System.out.println("子线程结束输出,主线程继续输出");
            }
        }
    }
}

class Thread01 implements Runnable{
    @Override
    public void run(){
        for (int i=1;i<=10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程输出hello " + i);
        }
    }
}

(10)守护线程

public class Main{
    public static void main(String args[]) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        //如果我们希望当main线程结束后,子线程自动结束
        //,只需将子线程设为守护线程即可
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();
        for (int i=1;i<=10;i++){//main线程
            System.out.println("啦啦啦");
            Thread.sleep(1000);
        }
    }
}

class MyDaemonThread extends Thread{
    public void run(){
        for (;;){//无限循环
            try{
                Thread.sleep(1000);//休眠50毫秒
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("哈哈哈");
        }
    }
}

(11)线程7大状态

 (12)线程同步机制

//使用多线程,模拟三个窗口同时售票100张

public class Main{
    public static void main(String agrs[]){
        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //这里我们会出现超卖..
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

//        System.out.println("=====使用实现接口方式来售票=====");
//        SellTicket02 sellTicket02 = new SellTicket02();

//        new Thread(sellTicket02).start();//第1个线程-窗口
//        new Thread(sellTicket02).start();//第2个线程-窗口
//        new Thread(sellTicket02).start();//第3个线程-窗口

        //测试
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第1个线程-窗口
        new Thread(sellTicket03).start();//第2个线程-窗口
        new Thread(sellTicket03).start();//第3个线程-窗口
    }
}

//实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    private boolean loop = true;//控制run方法变量

    public synchronized void sell(){//同步方法,在同一时刻,只能有一个线程来执行run方法
        if (ticketNum<=0){
            System.out.println("售票结束...");
            loop = false;
            return;
        }
        //休眠50毫秒,模拟
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
    }

    @Override
    public void run(){
        while(loop){
            sell();//sell方法是一个同步方法
        }
    }
}

//使用Thread方式

class SellTicket01 extends Thread{
    private static int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

//实现接口方式
class SellTicket02 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

 (13)互斥锁

 

//使用多线程,模拟三个窗口同时售票100张

public class Main{
    public static void main(String agrs[]){
        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //这里我们会出现超卖..
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

//        System.out.println("=====使用实现接口方式来售票=====");
//        SellTicket02 sellTicket02 = new SellTicket02();

//        new Thread(sellTicket02).start();//第1个线程-窗口
//        new Thread(sellTicket02).start();//第2个线程-窗口
//        new Thread(sellTicket02).start();//第3个线程-窗口

        //测试
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第1个线程-窗口
        new Thread(sellTicket03).start();//第2个线程-窗口
        new Thread(sellTicket03).start();//第3个线程-窗口
    }
}

//实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    private boolean loop = true;//控制run方法变量
    Object object = new Object();


    //同步方法(静态的)的锁为当前类本身
    //1,public synchronized static void m1(){}锁是加在SellTicket03.class
    //2,如果在静态方法中,实现一个同步代码块
    public synchronized static void m1(){

    }
    public static void m2{
        synchronized (SellTicket03.class){  //不可以写this
            System.out.println("m2");
        }
    }

    //1,public synchronized void sell(){}就是一个同步方法
    //2,这时锁在this对象
    //3,也可以在代码块上写synchronize,同步代码块,互斥锁还是在this对象上
    public /*synchronized*/ void sell() {//同步方法,在同一时刻,只能有一个线程来执行run方法
        synchronized (/*this*/object) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }

    @Override
    public void run(){
        while(loop){
            sell();//sell方法是一个同步方法
        }
    }
}

//使用Thread方式
//new SellTicket01().start()
//new SellTicket01().start()
//两次都是各自的this,不是同一个this
class SellTicket01 extends Thread{
    private static int ticketNum = 100;//让多个线程共享ticketNum

    //public void m1(){
    //    synchronized (this){
    //        System.out.println(this){
    //           System.out.println("hello");
    //       }
    //    }
    //}

    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

//实现接口方式
class SellTicket02 implements Runnable{
    private int ticketNum = 100;//让多个线程共享ticketNum
    @Override
    public void run(){
        while(true){
            if (ticketNum<=0){
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}

(14)线程死锁

//模拟线程死锁

public class Main{
    public static void main(String args[]){
        DeadLockDemo A = new DeadLockDemo(true);
        A.setName("A线程");
        DeadLockDemo B = new DeadLockDemo(false);
        B.setName("B线程");
        A.start();
        B.start();
    }
}

class DeadLockDemo extends Thread{
    static Object o1 = new Object();  //保证多线程,共享一个对象,这里使用static
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag){//构造器
        this.flag = flag;
    }

    @Override
    public void run(){

        //下面业务逻辑的分析
        //1,如果flag为T,线程A就会先得到/持有o1对象锁,然后尝试去获取o2对象锁
        //2,如果线程A得不到o2对象锁,就会Blocked
        //3,如果flag为F,线程B就会先得到/持有o2对象锁,然后尝试去获取o1对象锁
        //4,如果线程B得不到o1对象锁,就会Blocked
        if (flag){
            synchronized (o1){//对象互斥锁,下面就是同步代码
                System.out.println(Thread.currentThread().getName() + "进入1");
                synchronized (o2){//这里获得li对象的监视权
                    System.out.println(Thread.currentThread().getName() + "进入2");
                }
            }
        }else{
            synchronized (o2){
                System.out.println(Thread.currentThread().getName() + "进入3");
                synchronized (o1) {//这里获得li对象的监视权
                    System.out.println(Thread.currentThread().getName() + "进入4");
                }
            }
        }
    }
}

(15)释放锁

 

(16)练习

/*
(1)在main方法中启动两个线程
(2)第1个线程循环随机打印100以内的整数
(3)直到第2个线程从键盘读取了”Q“命令
 */

import java.util.Scanner;

public class Main{
    public static void main(String args[]){
        A a = new A();
        B b = new B(a);
        a.start();
        b.start();
    }
}

class A extends Thread{
    private boolean loop = true;

    @Override
    public void run(){
        //输出1-100数字
        while(loop) {
            System.out.println((int) (Math.random() * 100 + 1));
            //休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void setLoop(boolean loop){
        this.loop = loop;
    }
}

class B extends Thread{
    private A a;
    private Scanner scanner = new Scanner(System.in);

    public B(A a){//构造器中,直接传入A类对象
        this.a = a;
    }

    @Override
    public void run(){
        while(true) {
            System.out.println("请输入你的指令(Q表示退出)");
            char key = scanner.next().toUpperCase().charAt(0);
            if (key == 'Q'){
                a.setLoop(false);
                System.out.println("b线程退出");
                break;
            }
        }
    }
}

/*
(1)有2个用户分别从同一张卡上取钱(总额:10000)
(2)每次都取1000,当余额不足时,就不能取款了
(3)不能出现超取现象 -> 线程同步问题
 */

public class Main{
    public static void main(String args[]){
        User user = new User();
        Thread thread1 = new Thread(user);
        thread1.setName("用户1");
        Thread thread2 = new Thread(user);
        thread2.setName("用户2");
        thread1.start();
        thread2.start();
    }
}

class User implements Runnable{
    private int card = 10000;
    private boolean loop = true;

    public synchronized void get(){
        if (card<=0){
            System.out.println("余额不足,不能取款了!");
            loop = false;
            return;
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        card = card - 1000;
        System.out.println(Thread.currentThread().getName() + "取了1000元,还剩下" + card + "元");
    }

    @Override
    public void run(){
        while(loop) {
            get();
        }
    }
}

十八,IO流

1,文件基础知识

 2,创建文件

//演示创建文件

import org.junit.jupiter.api.Test;

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

public class Main{
    public static void main(String args[]){
        }
    //方式1 new File(String pathname)
    @Test
    public void create01() {
        String filePath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\news1.txt";
        File file = new File(filePath);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //方式2 new File(File parent,String child)  根据父目录文件+子路径构建
    @Test
    public void create02(){
        File parentFile = new File("C:\\Users\\wangyifei\\IdeaProjects\\test\\");
        String fileName = "news2.txt";
        //这里的file对象,在java程序中,只是一个对象
        //只有执行了createNewFile方法,才会真正的,在磁盘创建该文件
        File file = new File(parentFile,fileName);

        try {
            file.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //方式3 new File(String parent,String child)  根据父目录+子路径构建
    @Test
    public void create03(){
        String parentPath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\";
        String fileName = "news3.txt";
        File file = new File(parentPath,fileName);

        try {
            file.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 3,获取文件信息

import org.junit.jupiter.api.Test;

import java.io.File;

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

    }

    //获取文件的信息
    @Test
    public void info(){
        //先创建文件对象
        File file = new File("C:\\Users\\wangyifei\\IdeaProjects\\test\\news1.txt");
        //调用相应的方法,得到对应信息
        System.out.println("文件名字=" + file.getName());
        System.out.println("文件绝对路径=" + file.getAbsolutePath());
        System.out.println("文件父级目录=" + file.getParent());
        System.out.println("文件大小(字节)=" + file.length());
        System.out.println("文件是否存在=" + file.exists());
        System.out.println("是不是一个文件=" + file.isFile());
        System.out.println("是不是一个目录=" + file.isDirectory());
    }
}

4,目录操作

import org.junit.jupiter.api.Test;

import java.io.File;

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

    }
    //判断 C:\Users\wangyifei\IdeaProjects\test\news1.txt是否存在,如果存在就删除
    @Test
    public void m1(){
        String filePath  = "C:\\Users\\wangyifei\\IdeaProjects\\test\\news1.txt";
        File file = new File(filePath);
        if (file.exists()){
            if(file.delete()){
                System.out.println(filePath + "删除成功");
            }else{
                System.out.println(filePath + "删除失败");
            }
        }else{
            System.out.println("该文件不存在...");
        }
    }
    //判断 C:\Users\wangyifei\IdeaProjects\test\news 目录是否存在,如果存在就删除
    //这里需要体会到,在java编程中,目录也被当作文件
    @Test
    public void m2(){
        String filePath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\news";
        File file = new File(filePath);
        if (file.exists()){
            if (file.delete()){
                System.out.println(filePath + "删除成功");
            }else {
                System.out.println(filePath + "删除失败");
            }
        }else{
            System.out.println("该目录不存在...");
        }
    }
    //判断C:\Users\wangyifei\IdeaProjects\test\news\a\b\c 目录是否存在,如果存在就提示已经存在,否则就创建
    @Test
    public void m3(){
        String directoryPath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\news\\a\\b\\c";
        File file = new File(directoryPath);
        if (file.exists()){
            System.out.println(directoryPath + "存在..");
        }else{
            if(file.mkdirs()){
                System.out.println(directoryPath + "创建成功..");
            }else{
                System.out.println("创建失败..");
            }
        }
    }
}

 5,IO流原理和分类

6,FileInputStream

 

//演示FileInputStream的使用(字节输入流 文件 - > 程序)
//演示读取文件...
//单个字节的读取,效率比较低
//-> 使用read(byte[] b)

import org.junit.jupiter.api.Test;

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

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

    }
    @Test
    public void readFile01() {
        String filePath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\hello.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建FileInputStream对象,用于读取文件
            //如果是文本文件最好用字符流
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。如果没有输入可用,此方法将阻止。
            //如果返回-1.表示读取完毕
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char) readData);  //转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //使用read(byte[] b)读取文件,提高效率
    @Test
    public void readFile02() {
        String filePath = "C:\\Users\\wangyifei\\IdeaProjects\\test\\hello.txt";
        //字节数组
        byte[] buf = new byte[8];  //一次读取8个字节
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建FileInputStream对象,用于读取文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。如果没有输入可用,此方法将阻止。
            //如果返回-1.表示读取完毕
            //如果读取正常,返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf,0,readLen));  //显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

7,FileOutputStream

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java编程基础知识包括以下内容: 1. Java语言特点:Java是一种面向对象的编程语言,具有跨平台性、强类型和静态类型检查、自动内存管理等特点。 2. Java的开发环境:Java的开发环境包括JDK(Java Development Kit)和IDE(Integrated Development Environment),如Eclipse、IntelliJ IDEA等。 3. 基本语法:Java的基本语法包括变量、数据类型、运算符、控制流语句(if-else、for循环、while循环等)、数组等。 4. 面向对象编程Java是一种面向对象的编程语言,支持封装、继承和多态等面向对象的特性。类和对象是Java程序的基本组成单元。 5. 异常处理:Java提供了异常处理机制,通过try-catch-finally块来捕获和处理异常,保证程序的健壮性。 6. 输入输出:Java提供了丰富的输入输出API,可以通过标准输入输出、文件操作、网络通信等方式进行数据的输入和输出。 7. 集合框架:Java提供了一套集合框架,包括List、Set、Map等常用的数据结构和算法,方便处理和操作数据。 8. 多线程编程Java支持多线程编程,可以通过Thread类或Runnable接口创建线程,并使用synchronized关键字实现线程同步。 9. 强大的标准库:Java标准库提供了丰富的API,包括字符串处理、日期时间操作、网络编程、数据库操作等功能。 以上是Java编程的一些基础知识,希望对你有所帮助。如果你有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值