Java简介
Java发布于1995年5月23日,Java前身是Oak语言,衍生自C++,Java之父是詹姆斯·亚瑟·高斯林,Java最早是sun公司的产品,2009年被Oracle公司收购,现属于Oracle公司的产品。
Java的版本
最初版本
1995年1.0版本;1.1版本
Java2平台
1.2 1.3 1.4 1.5
走出Java2
6
易主Oracle
2011年7,8
新的举措,每半年发布一个版本
9, 10 ,11 ,12,....
Java的版本类型
JavaSE,Java标准版本,可以开发Java控制台程序,桌面程序等。为JavaEE提供基础
JavaEE,Java企业级版本,现以捐献给eclipse基金会,改名为JakartaEE(雅加达EE)。它是一套规范,由第三方对他具体实现。在实际开发中,一般使用框架来代替它
Java语言特点
1.面向对象
2.跨平台
3.健壮性
4.简单易学 废弃c中的指针操作
5.多线程
6.动态性
7.分布式
Java平台的组成部分
JVM:Java虚拟机,使Java程序跨平台
JRE:Java运行环境,包含JVM以及常用类库,运行Java程序的最小单位
JDK:Java开发工具包,包含JRE,以及编译工具,打包工具,开发Java程序的最小单位
IDE:集成开发环境,eclipse,Myeclipse,idea
第一个Java程序
创建项目:File → New(Other) → Java Project → Project Name → Finish
创建包:src右键 → New → Package → Name → Finish
创建类:在包上右键 → New → Class → Name → Finish
注释:
单行注释://
多行注释:/ /
文档注释:/* /
输出语句
1.不换行输出:System.out.print();
2.换行输出:System.out.println();
转义字符
双引号:\ "
单引号:\ '
反斜线:\ \
制表符:\t(Tab键,适合不对齐输出)
换行:\n
JAVA和JAVAC命令的作用
JAVAC:作用是将.java文件编译生成.class字节码文件
JAVA:作用将.class字节码文件解释并执行
数据类型
1、基本数据类型
整数类型
byte 字节型 1个字节 8位 范围:-128~127
short 短整型 2个字节 16位 范围:-32768~32767
int 整型 4个字节 32位 范围:约正负21亿
long 长整型 8个字节 64位 范围:约为正负922亿亿 后缀:L或l
浮点类型
float 单精度 4个字节 32位 后缀:F或f
double 双精度 8个字节 64位 后缀:D或d
字符类型
char 字符型 2个字节 16位 范围:0~65535(\u0000~\uFFFF)
布尔类型
boolean 布尔型 1个或4个 8或32位 值:true false
2、引用数据类型
类类型(class)
字符串(String),多个字符组成了字符串,用双引号表示,如"高斯林"
数组类型(array)
接口类型(interface)
枚举类型(enum)
注解类型(annotation)
变量
程序中可以改变的量
变量的三大要素(三大组成部分)
数据类型 变量名 变量值
1.声明变量:数据类型 变量名;int a;
2.变量赋值:变量名 = 值; a = 10;
合并:数据类型 变量名 = 值; int a = 10;
3.声明多个同一类型的变量
数据类型 变量名1,变量名2,...
变量名的建议规范
变量名遵循驼峰命名法,即首字母小写,其后每个单词首字母大写,strScore
不建议使用汉字,尽量使用英文单词或拼音,要见名知意
不建议使用单个字母命名,约定俗成的除外
不建议以下划线开头,不建议使用$
命名法:
驼峰命名法:classId
帕斯卡命名法:ClassId
蛇底式命名法:class_id
尖叫蛇底式命名法:CLASS_ID
标识符强制规范
1.可由字母,数字,下划线,$组成
2.不能以数字开头
3.字母区分大小写
4.不能使用关键字,字面量,保留字
5.在同一作用域下名称不能重名
关键字:是Java定义好的具有特定含义的单词,public ,class ,int...
字面量:是Java定义好的一些值:true,false,null
保留字:是Java的前身Oak和前代语言C、C++的关键字,但是在Java中没有使用却保留下来的,const,goto
控制台输入
Scanner是用来输入的,首先定义一个Scanner
Scanner sc = new Scanner(System.in);
导入Scanner
1.在package下面,写上import java.util.Scanner;
2.将鼠标放在Scanner上面,根据提示,选择...
3.将光标放在Scanner后面,按Alt+/(?)
4.使用快捷键:按Ctrl+Shift+o,会自动导入所有所需要的类
一般语法:sc.next类型(),类型要求首字母大写
输入double类型:sc.nextDouble()
输入int类型:sc.nextInt()
输入字符串String:sc.next()
没有char类型的输入
类型转换
1.自动类型转换(隐式类型转换)
范围小的类型转到范围大的类型
2.强制类型转换
范围大的类型转到范围小的类型
(想要转到的类型)变量
强制类型转换可能出现溢出(精度丢失)
3.在八大基本类型中,除了boolean之外的七种类型都可以互相转换
字符串转基本类型
一般语法:数据类型.parse数据类型(字符串),数据类型首字母大写
double score = Double.parseDouble(strScore);
1.字符串转int类型:Integer.parseInt(字符串);
int port = Integer.parseInt(strPort);
2.对于字符串转布尔类型,如果字符串是true(不区分大小写),则结果为true,否则为false
3.没有转成char类型的方法
基本类型转字符串
""+基本类型变量或数值
运算符
算术运算符,赋值运算符,关系运算符,逻辑运算符,条件运算符
【算术运算符】
+、-、*、/、%、++、--
1.两个整数相除的结果是整数(即整除),若想得到小数结果,需要将其中一个强制类型转换成浮点数
2.利用整除可以去掉最后一位:整数/10
3.浮点类型的计算不一定精准(加减乘除%)
4.取余数
1)负数取余数,以第一个数的正负为准,若第一个数是正数,则结果为正,若第一个数是负数,则结果为负
2)判断奇偶,数%2,若结果是0则是偶数,若结果非0则是奇数
3)获取整数的最后一位:整数%10
4)判断整数:数%1,若结果是0则是整数,若结果非0则是奇数
5.两种类型进行计算的结果
1)都是整数类型,若由long类型参与,则结果为long类型,否则为int类型
2)若有浮点类型参与,如果有double类型参与,则结果为double类型,否则为float类型
6.除了boolean之外的七种类型,都可以进行算术运算
7.++、--
++:自加1 --自减1
a++:先用后加
++a:先加后用
// 习题
int i=1;
//int c = i++ + ++i;
int a = i++;//a=1 i=2
int b = ++i;//b=3 i=3
int c = a+b;//c=4
System.out.println(i);
System.out.println(c);
int x = 1;
x = x++;
x = x++;
x = x++;
// ...
System.out.println(x);// 值永远不变
【赋值运算符】
1.简单赋值运算符(普通赋值运算符):=
2.复合赋值运算符:+= -= *= /= %=
short a = 1;
a+=2;//a变为3
a+=2和a=a+2并不完全一样,a+=2里边存在一个隐式的类型转换
相当于a=(a的类型)(a+2),结果是+=左边的类型
// 完成两值交换
int x = 1;
int y = 9;
int z = x;//z=1
x = y;//x=9
y = z;//y=1
System.out.println(x);//9
System.out.println(y);//1
short a = 1;
//a += 2;
a = (short)(a+2);
System.out.println(a);
【关系运算符】
//>,>=,<,<=,==(等于),!=(不等于)。结果是布尔类型
【逻辑运算符】
与 或 非
与:并且,如果两个条件同时为true,则结果是true
&&:短路与,如果第一个条件为false,则不会判断第二个条件
&:非短路与,即使第一个条件为false,也会判断第二个条件
或:或者,如果有一个条件为true,则结果为true
||:短路或,如果第一个条件为true,则不会判断第二个条件
|:非短路或,即使第一个条件为true,也会判断第二个条件
非:取反 !
【条件运算符】
? :
布尔类型表达式 ? 结果为true则执行此部分 : 否则执行此部分
// 求a,b,c的最大值
int a = 50;
int b = 10;
int c = 30;
int max;
// 方式一
// max = a > b ? a : b;
// max = max > c ? max : c;
// 方式二
max = (a > b ? a : b) > c ? (a > b ? a : b) : c;
System.out.println(max);
【按操作数划分】
一元运算符(单目运算符):只有一个操作数,如:a++,-1
二元运算符(双目运算符):有两个操作数,如a+b
三元运算符(三目运算符):有三个操作数,即?:
快捷键
万能键:alt+/(?) main=>主方法
复制:ctrl+c
粘贴:ctrl+v
剪切:ctrl+x
撤回:ctrl+z
删除一行:ctrl+d
保存:ctrl+s
单行注释:ctrl+/(?)
多行注释:ctrl+shift+/(?)
变成大写:ctrl+shift+x
变成小写:ctrl+shift+y
流程控制
1、顺序执行
分支语句
if、switch
if
一、单分支
if(条件){
// 如果条件为true,则执行此部分代码
}
二、双分支
if(条件){
// 如果条件为true,则执行此部分代码
}else{
// 否则执行此部分
}
三、多分支
if(条件1){
// 如果条件1为true,则执行此部分代码,不会判断下面的条件
}else if(条件2){
// 如果条件2为true,则执行此部分代码,不会判断下面的条件
}...else{
// 如果以上条件都不满足,则执行此部分代码
}
当代码块语句是一句执行语句时,{}可以省略不写
switch:开关语句
switch(变量){
case 值1:
// 如果变量和值1相等,则执行此部分代码
break;
case 值2:
// 如果变量和值2相等,则执行此部分代码
break;
...
default:
// 如果变量和以上值都不相等,则执行此部分代码
}
注意:
1.case贯穿:如果没有break,会继续执行,直到遇到一个break或者程序结束
2.case后面必须是一个具体的值,不能是变量,也不能是多个值,也不能表示范围
3.case的值不能重复
4.switch支持的6种类型:byte,short,int,char,String(Java 7),枚举(Java 5)
循环
1.while循环 2.for循环 3.do-while 4.新型for循环
while循环
确定循环次数
1.初始变量
while(2.循环条件){
3.循环体
4.迭代因子
}
执行顺序:1234 234 234...
for循环
for(1.初始变量;2.循环条件;4.迭代因子){
3.循环体
}
执行顺序:1234 234 234...
应用场景:
1.for循环适合确定循环次数的场景
2.while循环适合不确定循环次数的场景
循环控制
1.break:终止整个循环
2.continue:终止本次循环,还会执行下次循环
do-while
1.初始变量
do{
3.循环体
4.迭代因子
}while(2.循环条件);
执行顺序:1342 342 ...、
do while循环至少执行一次
数组
1.声明数组:数据类型[] 数组名;
数据类型 数组名[]; 不推荐
2.实例化数组:数组名 = new 数据类型[长度];
合并:声明并实例化对象:数据类型[] 数组名 = new 数据类型[长度];
注意:数组长度是int类型
3.使用数组
1)为元素赋值:数组名[索引] = 值
索引,下标,下角标,角标,从0开始,最大索引比长度小1
2)获取元素值:数组名[索引]
4.数组长度:数组名.length
int[] scores = new int[3];
scores[0] = 90;
scores[1] = 80;
scores[2] = 99;
//遍历数组
//方式一
// for (int i = 0; i < scores.length; i++) {
// System.out.println(scores[i]);
// }
/*
* 新型for循环,增强for循环,foreach循序,迭代循环,for循环
* for(元素的数据类型 变量名 : 数组名){
* // 每次循环,变量的值,就是数组中每个元素值
* }
*/
// 方式二
for(int score : scores) {
System.out.println(score);
}
5.数组默认值
整数类型:0
byte:byte(0)
short:short(0)
int:0
long:0L
浮点类型:0.0
float:0.0F
double:0.0
字符类型:char \u0000,实际上就是0
布尔类型:false
字符串:null
6.数组初始化
1.数据类型[] 数组名 = new 数据类型[]{值1,值2,...};
2.数据类型[] 数组名 = {值1,值2,...};此方式只能在声明数组时使用
ArrayIndexOutOfBoundsException 下标越界异常
冒泡排序
public static void main(String[] args) {
int[] arr = { 25, 16, 7, 51, 65, 78, 91, 1 };
for (int i = 0; i < arr.length-1; i++) {
/*
* i 7-i
* 0 7
* 1 6
* 2 5
* 3 4
* 4 3
* 5 2
* 6 1
*/
for(int j=0; j<arr.length-1-i; j++) {
// 交换 小-》大
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
数组排序:Arrays.sort(数组名); 默认是正序排序
数组转字符串:Arrays.toString(数组名);
数组扩容:Arrays.copyOf(数组名, 新长度)
扩容:新长度比原长度大
缩容:新长度比原长度小
二维锯齿数组
数据类型[ ] [ ] 数组名 = new 数据类型[长度] [长度]
int[] [] nums = new int[2] [3];
nums[0] [0] nums[0] [1] nums[0] [2]
nums[1] [0] nums[1] [1] nums[1] [2]
nums是一个数组,里面有2个元素,长度是2
nums[0]是一个数组,里面有3个元素,长度是3
nums[1]是一个数组,里面有3个元素,长度是3
public static void main(String[] args) {
/*
* 二维锯齿数组
*/
// int[][] nums = new int[2][3];
int[][] nums = new int[2][];
nums[0] = new int[3];
nums[1] = new int[2];
// 第一行
nums[0][0] = 1;
nums[0][1] = 2;
nums[0][2] = 3;
// 第二行
nums[1][0] = 4;
nums[1][1] = 5;
// System.out.println(nums.length);// 2
// System.out.println(nums[0].length);// 3
// System.out.println(nums[1].length);//2
// 遍历二维锯齿数组
for (int i = 0; i < nums.length; i++) {
for(int j=0;j<nums[i].length;j++) {
System.out.print(nums[i][j]+" ");
}
System.out.println();
}
}
面向对象
面向过程:是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现,注重的是具体实现 过程,每个步骤亲历亲为
例:吃饺子=》和面,和馅,包,蒸,....自己弄
面向对象:是模型化的,注重的是对象与对象之间的协作,不必去一步一步的实现
例:吃饺子=》点外面,商家做,骑手送,自己吃
优点:易维护,易复用,易扩展,由于面向对象有封装,继承,多态的特性,可以设计出低耦合的系统,使系 统更加灵活,更加易于维护
类:是一种抽象的表示,可以把具有相同特征的事物归为一类,例:人类,车类,鸟类
对象:类别中具体的一个事物
对象的成员:
1.属性:指对象的特征
2.方法:指对象的动作(行为)
定义类
访问修饰符 class 类名{
}
定义方法
访问修饰符 返回值类型 方法名(参数列表){
方法体
}
方法参数:方法中需要数据,但是它本身没有,需要从外部传入时,就要定义参数
形参:形式参数,在方法定义时写的参数
实参:实际参数,在方法调用时传入的参数
返回值:外部需要方法内部数据
1.在定义方法时,需要指定返回值类型
2.在方法体中,使用return关键字,返回一个具体的值
方法重载:方法名相同,参数列表不同(参数的个数不同,参数类型不同)
方法签名:方法名+参数列表
可变长参数/不定长参数:数据类型...变量名。可变长参数必须是最后一个参数
package com.zretc.javase.d0302.p1;
public class Cal {
/*
* 方法重载:方法名相同,参数列表不同(参数个数不同,参数类型不同)
* 方法签名:方法名+参数列表
*/
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
/*
* 可变长参数:数据类型... 变量名
* 可变长参数必须是最后一个参数
*/
// 求多个整数的和
public int add(int... nums) {
int sum = 0;
for (int num : nums) {
sum += num;// sum = sum+num
}
return sum;
}
public double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
Cal cal = new Cal();
// double sum = cal.add(1.2, 1.3);
// System.out.println(sum);
//int[] nums = { 1, 2, 35, 8 };
System.out.println(cal.add(1,2,3,4));
}
}
成员变量(字段,域)
成员变量默认值:
整数类型:0
byte:(byte)0
short:(short)0
int:0
long:0L
浮点类型:0.0
float:0.0F
double:0.0
字符类型:\u0000
布尔类型:false
类类型:null
成员变量和局部变量区别:
1.作用域不同:
局部变量的作用域仅局限于它的方法
成员变量的作用域在整个类内部都是可见的,还可以在外部访问
2.初始值不同
Java会给成员变量一个初始值
Java不会给局部变量一个初始值
3.在同一方法中,不允许有同名局部变量
在不同方法中,可以有同名局部变量
4.两类变量同名时,局部变量具有更高的优先级
面向对象三大特征
封装,继承,多态
封装
1.定义一个方法是一种封装
2.成员变量或方法用private修饰是一种封装
属性组成部分:属性变量和属性访问器
属性变量:用于表示属性的成员变量
属性访问器:setter和getter
属性名:setter和getter去掉set和get后,剩下部分首字母小写
只读属性:只有getter没有setter
private String name;
// 赋值方法 setter set访问器
public void setName(String name) {//name
// this:本类对象
this.name = name;
}
// 取值方法 getter get访问器
public String getName() {
return name;
}
构造器(构造方法)
没有返回值类型,名称和类名相同
默认构造方法
1.如果不创建构造方法,也有一个默认的构造方法
2.默认构造方法没有参数
3.如果创建了有参构造方法,则默认构造方法消失
在构造器中调用本类其他构造器:this(参数)
this()必须写在构造器的第一行代码上
package com.zretc.javase.d0303.p2;
public class Student {
private String name;
private int score;
public Student() {
System.out.println("调用了构造方法");
}
public Student(String name,int score) {
this();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
public static void main(String[] args) {
// 构造方法是在实例化对象时自动调用
Student st = new Student("高斯林", 90);
System.out.println(st.getName() + "," + st.getScore());
}
静态方法(类方法)
是属于类的,不属于对象,使用类调用
静态方法不能调用成员方法,也不能使用成员变量
静态方法不能使用this
成员方法可以调用静态方法,也可以使用静态变量
静态块,静态代码块
static{
}
在第一次使用类时自动调用,而且只调用一次
静态块的执行要优先于主方法的执行
静态块和静态方法的区别:
1.静态块自动调用,静态方法手动调用
2.静态块只能调用一次,静态方法可以调用多次
3.静态块没有访问修饰符,返回值类型,名称,参数,静态方法有
动态块:{},实例化对象时自动调用
继承
减少代码冗余,实现代码重用性
访问修饰符 class 子类 extends 父类{
}
子类又叫派生类
父类又叫基类,超类
单继承:子类只能继承一个父类,如Java
多继承:子类可以继承多个父类,如C++
继承中的构造器
1.先调用父类构造器,再调用子类构造器
2.子类默认调用父类的无参构造器
3.构造器不能继承
4.子类调用父类构造器:super(参数),super()必须写在构造器的第一行代码上
5.如果父类没有无参构造器,则子类必须手动调用父类的其他构造器
super:父类的
super.属性:父类的属性
super.方法(参数):父类的方法
super(参数):父类的构造器,必须放在第一行
this:本类对象
this.属性:当前对象属性
this.方法:当前对象的方法
this(参数):当前构造方法,必须放在第一行
访问修饰符
| 本类 | 本包其他类 | 其他包子类 | 其他包 | |
|---|---|---|---|---|
| public (公有的) | √ | √ | √ | √ |
| protected (保护的) | √ | √ | √ | |
| 默认的 | √ | √ | ||
| private (私有的) | √ |
重写
重写:子类重新写父类的方法
一大两小两相同
两相同:方法名相同,参数列表相同
一大:访问修饰符要大于等于父类
两小:1)返回值类型要小于等于父类的
如果是基本类型和void,必须保持一致
2)抛出异常的类型要小于等于父类的
不能重写的方法:
1.构造方法不能重写
2.私有方法不能重写
3.静态方法不能重写
4.final修饰的方法不能重写
重载与重写的相同点和不同点
| 重写与重载 | 重写(override) | 重载(overload) |
|---|---|---|
| 作用域 | 子重写父的 | 同一作用域(子重载父的,重载自己) |
| 方法名 | 必须一致 | 必须一致 |
| 参数列表 | 必须一致 | 必须不一致 |
| 返回值类型 | 返回值类型要小于等于父类的,如果是基本类型和void必须保持一致 | 无所谓 |
| 访问修饰符 | 子大于等于父 | 无所谓 |
| 异常 | 抛出异常的类型要小于等于父类的 | 无所谓 |
| 次数 | 一次 | 多次 |
多态
同一种行为,产生的结果不同
满足多态的条件:继承 重写 向上转型
面向对象三大特征:封装 继承 多态
面向对象四大特征:封装 继承 多态 抽象
final能修饰什么
1.修饰类,不能被继承
2.修饰方法,不能被重写
3.修饰变量,不能被重复赋值
1)成员变量用final修饰,可以在定义时赋值,也可以在构造器和动态块中赋值
2)常量:一般是公有静态常量,public static final修饰
命名规范:所有字母都大写,每个单词之间用下划线分隔(尖叫蛇底式命名法)
3)局部变量用final修饰,在使用前赋值即可,但只能赋值一次
4.参数上用final修饰,不可以赋值,值是通过方法调用传过来的,在方法内部不能改值
JVM内存
栈
栈帧:JVM为每一个方法都定义了一个栈帧,栈帧中存放局部变量
堆:实例化出来的对象,成员变量
方法区:静态变量,方法信息
抽象
抽象类:类上用abstract修饰
抽象方法:方法上用abstract修饰,没有方法体
抽象类不能实例化对象
抽象类可以有抽象方法,也可以有非抽象方法
如果子类不是抽象类,则必须实现父类的抽象方法
如果子类也是抽象类,则可以不实现父类的抽象方法
private 和 abstract 不能连用
final和abstract 不能连用
接口
是一种公共的规范标准,引用数据类型
定义接口:
[访问修饰符] interface 接口名{
}
1.在默认情况下,接口中的方法都是公有抽象方法(public abstract)
2.接口不能实例化
3.接口中没有构造器
4.接口中的变量,都是公有静态常量(public static final修饰)
5.在Java8中,提供了接口的默认方法(default)和静态方法
6.在Java9中,提供了接口的私有方法
7.定义一个类实现接口的语法:
访问修饰符 class 类名 implements 接口名1,接口名2...{
}
8.类是单继承,接口可以多实现,多继承
9.实现类既可以继承父类,又可以实现接口
访问修饰符 class 类名 extends 父类 implements 接口名1,接口名2...{
}
10.接口继承接口
访问修饰符 interface 接口名 extends 接口名1,接口名2...{
}
11.抽象类与接口
| 抽象类 | 接口 | |
|---|---|---|
| 定义关键字 | abstract class | interface |
| 继承关系 | 单继承 | 多继承 |
| 构造方法 | 有 | 没有 |
| 实例化对象 | 不能 | 不能 |
| 普通成员 | 可以 | 不能有变量,普通方法是jdk8之后可以 |
| 与接口的关系 | 多实现【implements】 | 多继承【extends】 |
instanceof
判断左边的对象是否属于右边类的实例,结果是boolean类型,是返回true,不是返回false
class Tree{}
class Pine extends Tree{}
class Oak extends Tree{}
public class Forest {
public static void main( String[] args ) {
Tree tree = new Pine();
if( tree instanceof Pine ) // true
System.out.println( "Pine" );
if( tree instanceof Tree ) // true
System.out.println( "Tree" );
if( tree instanceof Oak ) // false
System.out.println( "Oak" );
else
System.out.println( "Oops" );
}
}
则输出结果中有哪些? ABD
A.Pine B.Tree C.Forest D.Oops E.无输出
内部类
静态内部类 动态内部类 局部内部类 匿名内部类
静态内部类
访问修饰符 class 外部类{
访问修饰符 static class 内部类{
}
}
public class Outer {
private static int n = 1;
public static class Inner {
private int n = 2;
public void show() {
int n = 3;
System.out.println(Outer.n + "," + this.n + "," + n);
}
}
public void display() {
Inner inner = new Inner();
inner.show();
}
}
在外部访问静态内部类:外部类.内部类 变量 = new 外部类.内部类();
Outer.Inner inner = new Outer.Inner();
inner.show();
动态内部类
访问修饰符 class 外部类{
访问修饰符 class 内部类{
}
}
在外部访问动态内部类:
外部类 变量 = new 外部类();
外部类.内部类 变量 = 外部类变量.new 内部类();
public class Outer {
private int n = 1;
public class Inner {
private int n = 2;
public void show() {
int n = 3;
System.out.println(Outer.this.n + "," + this.n + "," + n);
}
}
public void display() {
Inner inner = new Inner();
inner.show();
}
}
public static void main(String[] args) {
// Outer outer = new Outer();
// outer.display();
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();
}
局部内部类
public class Test {
public static void main(String[] args) {
// 局部内部类
class Inner{
public void show() {
System.out.println("a");
}
}
Inner inner = new Inner();
inner.show();
}
}
匿名内部类
public class Test {
public static void main(String[] args) {
/*
* 匿名内部类
*/
Human h = new Human() {
@Override
public void work() {
System.out.println("工作");
}
};
h.work();
}
}
类能用什么访问修饰符修饰
1.对于外部类,只能用public和默认(不写)
2.对于内部类:
1)如果是静态内部类和动态内部类,四种都可以
2)如果是局部内部类和匿名内部类,不可以写
String
字符串
charAt(int index) :依据索引查找字符
compareTo():字符串比较,正数:第一个串大,负数:第二个串大,0:相等
compareToIgnoreCase:不区分大小写比较
contains:判断是否存在
startsWith:判断是否以某子串开头
endsWith:判断是否以某子串结尾
equals:判断字符串相等
equalsIgnoreCase:不区分大小写判断字符串相等
getBytes():将字符串转成byte数组
indexOf():依据元素查找索引(从左往右查找第一个)
lastIndexOf():依据元素查找索引(从左往右查找最后一个)
length():字符串长度
isEmpty():判断是否是空串
replace(旧串, 新串):字符串替换
split(分隔符):字符串分隔,返回值类型是字符串数组,注意:如果遇到分隔符出现错误,需要将分隔符用\ \转义
substring(开始位置,结束位置):截取子串,不包含结尾位置,含头不含尾、
substring(开始位置):从开始位置截取到最后
toCharArray():字符串转char类型数组
toLowerCase():转成小写
toUpperCase():转成大写
trim():去掉左右两边空格
package com.zretc.javase.d0309;
import java.util.Arrays;
public class StringDemo {
public static void main(String[] args) {
// 01234567
String str = "abcdefga";
// 依据索引查找字符 索引从0开始,最大索引比长度小1
System.out.println(str.charAt(0));
String s1 = "abc";
String s2 = "ABC";
// 字符串比较,正数:第一个串大,负数:第二个串大,0:相等
System.out.println(s2.compareTo(s1));
// 不区分大小写的比较
System.out.println(s1.compareToIgnoreCase(s2));
// 判断是否存在
System.out.println(str.contains("df"));
// 判断是否以某子串开头
System.out.println(str.startsWith("ab"));
// 判断是否以某子串结尾
System.out.println(str.endsWith("ab"));
// 判断字符串相等
System.out.println(s1.equals(s2));
// 不区分大小写判断字符串相等
System.out.println(s1.equalsIgnoreCase(s2));
// 将字符串转成字节数组
byte[] bs = str.getBytes();
// 将数组转字符串输出
System.out.println(Arrays.toString(bs));
// 依据元素查找索引(从左往右)
System.out.println(str.indexOf("a"));
// 依据元素查找索引(从左往右找最后一个)
System.out.println(str.lastIndexOf("a"));
// 判断是否是空串
System.out.println(str.isEmpty());
// 字符串长度:str.length()
System.out.println(str.length() == 0);
// 字符串替换
System.out.println(str.replace("ab", "q"));
/*
* 字符串分割:返回值类型是字符串类型数组
* 如果遇到分隔符出现错误,需要将分隔符用\\转义
*/
String strArr = "tom.amy.john";
String[] arr = strArr.split("\\.");
System.out.println(Arrays.toString(arr));
/*
* 截取子串:字符串.substring(开始位置,结束位置):不包含结尾位置,含头不含尾、
* 字符串.substring(开始位置):从开始位置截取到最后
*/
String str2 = "ABC";
System.out.println(str2.substring(1));
// 字符串转char类型数组
char[] cs = str2.toCharArray();
System.out.println(Arrays.toString(cs));
// 转成大写
System.out.println(str2.toUpperCase());
// 转成小写
System.out.println(str2.toLowerCase());
String ss = " abc ";
// 去掉左右两边空格
System.out.println(ss.trim());
}
}
字符串常量池:程序当中直接写上的双引号字符串,就在你的字符串常量池里
对于基本类型:==是进行数值的比较
对于引用类型:==是进行地址值的比较
地址相同的情况:
情况1:
String str1 = "abc";
String str2 = "abc";
情况2:
String str1 = "abc";
String str2 = "ab"+"c";
地址不相同的情况:
情况1:
String str1 = "abc";
String s = "c";
String str2 = "ab" + s;
情况2:
String str1 = new String("abc");
String str2 = new String("abc");
情况3:
Scanner sc = new Scanner(System.in);
String str1 = "abc";
String str2 = sc.next();
StringBuilder,StringBuffer
专门用来字符串拼接的,拼接时不生成新的对象,拼接速度快
String,StringBuilder,StringBuffer的区别:
1.String是常量,值不能改变,拼接字符串时生成新的对象,拼接速度慢
StringBuilder和StringBuffer是动态拼接,拼接时不生成新的对象,拼接速度快
2.StringBuilder和StringBuffer提供了更多的方法,如插入删除等
3.StringBuilder是线程不安全的,拼接速度稍快
StringBuffer是线程安全的,拼接速度稍慢
StringBuilder s = new StringBuilder();
// 添加字符串
s.append("ab");
s.append("Hello");
System.out.println(s);
// 插入字符串
s.insert(1, "q");
System.out.println(s);
// 删除字符串 含头不含尾
s.delete(1, 4);
System.out.println(s);
StringBuilder ss = new StringBuilder("abcd");
// 字符串反转
System.out.println(ss.reverse());
包装类型
每一个基本类型,都对应着一个类,它将基本类型封装到类中,添加了额为得功能,还可以赋值为null。
| 基本类型 | 包装类型 | 父类型 |
|---|---|---|
| byte | Byte | Number |
| short | Short | Number |
| int | Integer | Number |
| long | Long | Number |
| float | Float | Number |
| double | Double | Number |
| char | Character | Object |
| boolean | Boolean | Object |
装箱:基本类型转换成包装类型的过程
拆箱:包装类型转换成基本类型的过程
【手动拆装箱】
Java 5 之前
手动装箱:将基本数据写在包装类型的构造器中
手动拆箱:包装类型变量.xxxValue();
【自动拆装箱】
自动装箱:包装类型 变量 = 基本类型值 Integer a = 12;
自动拆箱:基本类型 变量 = 包装类型变量 int b = a;
自动装箱的注意事项:赋值时,要严格对应上类型。
如:Long num = 1L;
Double num2 = 10D;
包装类型比较用equals方法
【整型池】
整型缓存池,整型常量池
范围:-128~127
Integer a = 128;
Integer b = 128;
System.out.println(a >= b);// true
首先将两个包装类型变量自动拆箱成基本类型,然后比较基本类型的值
类型转换:
包装类型.parse类型(字符串):返回基本类型
包装类型.valueOf(字符串):返回包装类型
String str = "12";
int num = Integer.parseInt(str);
Integer num2 = Integer.valueOf(str);
MAX_VALUE:最大值
MIN_VALUE:最小值
SIZE:位数
Double
NaN:Not a Number
NaN和任何一个数都不相等,包括它自己
Double.isNaN():判断是否是NaN
Double.NaN:0.0d / 0.0
Infinity:正无穷大
-Infinity:负无穷大
Double.POSITIVE_INFINITY:1.0/0.0
Double.NEGATIVE_INFINITY:-1.0/0.0
double num = 0.0 / 0.0;
System.out.println(num);// NaN:Not a Number
System.out.println(num == num);// false, NaN和任何一个数都不相等,包括它自己
System.out.println(Double.isNaN(num));// 判断是否是NaN
System.out.println(Double.NaN);
System.out.println(-1.0 / 0.0);
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.NEGATIVE_INFINITY);
double a = Double.NEGATIVE_INFINITY;
System.out.println((a + 1) == a);// true
Character
public static void main(String[] args) {
char c = ' ';
// 判断是否是整数
System.out.println(c >= '0' && c <= '9');
System.out.println(Character.isDigit(c));
// 判断是否是小写字母
System.out.println(c >= 'a' && c <= 'z');
System.out.println(Character.isLowerCase(c));
// 判断是否是大写字母
System.out.println(c >= 'A' && c <= 'Z');
System.out.println(Character.isUpperCase(c));
// 判断是否是字母
System.out.println((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
System.out.println(Character.isLetter(c));
// 判断是否是字母或者数字
System.out.println((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'));
System.out.println(Character.isLetterOrDigit(c));
// 判断是否是空白字符
System.out.println(Character.isWhitespace(c));
}
正则表达式
Java是从Java 1.4开始支持的正则表达式
正则表达式,简写为regex,regxp,regexp
是用于匹配、查找、替换文本等操作的表达式
正则表达式中包含普通字符和元字符
元字符就是一类特殊的字符,有具体的含义
^、$:开始和结束
[abc] :匹配abc中任意字符
[^abc] :除了abc的任意字符
[a-z] :a到z之间的字符
[a-zA-Z0-9] :a到z,A到Z,0到9之间的任意字符
\d :数字字符,等效于[0-9]
\w :单词字符,等效于[a-zA-Z0-9_]
\s :空白字符,如空格,制表符,换行,回车
\D :非数字字符
\W :非单词字符
\S :非空白字符
? :0到1个
*:0到多个
+:1到多个
. :任意字符
| :或者
\ :转义
X{n}:n个X
X{n,}:n到多个X
X{n,m}:n到m个X
^:非
():分组
public static void main(String[] args) {
String str = "中国";
// 验证是否是整数
boolean ok = str.matches("^\\d+$");
System.out.println(ok);
// 验证是否是小数
boolean ok2 = str.matches("^\\d+\\.\\d+$");
System.out.println(ok2);
// 验证两位小数
boolean ok3 = str.matches("^\\d+\\.\\d{2}$");
System.out.println(ok3);
// 验证整数或者小数
boolean ok4 = str.matches("^\\d+(\\.\\d+)?$");
System.out.println(ok4);
// 验证大多数汉字 :\u4E00 \u9FA5
boolean ok5 = str.matches("^[\u4E00-\u9FA5]+$");
System.out.println(ok5);
}
public static void main(String[] args) {
String str = "abc123dea456wers";
// str = str.replace("123", "-");
str = str.replaceAll("\\d+", "-");
System.out.println(str);
String str2 = "Java.Python.PHP";
String[] ss = str2.split("\\.");
System.out.println(Arrays.toString(ss));
}
public static void main(String[] args) {
// String str = "Java123Python456";
// // 模式对象,设置正则表达式
// Pattern p = Pattern.compile("[a-zA-Z]+");
// // 匹配对象,匹配字符串
// Matcher m = p.matcher(str);
// // 判断有没有找到匹配上的字符串
// while (m.find()) {
// System.out.println(m.group());// 匹配上的字符串
// }
// 键值对
String str = "n@ame=to@%m;salary=18000;";
Pattern p = Pattern.compile("([^=]+)=([^;]+);");
Matcher m = p.matcher(str);
while (m.find()) {
// m.group(组索引):从1开始
System.out.println(m.group(1) + "\t" + m.group(2));
}
}
Object
Object是所有类的父类,又叫顶级父类,也叫万类之源
如果一个类没有指定父类,也默认继承了Object
除了Object之外,剩下的所有类都有父类
Object中有一些方法,有的时候我们需要重写:
如:toString():默认是返回类全名@哈希值
一般用作返回类中信息(成员变量的值),所以需要重写
对于print和println,如果传入类类型变量,就是调用toString方法
equals(),equals方法是Object中的方法,默认是判断地址相等(==)
但是官方建议你重写equals(),用于比较值相等
(比如String,在String类中定义的equals能够比较值相等,是因为String中重写了equals)
public class Emp {
private String name;
private double salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
// 一般用作返回类中信息(成员变量的值),所以需要重写
@Override
public String toString() {
return "Emp [name=" + name + ", salary=" + salary + "]";
}
}
public static void main(String[] args) {
Emp emp = new Emp();
emp.setName("tom");
emp.setSalary(18000);
// 对于print和println,如果传入的类类型变量,就是调用了toString方法
System.out.println(emp);
Emp emp2 = new Emp();
emp2.setName("tom");
emp2.setSalary(18000);
System.out.println(emp.equals(emp2));// false
}
Math
Math.max():最大值
Math.min():最小值
Math.round():四舍五入
Math.ceil():向上取整
Math.floor():向下取整
Math.pow():求幂
Math.sqrt():开平方
Math.cbrt():开立方
Math.abs():绝对值
Math.PI:Π
Random
随机数
1.Math.random() 生成0~1之间的随机小数
生成随机数[min,max) =》 int(Math.random()*(max-min))+min
2.Random类
Random rand = new Random();
rand.nextInt(上限):[0,上限) 随机整数
生成随机数[min,max) =》rand.nextInt(max-min)+min;
Date
date:日期类
Date date = new Date();调用了System.currentTimeMillis() 系统毫秒数
getTime():获取系统毫秒数
setTime():设置系统毫秒数
compareTo:日期比较,返回值:正数第一个日期大,负数第二个日期大,0相同
after:判断日期是否是另一个日期之后
before:判断日期是否是另一个日期之前
SimpleDateFormat,简单日期格式,用来做日期的格式化
格式化字符
y:年(yyyy,四位及以上;yy:两位年)
M:月(MM:两位月)
d:日(dd,两位日期)
h:12进制小时(hh,两位小时)
H:24进制小时(HH,两位小时)
m:分钟
s:秒
S:毫秒
E:星期
a:上午/下午
如果想直接显示字母,需要加上单引号
public static void main(String[] args) throws ParseException {
Date date = new Date();
// 如果想直接显示字母,需要加上单引号
SimpleDateFormat fmt = new SimpleDateFormat("yyyy年MM月dd日 'at' HH:mm:ss");
// Date对象转格式化字符串
String str = fmt.format(date);
System.out.println(str);
// 格式化字符串转Date对象 字符串与日期模板不一致时,日期转换异常:ParseException
// Date date2 = fmt.parse(str);
Date date2 = fmt.parse(str,new ParsePosition(0))
System.out.println(date2);
}
Calendar
获取年份:cal.get(Calendar.YEAR)
获取月份:cal.get(Calendar.MONTH)+1,月份从0开始,所以真实月份需要加1
获取日期:cal.get(Calendar.DATE) 或者 cal.get(Calendar.DAY_OF_MONTH)
获取小时(12进制):cal.get(Calendar.HOUR)
获取小时(24进制):cal.get(Calendar.HOUR_OF_DAY)
获取分钟:cal.get(Calendar.MINUTE)
获取秒:cal.get(Calendar.SECOND)
获取星期,星期从1开始,星期日是1:cal.get(Calendar.DAY_OF_WEEK)-1
添加字段的值:cal.add(字段的常量,值)
滚动字段的值:cal.roll(字段的常量, 值)
add 和 roll的区别:
add:如果超出文本字段的范围,会影响到上一个字段
roll:如果超出文本字段的范围,不会影响上一个字段,会在本字段中循环滚动
当前日期是当前年份的第几天:cal.get(Calendar.DAY_OF_YEAR)
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 设置字段:cal.set(字段常量,值)
cal.set(Calendar.YEAR, 2023);
cal.set(Calendar.MONTH, 2 - 1);
cal.set(Calendar.MONDAY, Calendar.OCTOBER);
// 设置年月日
cal.set(2022, 2 - 1, 28);
// 获取Date对象
Date date = cal.getTime();
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(fmt.format(date));
Calendar cal2 = Calendar.getInstance();
// 将Date对象传到Calendar中
cal2.setTime(date);
System.out.println(cal2.get(Calendar.YEAR));
// 获取系统毫秒数
System.out.println(cal2.getTimeInMillis());
// 设置系统毫秒数
cal2.setTimeInMillis(1646976305614L);
}
泛型
参数化的类型
泛型:泛型方法,泛型类
泛型不支持基本类型,不能实例化对象
Java中的泛型,是Java5增加的特性
擦除法泛型,Java的泛型是语法层面的,编译之后,泛型类型是Object
在Java7之前:类名<泛型类型> 变量 = new 类名<泛型类型>();
在Java7之后:类名<泛型类型> 变量 = new 类名<>();
集合
List是Collection的子接口,主要存储有序可重复数据
ArrayList:数组列表,使用数组实现
List
追加元素:list.add(元素);
插入元素:list.add(索引,元素);
修改元素:list.set(索引, 元素);
删除元素(依据索引删除):list.remove(索引);
删除元素(依据元素删除):list.remove(元素);
注意:如果泛型类型是Integer,使用remove时,传入元素,会当成索引,所以需要将元素强制类型转换成Integer或Object
依据索引获取元素:list.get(索引)
集合长度:list.size();
清空集合:list.clear();
public static void main(String[] args) {
// List是Collection的子接口,主要存储有序可重复数据
List<String> list = new ArrayList<>();
// 添加元素
list.add("任均宇");
list.add("何书龙");
System.out.println(list);
// 插入元素
list.add(1, "王博斓");
System.out.println(list);
// 修改元素
list.set(1, "任均宇");
System.out.println(list);
// 删除元素(依据索引删除)
list.remove(2);
System.out.println(list);
// 删除元素(依据元素删除)
list.remove("任均宇");
System.out.println(list);
// 获取元素
System.out.println(list.get(0));
// 集合长度
System.out.println(list.size());
}
遍历集合三种方式:
List<Integer> list = new ArrayList<>();
list.add(1433);
list.add(1521);
list.add(3306);
// 遍历集合,普通for循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("=============");
// 遍历集合,增强for循环
for(Integer num : list){
System.out.println(num);
}
System.out.println("=============");
// 遍历集合,使用迭代器
// 获取迭代器
Iterator<Integer> it = list.iterator();
// 判断有没有下一条数据
while(it.hasNext()){
// 移动到下一条数据,并获取数据
System.out.println(it.next());
}
Collections:集合工具类
Collections.addAll(集合, 元素,...):向集合中添加若干元素
Collection和Collections的区别
1.Collection是集合的总接口,是存储单一类型的集合
2.Collections是集合工具类,提供操作集合的相关方法
判断是否有某串:list.contains()
依据元素查找索引(从左往右找第一个):list.indexOf()
依据元素查找索引(从左往右找最后一个):list.lastIndexOf()
判断集合是否为空:list.isEmpty()
获取子集合(含头不含尾):list.subList()
实际上是从集合中获取一部分,但是还是同一个集合,一个修改,会影响另一个
如果想互不影响,需要将list.subList()放到集合实现类如ArrayList的构造器中
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
/*
* Collections:集合工具类
* Collections.addAll(集合, 元素,...):向集合中添加若干元素
*/
Collections.addAll(list, 1521, 1433, 3306);
Collections.addAll(list, 8080,1433);
System.out.println(list);
// 判断是否有某子串
System.out.println(list.contains(1433));
// 依据元素查找索引(从左往右找第一个)
System.out.println(list.indexOf(1433));
// 依据元素查找索引(从左往右找最后一个)
System.out.println(list.lastIndexOf(1433));
// 判断集合是否为空
System.out.println(list.isEmpty());
System.out.println(list.size() == 0);
/*
* 获取子集合(含头不含尾)
* 实际上是从集合中获取一部分,但是还是同一个集合,一个修改,会影响另一个
* 如果想互不影响,需要将list.subList()放到集合实现类的构造器中
*/
List<Integer> sub = new ArrayList<>(list.subList(1, 3));
sub.set(0, 9090);
System.out.println(list);
System.out.println(sub);
}
数组转List:
Arrays.asList():转成了只读集合
正确做法:将Arrays.asList()放在List实现类的构造器中
List<泛型类型> 变量 = new ArrayList<>(Arrays.asList(数组));
Integer[] nums = { 1, 2, 3 };
List<Integer> list = new ArrayList<>(Arrays.asList(nums));
list.add(0, 10);
System.out.println(list);
List转数组:
方式一:类型[] 数组 = list.toArray(new 类型[0]);
方式二:类型[] 数组 = new 类型[集合长度];
集合.toArray(arr);
// 方式一
Integer[] arr = list.toArray(new Integer[0]);
// 方式二
// Integer[] arr = new Integer[list.size()];
// list.toArray(arr);
System.out.println(Arrays.toString(arr));
排序
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 3306, 1433, 1521);
// 正序排序
Collections.sort(list);
System.out.println(list);
/*
* 正序排序:第一个数减第二个数
* 倒叙排序:第二个数减第一个数
*/
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2 - o1;
}
});
System.out.println(list);
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1 - o2;
}
});
System.out.println(list);
List<String> strList = new ArrayList<>();
Collections.addAll(strList, "a", "b", "c", "z", "e");
Collections.sort(strList);// 正序
System.out.println(strList);
// 倒序
strList.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return o2.compareTo(o1);
}
});
System.out.println(strList);
}
LinkedList:链表集合
是用链表实现的
ArrayList和LinkedList的区别:
1.ArrayList是用数组实现的,在内存中连续的,适合遍历和追加
2.LinkedLIst是用链表实现的,在内存中不是连续的,适合插入和删除
LinkedList能够实现队列和栈
队列:先进先出(FIFO)
栈:先进后出(FILO)
Queue<类型> queue = new LinkedList<>();
添加元素:queue.offer();
获取元素(不删除):queue.peek();
获取元素并删除:queue.poll();
Deque<类型> statck = new LinkedList<>();
进栈:stack.push();
出栈:stack.pop();
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
// 添加方法
queue.offer(10);
queue.offer(20);
queue.offer(30);
System.out.println(queue);
// 获取元素(不删除)
// System.out.println(queue.peek());
// System.out.println(queue.peek());
// // 获取元素并删除
// System.out.println(queue.poll());
// System.out.println(queue.poll());
// System.out.println(queue.poll());
for (Integer num : queue) {
System.out.println(num);
}
System.out.println("================");
Deque<Integer> stack = new LinkedList<>();
// 进栈
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println(stack);
// 出栈
// System.out.println(stack.pop());
// System.out.println(stack.pop());
// System.out.println(stack.pop());
for(Integer n : stack) {
System.out.println(n);
}
System.out.println(stack.size());
while(stack.size()>0) {
System.out.println(stack.pop());
}
Set集合是Collection的子接口,无序存储,不可重复存储,可以null值
HashSet:无序的
LinkedHashSet:添加的顺序
TreeSet:排序的(默认是正序排序)
添加元素:add()
依据元素删除:remove(元素)
获取长度:size()
遍历Set集合
// 遍历Set集合
for (String str : phone) {
System.out.println(str);
}
System.out.println("=============");
Iterator<String> it = phone.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
Set集合转List集合,放到List集合实现类的构造器中
List集合转Set集合,放到Set集合实现类的构造器中
public static void main(String[] args) {
Set<String> phone = new HashSet<>();
phone.add("13312345688");
phone.add("13912345688");
phone.add("13612345688");
// Set集合转List:放到List集合实现类的构造器中
List<String> list = new ArrayList<>(phone);
list.add(1, "13612345688");
System.out.println(list);
// List集合转Set集合,放到Set集合实现类的构造器中
Set<String> set = new HashSet<>(list);
System.out.println(set);
}
public static void main(String[] args) {
// 倒序排序:将比较器放在TreeSet的构造器中
Set<String> phone = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return o2.compareTo(o1);
}
});
phone.add("13312345688");
phone.add("13912345688");
phone.add("13612345688");
System.out.println(phone);
}
Map集合以键值对的形式存在,键是不能重复的,值是可以重复的
HashMap:无序的
LinkedHashMap:添加的顺序
TreeMap:排序的(默认按照键的正序顺序)
添加:put(键, 值)
修改:put()
删除(依据键删除):remove(键)
是否包含某键:containsKey()
获取长度:size()
获取键集合:keySet()
获取值集合:values()
依据键获取值:get(键)
清空集合:clear()
public static void main(String[] args) {
/*
* Map集合,是一种键值对集合
* HashMap:无序的
* LinkedHashMap:添加的顺序
* TreeMap:排序的(默认按照键的正序排序)
*/
Map<String,String> map = new HashMap<>();
// 添加
map.put("name", "高斯林");
map.put("gender", "男");
map.put("job", "架构师");
System.out.println(map);
// 修改
map.put("job", "程序员");
System.out.println(map);
// 删除(依据键删除)
map.remove("job");
System.out.println(map);
// 是否包含某键
System.out.println(map.containsKey("name"));
// 集合长度
System.out.println(map.size());
// 获取键集合
System.out.println(map.keySet());
// 获取值集合
System.out.println(map.values());
// 依据键获取值
System.out.println(map.get("name"));
// 清空集合
map.clear();
System.out.println(map.size());
}
遍历Map集合
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("name", "高斯林");
map.put("gender", "男");
map.put("job", "程序员");
// 遍历Map集合方式一,通过遍历键集合
for (String str : map.keySet()) {
System.out.println(str + "\t" + map.get(str));
}
System.out.println("=============");
/*
* 遍历Map集合方式二:通过遍历键值对对象
* Entry:键值对对象
* entry.getKey():获取键
* entry.getValue():获取值
*/
for (Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
System.out.println("==============");
// 只遍历值
for(String s : map.values()) {
System.out.println(s);
}
}
public static void main(String[] args) {
// Map<String,String> map = new LinkedHashMap<>();
// 倒序:将比较器放在TreeMap的构造器中
Map<String, String> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
map.put("name", "高斯林");
map.put("gender", "男");
map.put("job", "架构师");
System.out.println(map);
}
Properties:属性集合,键值对都是字符串,可以保存到文件中
添加值:setProperty(键,值);
获取值:getProperty(键);
获取值:getProperty(键,默认值); 如果没有键,就返回默认值
HashMap和Hashtable的区别:
1.HashMap是线程不安全的,速度稍快,推荐使用,键值可以存放null
2.Hashtable是线程安全的,速度稍慢,不推荐使用,键值不能存放null
异常
语法:try-catch,try-catch-finally,try-finally
【try-catch】
try {
// 可能会出现异常的代码
} catch(异常对象) {
// 处理异常,捕捉到异常之后会执行的代码
}
【try-catch-finally】
try {
// 可能会出现异常的代码
} catch(异常对象) {
// 处理异常,捕捉到异常之后会执行的代码
} finally{
// 无论是否有异常,都会执行(注意:System.exit(0)退出程序除外)
}
【try-finally】
try {
// 可能会出现异常的代码
} finally{
// 无论是否有异常,都会执行(注意:System.exit(0)退出程序除外)
}
| Throwable(Java语言中所有错误的父类) | -- Exception(异常) | |
|---|---|---|
| -- 运行时异常(非检查异常),RuntimeException |
| -- ArithmeticException(算术异常) | -- NumberFormatException(数字格式异常) | -- NullPointerException(空指针异常) | -- StringIndexOutOfBoundsException(字符串索引越界异常) | -- ArrayIndexOutOfBoundsException(数组索引越界异常) | -- IndexOutOfBoundsException(索引越界异常) | -- ClassCastException(类型转换异常) | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| -- 非运行时异常(编译异常,检查异常),Exception | ||||||||||||||||||||
| -- Error(错误) |
Exception:程序中出现的异常
Error:一般指虚拟机出现的错误
运行时异常:编写程序和编译程序时不会出现异常,但是运行的时候有可能出现异常
非运行时异常:在编写程序或编译程序时就出现了异常,需要异常处理
catch:可以有多个,但是范围必须由小到大
public class Test {
public static void main(String[] args) {
/*
* try-catch try-catch-finally try-finally
* 【try-catch 】
* try{
* // 可能会出现异常的代码
* }catch(异常对象){
* // 处理异常,捕捉到异常之后会执行的代码
* }
*
* catch可以有多个,但是范围必须是由小到大
*
* 常见的运行时异常:
* ArithmeticException:算术异常
* NumberFormatException:数字格式异常
* NullPointerException:空指针异常
* StringIndexOutOfBoundsException:字符串索引越界异常
* ArrayIndexOutOfBoundsException:数组索引越界异常
* IndexOutOfBoundsException:索引越界异常
* ClassCastException:类型转换异常
*/
try {
// int a = 0 / 0;
// Integer.parseInt("abc");
// String str = null;
// str.length();
// String str = "";
// str.charAt(0);
// int[] nums = new int[0];
// nums[0] = 10;
// List<Integer> list = new ArrayList<>();
// list.set(0, 10);
// Object obj = "abc";
// Integer num = (Integer)obj;
Object[] num = {1,2,3};
Integer[] aa = (Integer[])num;
} catch (NumberFormatException e) {
System.out.println("异常类型:" + e.getClass());
System.out.println("异常信息:" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (Exception e) {
System.out.println("异常类型:" + e.getClass());
System.out.println("异常信息:" + e.getMessage());
}
System.out.println("good");
}
}
抛异常:
1.在方法定义时,指定能够抛出哪些异常的类型
2.throw new 异常对象();
自定义异常:可以继承RuntimeException,调用一个父类的有参构造方法
public class Test02 {
public static void getDate(String s) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = fmt.parse(s);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("出现了日期转换异常");
}
System.out.println(date);
}
/*
* 重写中:
* 子类称谢方法的异常对象要小于等于父类的异常对象
* 父类的方法没有异常,子类重写的方法也不能有异常出现
*/
public static void getDate2(String s) throws ParseException {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
Date date = fmt.parse(s);
System.out.println(date);
}
/*
* 抛异常:
* 1.在方法定义时,指定能够抛出哪些类型的异常
* 2.抛出异常:throw new 异常对象()
*/
public static void setAge(int age) throws AgeException {
if (age < 0) {
throw new AgeException("年龄错误");
}
System.out.println("年龄:" + age);
}
public static void main(String[] args) {
getDate("2000-03");
try {
getDate2("2022-03");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("出现了日期转换异常");
}
System.out.println("=========================");
try {
setAge(-10);
} catch (Exception e) {
// TODO Auto-generated catch block
// e.printStackTrace();
System.out.println("年龄异常");
}
System.out.println("ok");
setAge(10);
}
}
public class AgeException extends RuntimeException {
public AgeException(String message) {
super(message);
}
}
try:将有可能出现异常的代码放在try里
catch:通过大于等于本身异常进行捕获
finally:无论是否有异常,都会执行的代码(System.exit(0)退出程序除外)
try可以分别和catch,finally连用,也可以三个一起使用
try,catch里有return,finally也会执行,且在return之前执行
public class Test04 {
public static int show() {
int a = 1;
try {
return a++;
}catch(Exception e) {
return a++;
}finally {
return a++;
}
}
public static void main(String[] args) {
System.out.println(show());// 2
}
}
final,finally,finalize的区别:
final修饰类不可被继承,修饰方法不可被重写,修饰变量值不能改变
finally在异常处理中,一定能执行的部分(除了System.exit(0))
finalize是Object中的方法,在销毁对象之前调用,不建议重写
File
File在io包中
IO:Input,Output,输入输出
文件路径:推荐使用相对路径,不要使用绝对路径(完整的路径,如d:\a.txt)
使用绝对路径不能跨平台
相对路径,是从项目路径开始
File file = new File("files/a.txt");
判断文件是否存在:file.exists()
创建文件:file.createNewFile()
文件长度:file.length()
文件路径(相对路径):file.getPath()
路径分隔符:/(Unix、Linux的分隔符,Windows也识别)
不要使用 \ (只有DOS和Windows识别)
获取当前系统的分隔符:File.separator
文件路径(绝对路径):file.getAbsolutePath()
设置最后修改时间:file.setLastModified()
获取最后修改设置:file.lastModified()
只获取文件名(不包含路径):file.getName()
获取父级名称:file.getParent()
设置只读:file.setReadOnly()
设置是否可写:file.setWritable(false) false不可写,true可写
获取是否是可写:file.canWrite()
是否是隐藏文件:file.isHidden()
判断是否是文件:file.isFile()
判断是否是文件夹(目录):file.isDirectory()
创建文件夹:file.mkdirs()
删除文件夹:file.delete()
获取到文件夹下面的所有的文件:file.listFiles() File[]
File[] files = file.listFiles();
for(File f : files) {
if(f.isFile()) {
System.out.print("文件:");
}else {
System.out.print("目录:");
}
System.out.println(f.getName());
}
IO流
| IO流 | -- 字节流(适合操作二进制数据) | ||
|---|---|---|---|
| -- InputStream(输入流) | |||
| -- FileInputStream(文件输入流) | |||
| -- BufferedInputStream(缓冲输入流) | |||
| -- DataInputStream(数据输入流) | |||
| -- ObjectInputStream(对象输入流) | |||
| -- ... ... | |||
| -- OutputStream(输出流) | |||
| -- FiltOutputStream(文件输出流) | |||
| -- FilterOutputStream(过滤输出流) | |||
| -- BufferedOutputStream(缓冲输入流) | |||
| -- DataOutputStream(数据输出流) |
| -- PrintStream(打印流) | -- ObjectOutputStream(对象输出流) | -- ... ... | ||||
|---|---|---|---|---|---|---|
| -- 字符流(适合操作文本数据) | ||||||
| -- Reader(读取流) | ||||||
| -- InputStreamReader(输入流读取器) | ||||||
| -- FileReader(文件读取器) | ||||||
| -- BufferedReader(缓冲读取器) | ||||||
| -- ... ... | ||||||
| -- Writer(写入流) | ||||||
| -- OutputStreamWriter(输出流写入器) | ||||||
| -- FileWriter(文件写入器) | ||||||
| -- BufferedWriter(缓冲写入器) | ||||||
| -- PrintWriter(打印写入器) | ||||||
| -- ... ... |

