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代码的编译运行流程
- 将Java源码(.java)编译成Java字节码(.class)。
- 使用JVM将Java字节码转化成机器码。
- JVM作用:跨平台、内存管理、安全。
- 在doc中的使用参见如何运行一个java程序 如何使用记事本和cmd执行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.4x
double:1.79x
//对于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 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
char | 2 |
boolean | 1 |
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 | 回车 |
\t | Tab |
\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():返回长度
和数组.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