FileOutputStream:文件输出流,向文件中写入数据
FileOutputStream fos = new FileOutputStream("f1.txt", true);
FileOutputStream构造器参数:参数1:文件名,参数2:是否追加(默认不追加,true追加)
fos.write():向文件中写入数据
字符串.getBytes(编码):将字符串转换成指定编码格式的字节数组
FileOutputStream fos = new FileOutputStream("f1.txt", true);
// write():向文件中写入数据
fos.write(97);
fos.write(98);
fos.write(99);
fos.write(100);
fos.write('\n');
byte[] bs = { 65, 66, 67, 68, 69 };
// fos.write(bs);
fos.write(bs, 1, 3);
fos.write('\n');
// 字符串.getBytes(编码):将字符串转换成指定编码格式的字节数组
fos.write("Hello World".getBytes("UTF-8"));
fos.write("Hello Java".getBytes("UTF-8"));
fos.close();
FileInputStream:文件输入流,从文件中读取数据
FileInputStream fis = new FileInputStream("f1.txt");
fis.read(byte数组):将数据读取到byte数组中
将byte数组转字符串:new String(byte数组,开始位置,长度,编码格式);
FileInputStream fis = new FileInputStream("f1.txt");
byte[] bs = new byte[10240];// 每次最多能够读取的长度
int len;// 真实的长度
String str = "";
while ((len = fis.read(bs)) > 0) {
// 将byte数组转字符串:new String(bs,开始位置,长度,编码格式)
str += new String(bs, 0, len, "UTF-8");
}
fis.close();
System.out.println(str);
拷贝:
package com.zretc.javase.d0321;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
public class CopyTest {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入源文件");
String str = sc.next();
System.out.println("请输入目标文件");
String str2 = sc.next();
FileInputStream fis = new FileInputStream(str);
FileOutputStream fos = new FileOutputStream(str2);
byte[] bs = new byte[10240];
int len;
while((len=fis.read(bs)) != -1) {
fos.write(bs);
}
fis.close();
fos.close();
System.out.println("拷贝成功!!");
File file = new File(str);
File file2 = new File(str2);
file2.setLastModified(file.lastModified());
}
}
BufferedOutputStream,BufferedInputStream:缓冲流
低级流,节点流:直接操作硬件设备的流,比如FileOutputStream
高级流,处理流:不直接操作硬件设备,需要传入一个流来进行操作,如BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f2.txt"));
bos.write("Hello World".getBytes("UTF-8"));
bos.write("Hello World".getBytes("UTF-8"));
bos.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f2.txt"));
byte[] bs = new byte[10240];
int len;
String str = "";
while ((len = bis.read(bs)) > 0) {
str += new String(bs, 0, len, "UTF-8");
}
bis.close();
System.out.println(str);
DataOutputStream,DataInputStream:数据流,高级流
public static void main(String[] args) throws IOException {
// DataOutputStream,DataInputStream:数据流,高级流
DataOutputStream dos = new DataOutputStream(new FileOutputStream("f3.txt"));
dos.writeInt(100);
dos.writeUTF("hello");
dos.writeDouble(3.14);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("f3.txt"));
System.out.println(dis.readInt());
System.out.println(dis.readUTF());
System.out.println(dis.readDouble());
dis.close();
}
ObjectOutputStream,ObjectInputStream:对象流,高级流
ObjectOutputStream:将对象转成byte数组
ObjectInputStream:将byte数组转对象
序列化:将对象转成byte数组、字符串等
反序列化:将byte数组、字符串等转成对象
import java.io.Serializable;
// 若想进行二进制序列化,实现Serializable(可序列化接口)
public class Emp implements Serializable{
private String name;
private double salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Emp [name=" + name + ", salary=" + salary + "]";
}
}
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("emp.txt"));
Emp emp = new Emp();
emp.setName("tom");
emp.setSalary(18000);
// 对象写入到流中(序列化的过程)
oos.writeObject(emp);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("emp.txt"));
// 数据写入到对象(反序列的过程)
Emp emp2 = (Emp) ois.readObject();
ois.close();
System.out.println(emp);
}
PrintStream :打印流
public static void main(String[] args) throws FileNotFoundException {
// PrintStream 打印流
// System.out.println("Hello");
PrintStream ps = new PrintStream("p.txt");
ps.print("hello");
ps.close();
// 输出在控制台
// PrintStream ps = new PrintStream(System.out);
// ps.print("Hello");
// ps.close();
}
字符流
FileWriter,FileReader,文件读写,低级流(节点流)
public static void main(String[] args) throws IOException {
// FileWriter,FileReader
FileWriter fw = new FileWriter("w1.txt");
fw.write(97);
fw.write(98);
fw.write(99);
char[] cs = { '你', '好', '你', '是', '小', '丑', '!' };
fw.write(cs);
fw.write(cs, 0, 2);
fw.write("helloworld", 2, 3);
fw.write("高斯林\n");
fw.write("埃里森");
fw.close();
FileReader reader = new FileReader("w1.txt");
char[] c = new char[10240];
int len;
String str = "";
while ((len = reader.read(c)) != -1) {
str += new String(c, 0, len);
}
reader.close();
System.out.println(str);
}
BufferedWriter, BufferedReader 缓冲读写流,高级流
public static void main(String[] args) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("w2.txt"));
writer.write("高斯林");
writer.close();
BufferedReader reader = new BufferedReader(new FileReader("w2.txt"));
char[] cs = new char[10240];
int len;
String str = "";
while ((len = reader.read(cs)) > 0) {
str = new String(cs, 0, len);
}
reader.close();
System.out.println(str);
}
public static void main(String[] args) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("w3.txt"),"UTF-8");
writer.write("高斯林");
writer.close();
InputStreamReader reader = new InputStreamReader(new FileInputStream("w3.txt"),"UTF-8");
char[] cs = new char[10240];
int len;
String str = "";
while ((len = reader.read(cs)) > 0) {
str = new String(cs, 0, len);
}
reader.close();
System.out.println(str);
}
PrintWriter 打印写入器
public static void main(String[] args) throws FileNotFoundException {
// PrintWriter 打印写入器
PrintWriter out = new PrintWriter("p2.txt");
out.println(100);
out.println("Hello World");
out.close();
// Scanner sc = new Scanner(new File("p2.txt"));
// System.out.println(sc.nextInt());
// sc.nextLine();
// System.out.println(sc.nextLine());
Scanner sc = new Scanner(new File("p2.txt"));
System.out.println(Integer.parseInt(sc.nextLine()));
System.out.println(sc.nextLine());
}
多线程
并发:指两个或多个事件在同一个时间段内发生
并行:指两个或多个事件在同一时刻(同时发生)

进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用
程序可以同时运行多 个进程;进程也是程序的一次执行过程,是系统运行程序的基本单
位;系统运行一个程序即是一个进程从创 建、运行到消亡的过程。

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

线程调度:
1.分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU事件
2.抢占式调用:优先让优先级高的线程使用CPU,如果线程优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度
主线程:执行主(main)方法的线程
单线程程序:java程序中只有一个线程

实现线程两种方式
方式一:
1.继承Thread类
2.重写run方法,设置线程的任务
3.创建Thread类的子类对象
4.调用Thread类中的start方法,开启新的线程,执行run方法
package com.zretc.javase.d0323.p2;
/*
* Thread java.lang.Thread类:是描述线程的类,
* 我们想要实现多线程程序,就需要继承Thread类
*/
// 1.创建一个Thread类的子类
public class MyThread extends Thread {
// 2.在Thread类的子类中重写Thread类中的run方法,设置线程认为(开启线程要做什么)
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run:" + i);
}
}
}
package com.zretc.javase.d0323.p2;
/*
* 1.创建一个Thread类的子类
* 2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程想要做什么?)
* 3.创建Thread类的子类对象
* 4.调用Thread类中的方法start方法,开启新线程,执行run方法
* void start():使该线程开始执行;Java虚拟机调用该线程的run方法
* 多次启动一个线程是非法的。
*
* Java程序属于抢占式调度,哪个线程的优先级高,哪个线程优先执行,同一个优先级,随机选择一个执行
*
*/
public class Thread01 {
public static void main(String[] args) {
// 3.创建Thread类的子类对象
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i < 20; i++) {
System.out.println("main:" + i);
}
}
}
方式二:
1.实现Runnable接口
2.重写run方法
3.创建一个Runnable接口的实现类对象
4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5.调用Thread类中的start()方法,开启新的线程
// 1.创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable {
// 2.在实现类中重写Runnable接口的run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
public class RunnableImplTest {
public static void main(String[] args) {
// 3.创建一个Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
// 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
// Thread t = new Thread(run);
// 5.调用Thread类中的start()方法,开启新的线程
// t.start();
// 步骤4和5合并
new Thread(run).start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
第一种方式,使用简单,但是影响子类继承其他类
第二种方式,不影响继承,还可以共享实现类资源
匿名内部类方式实现多线程的创建,简化代码
public class InnerClassThread {
public static void main(String[] args) {
// 线程的父类是Thread
new Thread() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " hello tom");
}
}
}.start();
// 线程的接口是Runnable
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " hello amy");
}
}
};
new Thread(run).start();
// 简化接口的方法
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " hello john");
}
}
}).start();
}
}
获取线程名称:
1.使用Thread类中的方法getName(),返回线程的名称
public class MyThread_getName extends Thread{
@Override
public void run() {
// ①
String name = getName();
System.out.println(name);
}
}
public static void main(String[] args) {
/*
* 线程的名称:Thread-0,Thread-1,Thread-2
*/
MyThread_getName mt = new MyThread_getName();
mt.start();
new MyThread_getName().start();
new MyThread_getName().start();
}
2.可以先获取到当前正在执行的线程,使用线程中的方法getName()获取线程的名称
Thread.currentThread()返回正在执行的线程对象
public class MyThread_getName extends Thread{
@Override
public void run() {
// ②
// Thread t = Thread.currentThread();
// System.out.println(t);
// String name = t.getName();
// System.out.println(name);
// ③:将②合并
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
/*
* 线程的名称:Thread-0,Thread-1,Thread-2
*/
MyThread_getName mt = new MyThread_getName();
mt.start();
new MyThread_getName().start();
new MyThread_getName().start();
// 获取主线程名称
System.out.println(Thread.currentThread().getName());
}
设置线程名称:
1.setName();
public class MyThread_setName extends Thread {
@Override
public void run() {
// 获取线程名称并输出
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyThread_setName mt = new MyThread_setName();
// ①设置线程名称
mt.setName("小强");
mt.start();
}
2.有参构造方法
public class MyThread_setName extends Thread {
public MyThread_setName() {
}
public MyThread_setName(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyThread_setName mt = new MyThread_setName("旺财");
mt.start();
}
sleep:让程序睡眠
public class MyThread_sleep {
/*
* public static void sleep(long millis);
* 使当前正在执行的程序以指定的毫秒数暂停(暂时停止执行)
* 毫秒数结束后,线程继续执行
*/
public static void main(String[] args) {
// 模拟秒表
for (int i = 1; i <= 60; i++) {
System.out.println(i);
try {
// Thread类的sleep方法让程序睡眠1秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
线程安全问题

解决线程安全问题
1.使用同步代码块解决
格式synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
package com.zretc.javase.d0324;
/*
* 卖票案例出现了线程安全问题,卖出了不存在的票和重复的票
*
* 解决线程安全问题的第一种方案:使用同步代码块
* 格式:
* synchronized(锁对象){
* 可能会出现线程安全问题的代码(访问了共享数据的代码)
* }
* 注意:1.通过代码块中的锁对象,可以使用任意的对象
* 2.但是必须保证多个线程使用的锁对象是同一个
* 3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
*
*/
public class TicketThread implements Runnable {
private int ticket = 100;
// 创建一个锁对象
Object obj = new Object();
// 设置线程任务:卖票
@Override
public void run() {
// 使用死循环,让卖票操作重复执行
while (true) {
synchronized (obj) {
// 先判断票是否存在
if (ticket > 0) {
// 票存在,卖票,ticket--
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
}
}
package com.zretc.javase.d0324;
public class TicketThreadTest {
public static void main(String[] args) {
TicketThread t = new TicketThread();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
2.使用同步方法解决
格式:
访问修饰符 synchronized 返回值类型 方法名(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
package com.zretc.javase.d0325;
/*
* 解决线程安全的第二种方案:使用同步方法
* 使用步骤:1.把访问了共享数据的代码抽取出来,放到一个方法中
* 2.在方法上添加synchronized修饰符
*
* 格式:
* 访问修饰符 synchronized 返回值类型 方法名(参数列表){
* 可能会出现线程安全问题的代码(访问了共享数据的代码)
* }
*/
public class TicketThread implements Runnable {
private int ticket = 100;
// 设置线程任务:卖票
@Override
public void run() {
// 使用死循环,让卖票操作重复执行
while (true) {
payTicket();
}
}
/*
* 定义一个同步方法,同步方法也会把方法内部的代码锁住,只让一个线程执行
* 同步方法的锁对象是谁?就是实现类对象 也就是this
*/
public synchronized void payTicket() {
// 先判断票是否存在
if (ticket > 0) {
// 票存在,卖票,ticket--
System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
ticket--;
}
}
}
package com.zretc.javase.d0325;
public class TicketThreadTest {
public static void main(String[] args) {
TicketThread t = new TicketThread();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
死锁:多个线程访问共同资源,且处于互相等待状态,会导致死锁
sleep让当前线程处于休眠状态,但是不是放资源
wait让当前线程处于等待状态,释放资源,但是需要通过notify/notifyAll进行唤醒
线程的五种状态(线程生命周期)
1.新建状态:线程被建立
2.就绪状态:将新建的线程,加入到执行队列中
3.运行状态:线程被执行
4.阻塞状态:执行过程中发生了等待状态,导致线程暂停
5.消亡状态:线程结束
同步:顺序执行(ABCD接力赛)
异步:同时执行(ABCD比赛)
23种设计模式
单例模式:只有一个实例
1.将构造方法私有化
2.设置公共的静态方法返回当前类对象
懒汉模式:使用时才创建
饿汉模式:使用前就创建好对象
package com.zretc.javase.d0325;
public class Earth {
// 懒汉模式,使用时才会创建
// private static Earth earth;
// private Earth() {
// System.out.println("创建对象");
// }
//
// public static Earth getEarth() {
// if(earth == null) {
// earth = new Earth();
// }
// return earth;
// }
// 饿汉模式,事先创建好对象
private static Earth earth = new Earth();
private Earth() {
System.out.println("创建了对象");
}
public static Earth getEarth() {
return earth;
}
}
public class EarthTest {
public static void main(String[] args) {
Earth e1 = Earth.getEarth();
Earth e2 = Earth.getEarth();
System.out.println(e1 == e2);
}
}
工厂模式:工厂里有n个管理类,统一由工厂来产生对象
枚举:将可以选择的数据,一一列出,只能选择给你列出的数据
public class Animal {
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat 对象");
}
}
public enum AnimalType {
CAT, DOG, PIG;
}
public class AnimalFactory {
public static Animal getAnimal(AnimalType i) {
Animal a = null;
switch (i) {
case CAT:
a = new Cat();
break;
case DOG:
a = new Dog();
break;
case PIG:
a = new Pig();
break;
}
return a;
}
}
public class TestFactory {
public static void main(String[] args) {
AnimalFactory.getAnimal(AnimalType.PIG);
}
}
JDK 8新特性
lamada表达式
主要针对是接口,且接口中只有一个抽象方法,替换匿名内部类的
无参数无返回值
package com.zretc.javase.d0325.p2;
public class Demo01 {
public static void main(String[] args) {
// ①匿名内部类
Interface1 interface1 = new Interface1() {
@Override
public void method() {
System.out.println("哈哈哈哈");
}
};
interface1.method();
// 主要针对是接口,且接口中只有一个抽象方法,替换匿名内部类的
// ②lamada表达式方式一
Interface1 interface2 = () -> {
System.out.println("啦啦啦啦");
};
interface2.method();
// ③由于只有一条语句,所以{}可以省略
Interface1 interface3 = () -> System.out.println("该吃饭了");
interface3.method();
}
}
interface Interface1 {
// 无参数无返回值
public abstract void method();
}
无参数有返回值
package com.zretc.javase.d0325.p2;
public class Demo02 {
public static void main(String[] args) {
// ①
Interface2 i1 = new Interface2() {
@Override
public int method() {
return 100;
}
};
System.out.println(i1.method());
// ②
Interface2 i2 = () -> {
return 200;
};
System.out.println(i2.method());
// ③
Interface2 i3 = () -> 300;
System.out.println(i3.method());
}
}
interface Interface2 {
// 无参数有返回值
public abstract int method();
}
有参数无返回值
package com.zretc.javase.d0325.p2;
public class Demo03 {
public static void main(String[] args) {
// ①
Interface3 i1 = new Interface3() {
@Override
public void method(int x, int y) {
System.out.println("x=" + x + ",y=" + y);
}
};
i1.method(1, 2);
// ②
Interface3 i2 = (int x, int y) -> {
System.out.println("x=" + x + ",y=" + y);
};
i2.method(3, 4);
// ③
Interface3 i3 = (int x, int y) -> System.out.println("x=" + x + ",y=" + y);
i3.method(5, 6);
// ④
Interface3 i4 = (x, y) -> System.out.println("x=" + x + ",y=" + y);
i4.method(7, 8);
}
}
interface Interface3 {
// 有参数无返回值
public abstract void method(int x, int y);
}
有参数有返回值
package com.zretc.javase.d0325.p2;
public class Demo04 {
public static void main(String[] args) {
// ①
Interface4 i1 = new Interface4() {
@Override
public int method(int x, int y) {
return x + y;
}
};
System.out.println(i1.method(1, 2));
// ②
Interface4 i2 = (int x, int y) -> {
return x + y;
};
System.out.println(i2.method(3, 4));
// ③
Interface4 i3 = (x, y) -> x + y;
System.out.println(i3.method(9, 10));
}
}
interface Interface4 {
// 有参数有返回值
public abstract int method(int x, int y);
}
集合遍历
package com.zretc.javase.d0325.p2;
import java.util.ArrayList;
import java.util.function.Consumer;
public class Demo05 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("tom");
list.add("rose");
list.add("jack");
list.add("kimi");
list.add("july");
// 函数式接口--Consumer接口
list.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
System.out.println("=======================");
list.forEach((String t) -> System.out.println(t));
System.out.println("=======================");
list.forEach((t) -> System.out.println(t));
// 就是把集合当中的每一个元素都赋值给了t,然后输出t
System.out.println("=======================");
list.forEach(t -> System.out.println(t));
System.out.println("************************");
list.forEach(System.out::println);
}
}
可以调用静态方法
package com.zretc.javase.d0325.p2;
public class Demo06 {
public static void main(String[] args) {
Interface6 i1 = new Interface6() {
@Override
public int getInt(String s) {
return Integer.parseInt(s);
}
};
System.out.println(i1.getInt("123") + 1);
Interface6 i2 = (s) -> Integer.parseInt(s);
System.out.println(i2.getInt("456") + 1);
/*
* 这里它由于是一个参数,它把很多东西都可以省略
* parseInt是用类调用,说明是一个静态方法,静态方法可以直接调用
*/
Interface6 i3 = Integer::parseInt;
System.out.println(i3.getInt("780") + 1);
}
}
interface Interface6 {
int getInt(String s);
}
构造方法
package com.zretc.javase.d0325.p2;
public class Demo07 {
public static void main(String[] args) {
Interface7 i1 = new Interface7() {
@Override
public Person getPerson() {
return new Person();
}
};
i1.getPerson();
// 构造方法
Interface7 i2 = Person :: new;
i2.getPerson();
}
}
class Person {
public Person() {
System.out.println("创建Person对象");
}
}
interface Interface7{
public Person getPerson();
}
package com.zretc.javase.d0325.p2;
public class Demo08 {
public static void main(String[] args) {
// 有参构造方法
Interface8 i1 = Student::new;
i1.getStudent("tom", 18);
Interface8 i2 = new Interface8() {
@Override
public Student getStudent(String name, int age) {
return new Student(name, age);
}
};
i2.getStudent("amy", 20);
}
}
class Student {
public Student(String name, int age) {
System.out.println(name + "\t" + age);
}
}
interface Interface8 {
public Student getStudent(String name, int age);
}
时间类
LocalDate
LocalTime
LacalDateTime
package com.zretc.javase.d0325.p3;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
public class Demo {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(cal);
// 获取Date对象
System.out.println(cal.getTime());
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 转成格式化字符串
System.out.println(f.format(cal.getTime()));
System.out.println(cal.get(Calendar.MONTH) + 1 + "月");// 0-11
System.out.println(cal.get(Calendar.DAY_OF_WEEK) - 1);// 1-7 周日是第一天
// jdk 8之后新增时间类
LocalDate ld = LocalDate.now();// 获取当前系统时间
System.out.println(ld);// 2022-03-25
LocalDate ld2 = LocalDate.of(2023, 3, 1);// 指定时间
System.out.println(ld2);
// 通过控制当前赋值的正负数,完成是加还是减
ld2 = ld2.plusYears(-3);
System.out.println(ld2);
// 月份
ld2 = ld2.plusMonths(-13);
System.out.println(ld2);
// 日期
ld2 = ld2.plusDays(1);
System.out.println(ld2);
// 周,1代表7天
ld2 = ld2.plusWeeks(2);
System.out.println(ld2);
System.out.println("=========================");
// 格式化
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;// 获取系统设置好的时间格式
System.out.println(dtf);
// 转成格式化字符串
System.out.println(dtf.format(ld));// 2022-03-25
// 设置时间格式
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy/MM/dd");
// 转成格式化字符串
System.out.println(dtf2.format(ld));// 2022/03/25
System.out.println("***************************");
// 时分秒毫秒
LocalTime lt = LocalTime.now();
System.out.println(lt);
// 格式化字符串
DateTimeFormatter d1 = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");// HH:24进制 hh:12进制
System.out.println(d1.format(lt));
System.out.println("############################");
// 年月日-十分秒毫秒
LocalDateTime lt2 = LocalDateTime.now();
System.out.println(lt2);
// 格式化字符串
DateTimeFormatter d2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String str = d2.format(lt2);
System.out.println(str);
}
}