自学java面向实习从零开始路线
Java
目录
Java基础
Java开发底层
JDK(Java Development Kit)Java开发工具包
JDK = JRE + Java的开发工具(java, javac, javadoc, javap等)
JRE(Java Runtime Environment)Java运行环境
JRE = JVM + Java核心库类
JVM(Java Virtual Machine)Java虚拟机
感慨语录
bgm
《渡月桥思君》
1
年少总将被不可得之物困其一生
2
不要指望谁陪你一辈子,没光的时候连影子都不陪你
文档注释
- 基本格式
代码开头写入
/**
*@author LX
*@version 1.0.0
*/
javadoc -d 产生注释文档到哪个文件夹的路径 -Java标签 -Java标签 java文件名
- 例子
javadoc -d C:\Users\god\Desktop\java学习代码 -author -version hello.java
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oKbXjJNj-1676111541442)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20220927111012658.png)]
快速多行注释
有时候我们想讲多行代码转化为注释,在每一行的行首输入’//'太过麻烦,我们可以讲这几行框选出来,输入ctrl+/
,便可快速实现多行注释(多行代码分行注释)
// System.out.println(5.12e2);
// System.out.println(5.12e-2);
// System.out.println(5.12E-2);
// double num1=1.112233445566778899;
// System.out.println(num1);
输入ctrl+Shift+/
,便可快速实现多行注释(多行代码整体注释)
/*
//类名.类变量名访问
System.out.println(A.name);
A a = new A();
//对象名.类变量名访问
System.out.println(a.name);
*/
停止运行程序
输入ctrl+c
数据类型
浮点型
float&&double
java 的浮点型常量默认
为double类型,声明float常量,须后加‘f’或‘F’
double num1 = 1.1314;
float num2 = 1.1314f;
float 4个字节
double 8个字节
所以
float num1 = 1.1314; //错误的,因为不加‘f’或‘F’说明是默认的double类型的,多字节转化为少字节可能会造成精度丢失,编译器会报错
double num2 = 1.1314f; //正确的,少字节转化为多字节
字符型
char
本质上char是一个整数,默认输出是unicode码对应的字符,我们可以简单的通过强制类型转换将字符回缩为数字
public class char01{
public static void main(String[] args){
char a = '李';
System.out.println("李的ASCII码为"+(int)a); //李的ASCII码为26446
}
}
基本数据类型和String类型转换
基本数据类型->String类型
语法:将基本类型的值+ “” 即可
int n1 = 100;
float f1 = 1.01f;
double d1 = 1.01;
boolean b1 = true;
String s1 = n1 + "";
String s2 = f1 + "";
String s3 = d1 + "";
String s4 = b1 + "";
String类型基本数据类型->基本数据类型
语法:使用基本数据类型对应的包装类的相应方法,得到基本数据类型
String s1 = "123";
int num1 = Integer.parseInt(s1);
double num2 = Double.parseDouble(s1);
float num3 = Float.parseFloat(s1);
boolean num4 = Boolean.parseBoolean(s1);
字符串 ->char
从字符串的第一个字符得到char
String s1 = "123";
System.out.println(s1.charAt(0));
随机数生成
Math.random() 生成随机数[0,1)
for(int i = 0; i < 20; i++){
System.out.println(Math.random()); //生成随机数[0,1)
}
for(int i = 0; i < 20; i++){
System.out.println((int)(Math.random()*100)+1); //生成随机数[1,100]
}
运算符
算术运算符
++(自增)
i++ 先赋值后自增
++i 先自增后赋值
int j = 8;
int k = ++j;
System.out.println(k);
System.out.println(j);
//运行:k = 9; j = 9
等价于j = j + 1; k = j
int j = 8;
int k = j++;
System.out.println(k);
System.out.println(j);
//运行:k = 8; j = 9
等价于k = j; j = j + 1
%(取余)
a % b=a - (int)a / b * b
int a = 10 % 2; //10 % 2 = 10 - 10 / 2 * 2 = 0
int b = -10.5 % 3; //-10.5 % 3 = -10.5 - (int)(-10.5)/3*3 = -10.5 - (-10) /3* 3 = -1.5
易错点
int j = 1;
j = j++;
System.out.println(j);
//运行:j = 1
规则使用临时变量temp:(1)temp = j;(2)j = j + 1;(3)j = temp;
int j = 1;
j = ++j;
System.out.println(j);
//运行:j = 2
规则使用临时变量temp:(1)j = j + 1;(2)temp = j;(3)j = temp;
逻辑运算符
&与&&(与)
a & b 逻辑与 a,b全真结果才为真 若a为假,仍需判断
b,结果虽然b是真是假都为假
a && b 短路与 a,b全真结果才为真 若a为假,跳过判断
b,结果直接为假
短路操作效率高
,实际开发基本都用短路操作
int a = 4;
int b = 9;
if(a < 1 & ++b < 50){
System.out.println("ok");
}
System.out.println("b = " + b);
//运行:b = 10
int a = 4;
int b = 9;
if(a < 1 && ++b < 50){
System.out.println("ok");
}
System.out.println("b = " + b);
//运行:b = 9
| 与 ||(或)
a | b 逻辑或 a,b有真结果就为真 若a为真,仍需判断
b,结果虽然b是真是假都为真
a || b 短路或 a,b有真结果就为真 若a为真,跳过判断
b,结果直接为真
短路操作效率高
,实际开发基本都用短路操作
例子参见&与&&的java实例区别
!(取反)
操作取反,T->F,F->T
System.out.println(100>99);
System.out.println(!(100>99));
^(逻辑异或)
a^b a 与 b不同真假时,结果才为true,否则为false
System.out.println((2>1)^(4>3));
//运行:false
易错点
public class test03{
public static void main(String[] args){
boolean x = false;
boolean y = true;
System.out.println(x = true);
System.out.println(y = false);
}
}
//运行:true
// false
赋值是真还是假,结果也就是真还是假
小试牛刀
public class test03{
public static void main(String[] args){
boolean x = true;
boolean y = false;
short z = 46;
if((z++ == 46)&&(y = true))
z++;
if((x = false)||(++z == 49))
z++;
System.out.println("z = "+ z);
}
}
//运行:z = 50
符合赋值运算符(自动类型转换)
复合赋值运算符会自动进行类型转换
byte b = 2;
b += 3;
b++;
//运行:不报错
b += 3 实际上等价于 b = (byte)(b + 3);
否则若b = b + 3;右边是byte类型与int类型相加实际上会自动类型转换为int类型,此时再赋给byte类型的b就会不合适,因为多字节转化为少字节可能会造成精度丢失,编译器会报错。b++的自动类型转换一致
三元运算符
条件表达式 ? 表达式1 :表达式2
public class ternaryopertar{
public static void main(String[] args){
int a = 10;
int b = 100;
int result = a > b ? a++ : b--;
System.out.println(result);
}
}
//运行:100
b--先将b赋给result,后进行b-1
标识符的命名规则和规范
只是更加专业
- 包名:多单词组成时,所有单词都小写:xxxyyyzzz,比如:com.hsp.crm
- 类名、接口名:多单词组成时,所有单词首字母大写【
大驼峰
】:XxxYyyZzz,比如:TankShotGame - 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始首字母大写【
小驼峰
】:xxxYyyZzz,比如:tankShotGame - 常量名:所有字母都大写,多单词时每个字母用下划线连接:XXX_YYY_ZZZ,比如:定义一个所得税率 TAX_RATE***(常量本质就是不能被修改的量,后面会学习到final修饰)***
tips:
输入小写 字母时,突然输入大写字母,这时候我们可以按住shift键同时按住字母就可以快速写出大写字母了
键盘输入与字符串比较
使用扫描器Scanner
.equals()方法比较字符串
import java.util.Scanner; //表示把java.util包下的Scanner类导入
public class Input{
public static void main(String[] args){
// 演示接受用户的输入
// 步骤
// Scanner类 表示 简单文本扫描器,在java.util包下
// 1.引入/导入Scanner类所在的包
// 2.创建Scanner对象
Scanner myScanner = new Scanner(System.in);
//3.接受用户的输入,使用相关的方法
System.out.println("输入代号");
char code = myScanner.next().charAt(0); //接受用户输入字符
System.out.println("输入姓名");
String name = myScanner.next(); //接受用户输入字符串
System.out.println("输入年龄");
int age = myScanner.nextInt(); //接受用户输入int
System.out.println("输入期待月薪");
double sal = myScanner.nextDouble(); //接受用户输入double
System.out.println("求职者信息如下");
System.out.println("代号 = "+ code +"姓名 = "+ name +" 年龄 = "+ age +" 期待月薪 = "+ sal);
if(name.equals("老板")){ //字符串比较
System.out.println("老板好!");
}
}
}
进制转换与位运算
进制
二进制 以0b或0B开头表示
八进制 以数字0开头表示
十进制
十六进制 以0x或0X开头表示
int n1 = 0b1010; //二进制
int n2 = 01010; //八进制
int n3 = 1010; //十进制
int n4 = 0x1010; //十六进制
原码、反码、补码
- 有符号的二进制,最高位为符号位:0表示正数,1表示负数(记忆方法:0绕着中心旋转90°还是0是正数,1绕着中心旋转90°是变成—是负数)
- 正数的原码,反码,补码都一样(三码合一)
- 负数的反码=它的原码符号位不变,其他位取反
- 负数的补码=它的反码+1
- java没有无符号数,换言之,java中的数都是有符号的
- 在计算机运算的时候,都是以
补码的方式来运算
的 - 当我们看运算结果的时候,要看他的
原码
位运算
(补码进行运算,最后返回原码
)
按位与&: 两位全为1,结果为1,否则为0
按位或|: 两位有一个为1,结果为1,否则为0
按位异或^: 两位一个为0,一个为1,结果为1,否则为0
按位取反~: 0->1,1->0
System.out.println(2&3);
System.out.println(~-2);
System.out.println(~2);
//运行: 2
// 1
// -3
位运算符
- 算术右移>>:低位溢出,符号位不变,并用符号位补溢出的高位
- 算数左移<<:符号位不变,低位补0
- 逻辑右移(无符号右移)>>>:低位溢出,高位补0
- 特别说明:没有<<<符号
控制循环
switch
public class SwitchDetail{
public static void main(String[] args){
char c = 'a';
//细节1:表达式数据类型,应和case后的数据类型一致,或者是可以自动
//转成可以相互比较的类型,比如下面代码中输入的是字符,而常量是int
//细节2:case子句的值只能是常量或常量表达式,而不能是变量
switch(c){
case 'a' :
System.out.println("ok1");
break;
case 11 :
System.out.println("ok2");
break;
default :
System.out.println("No");
}
}
}
//运行:ok1
public class SwitchDetail{
public static void main(String[] args){
char c = 'a';
switch(c){
case 'a' :
System.out.println("ok1");
case 11 :
System.out.println("ok2");
break;
default :
System.out.println("No");
}
}
}
//运行:ok1
// ok2
第一个case满足后没有break,所以忽略第二个case条件,直接运行第二个case的语句,此时看到break而跳出switch
for
for(int i = 1; i < 10; i++){
System.out.println("Yeah");
}
System.out.println(i);
//运行:报错
当i在for循环中初始化,那么i的作用域就只在for循环里
do…while
不管while是否满足,首先先执行一次do语句,再判断while条件是否满足
int i = 1;
do{
System.out.println(i);
i++;
}while(i <= 10); //while后面有分号
退出循环
break
直接退出,while for switch循环,执行下面语句
continue
只退出当前判断,依旧执行循环
return
退出方法用return;
思考题
打印输出一个空心金字塔
思路
:
1.先简单打印这样的
*
**
***
****
*****
这种可以看作半金字塔
2.进阶:打印如下金字塔
*
***
*****
*******
*********
这种金字塔只需要再多打印每行前面的空格数即可
3.再进阶:打印如下金字塔
*
* *
* *
* *
* *
这种金字塔只需要再多加一个if(j == 0||j == 2*i-1)即可只打印每行的第一个和最后一个
空出的部分用else打印空格
4.完成:打印如下金字塔
将if判断多加一个或条件即可
// *
// * *
// * *
// * *
// *********
import java.util.Scanner;
public class Star{
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入层数来构建属于你的空心金字塔吧!");
int num = myScanner.nextInt();
int i; //层数变量
int j; //每层个数
for(i = 0; i <= num; i++){
for(j = 0; j <= num-i; j++){
System.out.print(" ");
}
for(j = 0; j <= 2*i-1; j++){
if(j == 0||j == 2*i-1||i == num){ //i == num确保了金字塔最后一层
System.out.print("*");
}else{
System.out.print(" ");
}
}
System.out.println("");
}
}
}
数组
一维数组
数组创建
- 第一种动态分配方式
double Scores[] = new double[5];
- 第二种动态分配方式
double Scores[]; //先声明数组,再分配空间
Scores = new double[5];
- 第三种静态初始化方式
double Scores[] = {2, 3.1, 4, 5, 1};
数组赋值
数组创建后,如果没有赋值,有默认值
:int 0 ; short 0 ;byte 0 ;float 0.0; double 0.0;char \u0000; boolean false , String null …
- 数组在默认情况下是引用传递,赋的值是地址,
int arr1[] = {1,2,3};
int arr2[] = arr1;
arr2[0] = 10;
//此时arr1[0] 也变成了10,可以看出是引用传递
如果要数组拷贝
,而不是传递地址,需要先建立数组地址
int arr1[] = {1,2,3};
int arr2[] = new int[arr1.length]; //建立数组空间,此时初始值都是0
for(int i = 0; i < arr1.length; i++){
arr2[i] = arr1[i];
}
数组扩容
-
简单版:创建新链表,赋值后再将新链表地址赋给旧链表
import java.util.Scanner; public class Array01{ public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); int arr[] = {1, 2, 3}; System.out.println("初始数组的值为:"); for(int i = 0; i < arr.length; i++){ System.out.print(arr[i]+" "); } do{ System.out.println("\n请输入要插入数组的值:"); int addNum = myScanner.nextInt(); int arrNew[] = new int[arr.length + 1]; for(int i = 0; i < arr.length; i++){ arrNew[i] = arr[i]; } arrNew[arrNew.length - 1] = addNum; arr = arrNew; System.out.println("现在数组的值为:"); for(int i = 0; i < arr.length; i++){ System.out.print(arr[i]+" "); } System.out.println("还想插入新数据吗,回复y/n"); char key = myScanner.next().charAt(0); if(key == 'n'){ break; } }while(true); } }
-
使用数据结构链表实现动态数组
数组排序
冒泡排序
就像一个气泡从水底上升到空气中,每次比较相邻的两个大小,最终会将最大值放到最后一位,第二轮将第二大值放在倒数第二位,依次类推
public class Test174{
public static void main(String[] args){
int arr[] = {1,5,2,4,3};
int temp = 0; //辅助交换变量
for(int i = 0; i < arr.length-1; i++){
for(int j = 0; j < arr.length -1-i; j++){
if(arr [j] > arr[j+1]){
temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}
数组查找
-
顺序查找
import java.util.Scanner; public class Test175{ public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); String names[] = {"王者荣耀","穿越火线","天天酷跑","吃鸡","生死狙击"}; System.out.println("请输入你要查找的游戏名,如果找到稍后将返回数据库中编号"); String findName = myScanner.next(); int index = -1; //一个编程技巧:通过标识符判断是否查找成功 for(int i = 0; i < names.length; i++){ if(findName.equals(names[i])){ //字符串匹配比较 System.out.println("恭喜您找到了!\n数据库编号为"+i); index = i; break; } } if(index == -1){ //通过标识符 System.out.println("查无此游戏"); } } }
-
二分查找
二维数组
数组创建
-
静态创建
public class Test176{ public static void main(String[] args){ int arr[][] = { {1,0,0,0}, {2,0,0,0}, {3,0,0}, {4,0,0,0} }; for(int i = 0; i < arr.length; i++){ for(int j = 0; j < arr[i].length; j++){ System.out.print(arr[i][j]+" "); } System.out.println(""); } System.out.println(arr[2][3]); //数组越界:arr[2]只是一个有三个空间的一维数组 } }
-
动态创建
public class Test179{ public static void main(String[] args){ int arr[][] = new int[3][]; for(int i = 0; i < arr.length; i++){ arr[i] = new int[i+1]; //给每一个一维数组开空间,可以自己任意划定空间 } } }
练习题—杨辉三角
/*杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
*/
import java.util.Scanner;
public class Test182{
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入要生成的杨辉三角行数");
int num = myScanner.nextInt();
int yangHui[][] = new int[num][];
System.out.println("为您生成的杨辉三角图形为");
for(int i = 0; i < yangHui.length; i++){
yangHui[i] = new int[i+1];
for(int j = 0; j < yangHui[i].length; j++){
if(j == 0 || j == yangHui[i].length-1){
yangHui[i][j] = 1;
}else{
yangHui[i][j] = yangHui[i-1][j]+yangHui[i-1][j-1]; //规律
}
}
}
for(int i = 0; i < yangHui.length; i++){
for(int j = 0; j < yangHui[i].length; j++){
System.out.print(yangHui[i][j]+" ");
}
System.out.println("");
}
}
}
面向对象编程(初级)
类
创建类,每个java程序只能有一个public类
public class Test203{
public static void main(String[] args){
person p1 = new person(); //创建对象
p1.num = 1;
p1.OutPut();
int i = p1.sum(1,99);
System.out.println(p1.sum(1,99));
System.out.println(i);
}
}
class person{ //类
int num;
public void OutPut(){ //创建方法
System.out.println("学java");
}
public int sum(int x, int y){ //创建有输入参数的方法
int sunNum = x + y;
return sunNum;
}
}
方法
方法调用
当程序执行到调用方法时,程序会在main主栈额外开一个方法栈进行执行
同一个类中的方法调用:直接调用即可
public class Test209{
public static void main(String[] args){
A a = new A();
a.sayOk();
}
}
class A{
public void print1(int n){
System.out.println("调用print1 "+n);
}
public void sayOk(){
print1(10); //同一个类中的方法可以直接调用
System.out.println("继续执行sayOk()");
}
}
方法传参机制
- 基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参
public class Test209{
public static void main(String[] args){
int a = 10;
int b = 20;
System.out.println("调用置换方法前a = " + a + " b = " + b);
AA op = new AA();
op.swap(a,b); //当程序执行到调用方法时,程序会在main主栈额外开一个方法栈,然后对传入的参数进行变换,并不会影响到传入的变量本身的值
System.out.println("调用置换方法后a = " + a + " b = " + b);
}
}
class AA{
public void swap(int a, int b){
int temp = a;
a = b;
b = temp;
}
}
- 引用数据类型,传递的是地址,形参的值改变会影响实参,但是形参的地址改变却不影响实参
public class Test213{
public static void main(String[] args){
person p = new person();
p.age = 20;
B b = new B();
b.change1(p);
System.out.println(p.age); //change1改变的是值,依旧是同一个地址,所以p.age = 9999
b.change2(p);
System.out.println(p.age); //change2改变的是地址,所以不会影响到main栈p指向的空间所以依旧是p.age = 9999
}
}
class person{
String name;
int age;
}
class B{
public void change1(person p){
p.age = 9999;
}
public void change2(person p){
p = null;
}
}
方法重载
- java中允许同一个类中,多个同名方法存在,但是要求形参列表不一致,不必关心方法的返回值是否一样
- 好处:
- 减轻了起名的麻烦
- 减轻了记名的麻烦
可变参数
java允许将同一个类中,多个同名同功能但参数个数不同的方法,封装成一个方法
public class Test234{
public static void main(String[] args){
AA a = new AA();
a.sum();
a.sum(1,2,3,4,5);
}
}
class AA{
//int...表示接受的是可变参数,类型是int,个数为0到任意多个
//使用可变参数时,可以当成数组来使用 即参数名nums可当做数组
public int sum(int... nums){
int res = 0;
for(int i = 0; i < nums.length; i++){
res += nums[i];
}
System.out.println("接受参数数量为 " + nums.length);
System.out.println("计算结果为 " + res);
return res;
}
}
//运行: 接受参数数量为 0
// 计算结果为 0
// 接受参数数量为5
// 计算结果为 15
细节:
- 可变参数的实参可以是数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但
必须保证可变参数在最后
- 一个形参列表只能出现一个可变参数
public class Test234{
public static void main(String[] args){
AA a = new AA();
int[] arr1 = {1, 2, 3};
int[] arr2 = {10, 20};
a.sum(arr1); //正确:会将数组中各个值相加
a.sum(arr1, arr2); //错误:实参本质应该是(int[]),这里输入(int[],int[])
}
}
class AA{
public int sum(int... nums){
int res = 0;
for(int i = 0; i < nums.length; i++){
res += nums[i];
}
System.out.println("接受参数数量为 " + nums.length);
System.out.println("计算结果为 " + res);
return res;
}
public void f1(String str, double... nums){ //可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
}
public void f1(double... str, double... nums){ //错误:一个形参列表只能出现一个可变参数
}
}
构造器
- 构造方法又叫构造器,是类的一种特殊的方法,它的主要作用是完成对
新对象的初始化
- 用法:
- 构造器没有返回值,也不能写void
- 构造器的名称必须和类名一样
public class Test239{
public static void main(String[] args){
AA a = new AA("李华", 21);
System.out.println(a.name);
System.out.println(a.age);
}
}
class AA{
String name;
int age;
public AA(String aName, int aAge){
System.out.println("构造器被调用~~完成对象的属性初始化");
name = aName;
age = aAge;
}
- 细节:
- 一个类可以定义多个不同的构造器,即构造器重载
- 构造器是完成对象的初始化,而不是创建对象
- 在创建对象时,系统自动的调用该类的构造方法
- 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造方法)
- 一旦定义了自己的构造器,
默认的构造器就覆盖了
,就不能再使用默认的无参构造器了,除非显示的定义一下,即:Dog(){}
public class Test239{
public static void main(String[] args){
AA a = new AA("李华", 21);
//a.AA(); //错误:在创建对象时,系统自动的调用该类的构造方法,而不能自己调用
//AA a = new AA(); //错误:一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器了
}
}
class Dog{
// 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造方法)
//可以通过javap指令 反编译查看到这个默认的构造方法
// 默认构造器:
// Dog(){
// }
}
class AA{
String name;
int age;
public AA(String aName, int aAge){
name = aName;
age = aAge;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s0cEMdSY-1676111541444)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221023104103975.png)]
this
为了简化构造器的形式参数,使它和对象的属性一样,在方法中需要引入this来指定对象的属性
class AA{
String name;
int age;
public AA(String name, int age){
System.out.println("构造器被调用~~完成对象的属性初始化");
this.name = name; //this.name指的是对象的属性,name还是按照就近原则,是方法的形式参数
this.age = age; //this.age指的是对象的属性,age还是按照就近原则,是方法的形式参数
}
- 细节
- this关键字可以用来访问本类的属性,方法,构造器
- this用来区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表)
- 访问构造器语法:this(参数列表);
注意只能在构造器中使用(即只能在构造器中访问另一个构造器,并且this(参数列表)必须放在第一条语句)
- this不能在类定义的外部使用,只能在类定义的方法中使用
public class Test250{
public static void main(String[] args){
AA a = new AA();
a.f1();
}
class AA{
String name;
int age;
public AA(){ //在无参构造器中访问到另一个构造器
this("jack", 3); //使用this在构造器中访问另一个构造器,且必须放在第一条
System.out.println("AA()构造器被调用~~完成对象的属性初始化");
}
public AA(String name, int age){
System.out.println("AA(String name, int age)构造器被调用~~完成对象的属性初始化");
this.name = name;
this.age = age;
}
public void f1(){
String name = "lucy";
System.out.println(name); //作用域先判断就近原则,此时局部变量也有name,所以此处是lucy
System.out.println(this.name); //this直接定位到类的属性name,所以此处是jack
}
}
构造器复用
public class Test260{
public static void main(String[] args){
Employee e = new Employee("工程师", 1000000, "li");
}
}
class Employee{
String job;
double sal;
String name;
char gender;
int age;
public Employee(String job, double sal, String name){
this.job = job;
this.sal = sal;
this.name = name;
}
public Employee(String job, double sal, String name, char gender, int age){
this(job, sal, name); //构造器复用
this.gender = gender;
this.age = age;
}
}
变量作用域
基本使用
- 全局变量:也就是属性,作用域为整个类体,类中的成员方法可以使用属性
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中。局部变量一般指在成员方法中定义的变量,它的作用域就在方法中
- 全局变量(属性)可以不赋值,直接使用,因为有默认值。
局部变量必须赋值后才能使用
,因为没有默认值
细节
-
属性和局部变量可以重名,访问时遵循
就近原则
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
-
属性生命周期比较长,伴随着对象的创立而创立,伴随着对象的消亡而消亡。局部变量生命周期比较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而消亡,即在一次方法调用过程中
public class Test237{
public static void main(String[] args){
AA a = new AA();
a.say(); //调用方法时创建方法中name变量,执行完后自动销毁
}
}
class AA{
String name = "jack";
public void say(){
String name = "li"; //重名的属性和局部变量访问时遵循就近原则
System.out.println("say()name = " + name);
}
}
- 全局变量(属性)可以被本类使用,也可以被其他类使用(通过对象调用)
public class Test238{
public static void main(String[] args){
AA a = new AA();
BB b = new BB();
a.say();
b.say01();
b.say02(a);
}
}
class AA{
String name = "jack";
public void say(){
String name = "li";
System.out.println("say()name = " + name);
}
}
class BB{
public void say01(){
AA a = new AA();
System.out.println(a.name); //跨类方法调用1
}
public void say02(AA p){
System.out.println(p.name); //跨类方法调用2
}
}
- 全局变量(属性)可以加修饰符,不加就是默认,局部变量不可以加修饰符
javap反编译
javap是JDK提供的一个命令行工具,javap能对给定的.class文件提供的字节代码进行反编译
- 用法:javap 文件名.class
class Dog{
// 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造方法)
//可以通过javap指令 反编译查看到这个默认的构造方法
// 默认构造器:
// Dog(){
// }
}
//输入 javap Dog.class
运行结果:
Compiled from "Test238.java" //Dog类所在的文件的主类名
class Dog {
Dog();
}
递归
斐波那契数列
import java.util.Scanner;
public class Test220{
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
AA a = new AA();
System.out.println("斐波那契数列是形如:1,1,2,3,5,8,13的数字,请输入你想要生成的具有几位的斐波那契数列");
int fibonacciNum = myScanner.nextInt();
System.out.println("生成的斐波那契数列为");
for(int i = 1; i <= fibonacciNum; i++)
System.out.println(a.Fibonacci(i));
}
}
class AA{
public int Fibonacci(int n){
return n >= 3 ? Fibonacci(n-1)+Fibonacci(n-2) : 1;
}
}
老鼠出迷宫
import java.util.Scanner;
public class Test223{
public static void main(String[] args){
System.out.println("即将开始老鼠出迷宫游戏");
Scanner myScanner = new Scanner(System.in);
mouse a = new mouse();
System.out.println("请输入您要生成的围墙行数");
int xNum = myScanner.nextInt();
System.out.println("请输入您要生成的围墙列数");
int yNum = myScanner.nextInt();
int[][] map = new int[xNum][yNum];
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
if(i == 0 || i == map.length-1 || j == 0 || j == map[i].length-1)
map[i][j] = 1;
}
}
do{
System.out.println("1表示围墙,0代表未堵住的路,2表示老鼠最终选择走的路,3表示老鼠走过但是走不通");
System.out.println("====生成的初始围墙为=====");
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.print(map[i][j]+" ");
}
System.out.println("");
}
System.out.println("还想插入新的围墙吗,回复y/n");
char key = myScanner.next().charAt(0);
if(key == 'n'){
break;
}
System.out.println("请输入您要生成的围墙所在行数");
xNum = myScanner.nextInt();
System.out.println("请输入您要生成的围墙所在列数");
yNum = myScanner.nextInt();
map[xNum-1][yNum-1] = 1;
}while(true);
//定义老鼠出发点位
System.out.println("请输入您老想要鼠出发所在行数");
xNum = myScanner.nextInt();
System.out.println("请输入您老想要鼠出发所在列数");
yNum = myScanner.nextInt();
boolean gameResult = a.findWay(map,xNum-1,yNum-1);
System.out.println("====突围后的迷宫图为=====");
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.print(map[i][j]+" ");
}
System.out.println("");
}
if(gameResult){
System.out.println("老鼠出逃成功!^^");
}else{
System.out.println("老鼠出逃失败");
}
}
}
class mouse{
public boolean findWay(int[][] map, int x ,int y){
int xNum = map.length;
int yNum = map[xNum-1].length;
//找路策略:0表示可以走,1表示围墙走不了,2表示可以走,并且走过的路,3表示走过但是走不通
if(map[xNum-2][yNum-2] == 2){
return true;
}else{
if(map[x][y] == 0){
map[x][y] = 2;
}else{
return false;
}
//按照先判断下面是否可以走->右->上->左
if(findWay(map,x+1,y)){
return true;
}else if(findWay(map,x,y+1)){
return true;
}else if(findWay(map,x-1,y)){
return true;
}else if(findWay(map,x,y-1)){
return true;
}else{
map[x][y] = 3;
return false;
}
}
}
}
汉诺塔
import java.util.Scanner;
public class Test226{
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
System.out.println("===请输入您要生成的a盘的基础盘数====");
int num = myScanner.nextInt();
tower t = new tower();
System.out.println("===盘间的移动次序为====");
int moveNum = t.move(num, 'a', 'b', 'c');
System.out.println("总共移动次数为:" + moveNum);
}
}
class tower{
int moveNum = 0;
public int move(int num, char a, char b, char c){ //将a所有盘借助b移动到c
if(num == 1){
System.out.println(a + "->" + c);
moveNum++;
}else{
move(num - 1 , a, c, b); //将a最后一个盘之上的所有盘借助c移动到b
System.out.println(a + "->" + c); //将a最后一个盘直接移动到c
moveNum++;
move(num - 1, b, a, c); //将b盘之上的所有盘借助a移动到c
}
return moveNum;
}
}
八皇后
难度太大,思考了三个小时,敲了116行代码最后宣布放弃,待继续补充基础知识后必将回来重新啃下这快骨头
//错误代码,仅代表我思考过了
public class Test227{
public static void main(String[] args){
int[][] map = new int[8][8];
AA a = new AA();
a.distribution(map,0,0);
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.print(map[i][j]+" ");
}
System.out.println("");
}
}
}
class AA{
int queenNum = 0;
public void move(int[][] map, int x, int y){
int yNum = y;
for(int i = 1; yNum > 0; yNum--){
if(x+i < 0 || y-i > 7 || x+i > 7 || y-i < 0){
break;
}
map[x+i][y-i] = 1;
i++;
}
int xNum = 7 - x;
for(int i = 1; xNum > 0; xNum--){
if(x+i < 0 || y+i > 7 || x+i > 7 || y+i < 0){
break;
}
map[x+i][y+i] = 1;
i++;
}
for(int i = 0; i < 8; i++){
if(i != y){
map[x][i] = 1;
}
}
for(int i = 0; i < 8; i++){
if(i != x){
map[i][y] = 1;
}
}
}
public void reduction(int[][] map, int x, int y){
int yNum = y;
for(int i = 1; yNum > 0; yNum--){
if(x+i < 0 || y-i > 7 || x+i > 7 || y-i < 0){
break;
}
map[x+i][y-i] = 0;
i++;
}
int xNum = 7 - x;
for(int i = 1; xNum > 0; xNum--){
if(x+i < 0 || y+i > 7 || x+i > 7 || y+i < 0){
break;
}
map[x+i][y+i] = 0;
i++;
}
for(int i = 0; i < 8; i++){
if(i != y){
map[x][i] = 0;
}
}
for(int i = 0; i < 8; i++){
if(i != x){
map[i][y] = 0;
}
}
}
public boolean distribution(int[][] map, int x, int y){
if(y < 0 || y > 7 || x < 0 || x > 7){
return false;
}
if(queenNum == 8){
return true;
}else {
if(map[x][y] == 0){
map[x][y] = 2;
queenNum++;
move(map, x, y);
}else{
return false;
}
if(distribution(map,x,y+1)){
return true;
}else if(distribution(map,x,y-1)){
return true;
}else if(distribution(map,x+1,y+2)){
return true;
}else if(distribution(map,x+1,y-2)){
return true;
}else{
map[x][y] = 0;
reduction(map, x, y);
return false;
}
}
}
IDEA快捷键
删除光标所在行
已经自定义为 ctrl + d
复制光标所在行到下一行
ctrl + alt + ↓
补全代码
alt + /
添加注释
ctrl + /
导入该行需要的类
alt + enter
快速格式化美化代码
ctrl + alt + l
生成构造器
alt + insert
或者鼠标右键选择generate,再选择constractor
按住ctrl不松手可以选择全部变量
定位方法
将光标放在一个方法上,输入 ctrl + B,可以自动定位到方法位置
自动分配变量名
末尾加上 .var 便可自动生成变量名
模板
file -> settings -> editor -> Live templates
查看有哪些模板快捷键/可以自己增加模板
插入异常处理
ctrl + Alt + T
实现接口需要重写的方法
Alt + Enter
快速抛出异常
Alt + Enter
面向对象编程(中级)
包
包基础知识
-
包的作用
- 区分相同名字的类
- 当类很多的时候,可以很好的管理类
- 控制访问范围
-
包的本质:实际上就是创建不同的文件夹保存类文件
-
包的命名规则:只能包含数字,字母,下划线,小圆点,但不能用数字开头,不能是关键字或保留字
demo.class.execl1 //class是关键字
demo.12a.Test //12a数字开头
- 包的命名规范:一般是小写字母加小圆点
com.公司名.项目名.业务模块名
举例:com.sina,crm.user //用户模块
- Java常用包
java.lang //lang包是基本包,默认引入,不需要再引入
java.util //util包是系统提供的工具包,工具类,比如Scanner
java.net //网络包,网络开发
java.awt //是做Java的页面开发,GUI
包的使用
//注意:
//建议:我们需要使用哪个类,就哪个类即可,不建议使用*导入
import java.util.Scanner; //表示只引入java.util包下的Scanner类
import java.util.*; //表示将java.util包下的所有类都引入
访问修饰符
- 公开级别:用
public
修饰,对外公开 - 受保护级别:用
protected
修饰,对子类和同一个包中的类公开 - 默认级别:
没有修饰符号
,向同一个包的类公开 - 私有级别:用
private
修饰,只有类本身可以访问,不对外公开
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 | |
---|---|---|---|---|---|---|
1 | 公开 | public | ✔ | ✔ | ✔ | ✔ |
2 | 受保护 | protected | ✔ | ✔ | ✔ | X |
3 | 默认 | 没有修饰符号 | ✔ | ✔ | X | X |
4 | 私有 | private | ✔ | X | X | X |
-
细节:修饰符可以修饰类中的属性,方法,类
只有默认和public可以修饰类
面向对象编程三大特征
封装、继承和多态
封装
封装基础
- 定义
封装就是把抽象出来的数据[属性]
和对数据的操作[方法]
封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法]
,才能对数据进行操作
-
好处
- 隐藏实现细节:用户无需关注方法的实现细节,只需要传入参数,对方法进行调用
- 可以对传入的数据进行验证,保证安全合理
-
封装的实现步骤
-
将属性进行私有化private(让外部不能直接修改属性)
-
提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(形参){ //Xxx表示某个属性 //加入数据验证的业务逻辑 属性 = 参数名; }
-
提供一个公共的(public)get方法,用于获取属性的值
public 数据类型 getXxx(){ //Xxx表示某个属性 //加入权限判断的业务逻辑 return Xxx; }
-
-
Tips:
IDEA中可以通过鼠标右键进入generate,再选择Getter and Setter,按住ctrl即可选择多个变量设置封装方法
封装与构造器
如果还是和之前一样创建构造器,让this.age = age;那么对age传入数据的判断与封装便毫无意义,所以我们需要在构造器中调用set(Xxx)方法
便可以了
public class Person {
String name;
private int age;
private double salary;
public Person() {
}
public Person(String name, int age, double salary) {
setName(name); //构造器中调用set(Xxx)方法以完善封装
setAge(age);
setSalary(salary);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() < 2 || name.length() > 6) {
System.out.println("姓名应该在2~6个字符之间");
this.name = "佚名";
} else {
this.name = name;
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 1 && age <= 120) {
this.age = age;
} else {
System.out.println("输入年龄有误,应该在1~120之间");
this.age = 18;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void info() {
System.out.println(name + " " + age + " " + salary);
}
}
继承
继承基础
为了代码复用性,当多个类存在相同的属性和方法时,可以从这些类中抽取出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends声明继承父类即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4RrFq0y2-1676111541445)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221028100939418.png)]
- 语法
class 子类 extends 父类{
}
细节
-
子类继承了父类的所有属性和方法,但是私有属性和方法不能在子类直接访问,要通过公共的方法去访问
默认属性和方法若和主类在同一个包内可以访问,不在则也不可以访问(访问修饰符知识)
-
子类必须调用父类构造器,完成对父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器,实现对父类的初始化工作,否则编译报错
-
如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
-
super在使用时,需要放在构造器第一行(super只能在构造器中使用)
-
super()和this()都需要放在构造器第一行,所以这两个不能共存一个构造器中
-
java所有类都是object的子类,object是所有类的基类
-
父类构造器的调用不限于直接父类,将一直往上追溯直到object类(顶级父类)
-
子类最多只能继承一个父类(指直接继承),即Java中是单继承机制
思考:如何让A同时继承B类和C类属性和方法呢(让A继承B类,B类再继承C类即可)
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
Person is a Music? Person extends Music is 不合理
Cat is an Animal? Cat extends Animal is 合理
变量访问规则
- 按照查找关系来确定访问的是哪个类中的哪个变量
- 首先看子类是否有该属性
- 如果子类有这个属性,并且可以访问,则返回这个属性的信息(当子类有这个属性,却没有访问权限时候,它不会再接着去父类查找了,而会直接报错)
- 如果子类没有这个属性,就看父类有没有这个属性(如果父类有这个属性,并且可以访问,则返回这个属性的信息…)
- 如果父类没有就按照(3)的规则,继续找上级父亲,直到Object…
super关键字
- super代表父类的引用,用于访问父类的属性,方法,构造器
- 访问父类的属性(或方法),但不能访问父类的private属性(或方法) super.属性名(或方法名)
- 访问父类的构造器(详见继承细节)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super(直接跳过查看本类)
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以通过super去访问爷爷类的成员(如果多个基类中都有同名的成员,使用super访问要依据变量访问规则)
No. | 区别点 | this | super |
---|---|---|---|
1 | 访问权限 | 访问本类中的属性,没有则到父类中继续查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,没有则到父类中继续查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器首行 | 调用父类构造器,必须放在子类构造器首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
方法覆盖(重写)
-
子类的某个方法方法名称和父类的一模一样,参数和父类的一模一样,返回类型也和父类方法一样或者返回类型是父类返回类型的子类,就称子类的这个方法覆盖了父类的那个方法(子类返回类型也和父类方法一样,或者返回类型是父类返回类型的子类,比如父类返回类型是Object,子类方法返回类型是String)
-
子类方法不能缩小父类方法的访问权限(比如父类是默认,子类可以是public,protected,默认但不能是private这种更小权限)
多态
方法或对象具有多种形态,是面向对象的第三大特征,,多态是建立在封装和继承基础之上的
多态的具体体现
- 方法的多态:重写和重载就体现多态
- 对象的多态(核心):(1)一个对象的编译类型和运行类型可以不一致*(运行类型可以是编译类型的子类)*;(2)编译类型在定义对象时就确定了,不能改变;(3)运行类型可以是变化的;(4)编译类型看等号的左边,运行类型看等号的右边;(4)对于方法的形式参数而言,实际参数可以传入形式参数子类对象(此时形参就是编译类型,实参就是运行类型)
*举例:*Animal animal = new Dog();【Dog类是Animal类的子类,animal的编译类型为Animal,运行类型为Dog】
animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是Animal】
package Test308;
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.cry(); //animal类不同的运行类型体现了对象的多态
animal = new Dog();
animal.cry();
animal = new Cat();
animal.cry();
}
}
public class Animal {
public void cry(){
System.out.println("动物在叫。。。");
}
}
public class Cat extends Animal{
public void cry(){
System.out.println("猫在叫。。。");
}
}
public class Dog extends Animal{
public void cry(){
System.out.println("狗在叫。。。");
}
}
package Test308;
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.cry(animal);
Dog dog = new Dog();
animal.cry(dog); //对于方法的形式参数而言,实际参数可以传入形式参数子类对象(此时形参就是编译类型,实参就是运行类型)
Cat cat = new Cat();
animal.cry(cat); //对于方法的形式参数而言,实际参数可以传入形式参数子类对象(此时形参就是编译类型,实参就是运行类型)
}
}
public class Animal {
public void cry(Animal animal){
System.out.println("动物在叫。。。");
}
}
public class Cat extends Animal{
public void cry(){
System.out.println("猫在叫。。。");
}
}
public class Dog extends Animal{
public void cry(){
System.out.println("狗在叫。。。");
}
}
细节
-
多态的前提是:两个对象存在继承关系
-
多态的向上转型:父类的引用指向了子类的对象(语法:父类类型 引用名 = new 子类类型();)可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员(因为在编译阶段能调用哪些是由编译类型决定的)
-
多态的向下转型:语法:子类类型 引用名 = (子类类型)父类引用;只能强转父类的引用,不能强转父类的对象;要求父类的引用必须指向的是当前目标类型的对象;可以调用子类类型中所有的成员
Animal animal = new Dog(); Dog dog = (Dog) animal;
-
属性没有重写之说,属性的值看编译类型
-
动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用(看编译类型)
instanceof
- 如果instanceof前面的对象的运行类型是instanceof后面的类的子类或者和instanceof后面的类相同,则返回true
Animal animal = new Animal();
System.out.println(animal instanceof Dog); //false
多态的应用
- 多态数组
public class Test {
public static void main(String[] args) {
Person[] person = new Person[5]; //设置父类数组,运行类型可以为子类
person[0] = new Person("李", 20);
person[1] = new Student("李", 21, 100);
person[2] = new Student("王", 21, 99);
person[3] = new Teacher("ling", 32, 1000000);
person[4] = new Teacher("shao",33,1000000);
for (int i = 0; i < person.length; i++) {
System.out.println(person[i].say());
if(person[i] instanceof Student){ //设置判断才能调用子类特有方法
((Student) person[i]).study(); //调用子类特有方法(父类没有)需向下转型
} else if (person[i] instanceof Teacher) {
((Teacher) person[i]).teach();
}
}
}
}
public class Person {
private String name;
private int age;
public Person(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;
}
public String say() {
return name + "\t" + age;
}
}
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String say() {
return super.say() + "\t" + score;
}
public void study() {
System.out.println(getName() + " is studing");
}
}
public class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() {
return super.say() + "\t" + salary;
}
public void teach() {
System.out.println(getName() + " is teaching");
}
}
- 多态参数
public void testWork(Employee e){ //多态参数本质就是向上转型,达到可以输入本类以及子类
if(e instanceof Worker){
((Worker) e).work(); //向下转型访问子类的独有方法
} else if (e instanceof Manage) {
((Manage) e).manage();
}
}
查看JDK原码
- 鼠标放在方法上面,鼠标右键选择go to,再选择declaration and usages即可
Object类
equals方法
- == 与 equals的比较
- ==如果判断基本类型,判断的是值是否相等;如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象(不看编译类型)
- equals是Object类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中常常重写该方法,用于判断内容是否相等(语法:引用类型的对象.equals(引用类型的对象))
int a = 10;
double b = 10.0;
System.out.println(a == b); //true
String string1 = new String("abcde");
String string2 = new String("abcde");
System.out.println(string1 == string2);//false,因为对于引用类型是判断地址
System.out.println(string1.equals(string2));//true;String子类改写了equals方法使得可以比较字符串是否相同内容(JDK中可以查看原码)
Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
System.out.println(integer1 == integer2);//false;因为对于引用类型是判断地址
System.out.println(integer1.equals(integer2));//true;Integer子类也重写了(JDK中可以查看原码)
自己重写equals方法,使得可以判断两个类所有的属性是否相等
public class EqualsExercise {
public static void main(String[] args) {
Person person1 = new Person("li", 20, '男');
Person person2 = new Person("li", 20, '男');
System.out.println(person1.equals(person2));
}
}
class Person {
private String name;
private int age;
private char gender;
public boolean equals(Object obj) {
if (obj == this) { //先判断两个类是不是一个对象
return true;
} else if (obj instanceof Person) { //再判断类型
Person p = (Person) obj; //向下转型
return p.name.equals(this.name) && p.age == this.age && p.gender == this.gender;
//return ((Person) obj).name.equals(this.name) && ((Person) obj).age == this.age &&((Person) obj).gender == this.gender;
}
return false;
}
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
hashCode方法
- 返回该对象的哈希码值
- 提高具有哈希结构的容器的效率
- 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
- 两个引用如果指向同一个对象,则哈希值肯定是一样的
- 在集合中,如果需要的话,也会重写hashCode
public class Hashcode_ {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
A a3 = a1;
System.out.println(a1.hashCode());
System.out.println(a2.hashCode());
System.out.println(a3.hashCode());
}
}
class A{}
//运行:
1324119927
990368553
1324119927
toString方法
-
默认返回:全类名+@+哈希值的十六进制(全类名:包名加类名),子类往往重写toString方法,用于返回对象的属性信息
-
当直接输出一个对象时,toString方法会被默认调用
public class ToString_ {
public static void main(String[] args) {
Monster monster = new Monster();
System.out.println(monster.toString());
System.out.println(monster);//直接输出一个对象时默认调用monster.toString()
}
}
class Monster {
}
//运行:
com.test322.Monster@4eec7777
com.test322.Monster@4eec7777
finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,则JVM就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定的(即由自己的GC算法),也可以通过System,gc()主动触发垃圾回收机制
public class Finalize_ {
public static void main(String[] args) {
AA a = new AA();
a = null; //让对象失去引用
//主动触发垃圾回收机制,此时垃圾回收以及后续语句多线程同时执行,所以程序结束语句会先执行,调用后至
System.gc();
System.out.println("程序结束");
}
}
class AA{
@Override
protected void finalize() throws Throwable { //重写finalize()方法
System.out.println("销毁了对象");
System.out.println("释放了资源");
}
}
//运行:
程序结束
销毁了对象
释放了资源
断点调试
F8逐行执行代码
F7跳入方法体内部
shift+F8跳出方法体内部
F9直接执行到下一个断点
断点可以在debug时,动态下断点
面向对象编程(高级)
类变量
也叫静态变量,会被类的所有对象实例共享
- 语法:访问修饰符 static 数据类型 变量名
public class Test {
public static void main(String[] args) {
//类名.类变量名访问
System.out.println(A.name);
A a = new A();
//对象名.类变量名访问
System.out.println(a.name);
}
}
class A {
//类变量的访问需要遵循访问权限
public static String name;
}
类方法
也叫静态方法,会被类的所有对象实例共享
静态方法就可以访问静态属性或变量
- 使用细节:
- 类方法中不允许使用和对象有关的关键字,比如this和super
- 静态方法中只能访问静态变量或静态方法(非静态成员只能通过new实例化才能在静态方法中被调用)
- 普通成员方法既可以非静态成员,也可以访问静态成员(遵循访问权限)
class A {
//类变量的访问需要遵循访问权限
public static int fee;
public static void setPay(int fee) {
//this.fee = fee; //this关键字在静态方法中不可使用
A.fee = fee; //静态初始化
}
main方法
- public static void main(String[] args)
- main方法时虚拟机调用
- Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
- Java虚拟机执行类的main()方法时不必创建对象,所以该方法必须是static
- 该方法接受String类型对的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数
- java 执行的程序 参数1 参数2 参数3
public class Test {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
//命令行编译:
javac Test.java
java Test tom jack smith
//结果
tom
jack
smith
IDEA中通过Edit Configurations中的Program arguments(程序参数)中填入参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqA9mC0N-1676111541446)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221111173313717.png)]
- 细节
- 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
- 但不能直接访问该类中的非静态成员,必须创建该类的一个实例对象,才能通过这个对象来访问该类中的非静态成员
代码块
其实说白了就是类中方法体之外只能写属性初始化这种语句,无法直接写基本语句,虽然构造器也可以写其他语句,这个代码块其实就是一种补充,很简洁的在类中也可以直接写基本语句
class A{
int name; //写属性是对的
System.out.println(); //错误,类的方法体,构造器或主方法外不可直接写基本语句
}
代码块又称为初始化块,属于类的成员,类似于方法,但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用
- 基本语法:
[修饰符]{
代码
};
-
语法注意点:
- 修饰符可选,要写的话也只能写static
- 有static修饰的叫做静态代码块,没有static修饰的,叫普通代码块/非静态代码块
- ;号可以写上,也可以省略
- 一般用于当多个构造器都有重复语句时,抽取到初始化块中,提高代码的复用性
-
使用细节
-
静态代码块的主要作用是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次,如果是普通代码块,每创建一个对象,都会执行
-
类什么时候被加载:
- 创建对象实例时(new)
- 创建子类对象实例时候,父类会被加载,而且父类先被加载,子类后被加载
- 使用类的静态成员时(静态属性,静态方法)[父类先被加载,子类后被加载,最后再使用类的静态成员]
public class Test { public static void main(String[] args) { System.out.println(B.age); System.out.println(B.age); //静态代码块只会被执行一次 } } class A { static { System.out.println("A的静态代码1被执行"); } } class B extends A { public static int age = 20; static { System.out.println("B的静态代码1被执行"); } } //运行: A的静态代码1被执行 B的静态代码1被执行 20 20
-
普通代码块:在创建对象实例时(new对象时),会被隐式调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行
-
创建一个对象时,在一个类的调用顺序是:(重点)
- 调用静态代码块和静态属性初始化(优先级一样,按照定义顺序调用)
- 调用普通代码块和普通属性的初始化(优先级一样,按照定义顺序调用)
- 调用构造方法
-
构造器的最前面其实隐含了super()和调用普通代码块,静态相关的代码块在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
public class Test { public static void main(String[] args) { new B(); } } class A { { System.out.println("父类A的普通代码块1被执行"); } A() { System.out.println("父类A的构造器被执行"); //隐含了super()和调用普通代码块 } } class B extends A { { System.out.println("子类B的普通代码块1被执行"); } B() { System.out.println("子类B的构造器被执行"); //隐含了super()和调用普通代码块 } } //运行: 父类A的普通代码块1被执行 父类A的构造器被执行 子类B的普通代码块1被执行 子类B的构造器被执行
-
静态代码块只能直接调用静态成员,普通代码块可以直接调用任意成员
-
-
总结:创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
-
父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
-
子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
-
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
-
父类的构造方法
-
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
-
子类的构造方法
public class Test { public static void main(String[] args) { new B(); } } class A { public static int age = getval(); static { System.out.println("父类A的静态代码1被执行"); } { System.out.println("父类A的普通代码块1被执行"); } A() { System.out.println("父类A的构造器被执行"); } public static int getval(){ System.out.println("getA"); return 10; } } class B extends A { public static int age = getval(); { System.out.println("子类B的普通代码块1被执行"); } static { System.out.println("子类B的静态代码1被执行"); } B() { System.out.println("子类B的构造器被执行"); } public static int getval(){ System.out.println("getB"); return 10; } } //运行: getA 父类A的静态代码1被执行 getB 子类B的静态代码1被执行 父类A的普通代码块1被执行 父类A的构造器被执行 子类B的普通代码块1被执行 子类B的构造器被执行
-
单例设计模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
饿汉式
饿汉意思是很着急,一旦加载,这个对象也被创建好了,即使你没有使用到这个对象
-
步骤
-
构造器私有化
-
类的内部创建对象(该对象是static)
-
向外暴露一个静态的公共方法
public class Test { public static void main(String[] args) { GirlFriend a = GirlFriend.getInstance(); GirlFriend b = GirlFriend.getInstance(); System.out.println(a.equals(b)); //输出为true,证明了是单例设计模式 } } class GirlFriend { private static GirlFriend gd = new GirlFriend(); //静态属性只会在类加载时初始化一次,因此保证了类只有一个对象 private GirlFriend() { //保证无法自己new一个对象 } public static GirlFriend getInstance(){ //构造器被限制所以无法通过对象访问这个公共方法,所以设置为static return gd; } }
-
懒汉式
步骤与饿汉式类似,不过类加载时候不会创建对象,只有当用户使用getInstance()方法时,才会创建并返回对象
public class Test {
public static void main(String[] args) {
GirlFriend a = GirlFriend.getInstance();
GirlFriend b = GirlFriend.getInstance();
System.out.println(a.equals(b)); //输出为true,证明了是单例设计模式
}
}
class GirlFriend {
private static GirlFriend gd;
private GirlFriend() { //保证无法自己new一个对象
}
public static GirlFriend getInstance() { //构造器被限制所以无法通过对象访问这个公共方法,所以设置为static
if(gd == null){
gd = new GirlFriend();
}
return gd;
}
}
饿汉式VS懒汉式
- 二者最主要区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式式在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题(因为如果多个线程同时访问到 if(gd == null) 语句时,会同时创建多个对象,最后返回保留最后一个返回的对象)
- 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用(其他方式完成了类的加载),那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
final
final可以修饰、属性、方法和局部变量
- 使用场景:
- 当不希望类被继承时,可以用final修饰
- 当不希望父类的某个方法被子类覆盖/重写时,可以用final关键字修饰
- 当不希望类的某个属性的值被修改,可以用final修饰。
- 当不希望某个局部变量被修改,可以使用final修饰
-
细节:
-
final修饰的属性又叫常量(按照标识符命名规范,一般用字母全大写来命名)
-
final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:
- 定义时:如 public final double TAX_RATE = 0.08;
- 在构造器中(非静态属性)
- 在代码块中
-
如果final修饰的属性是静态的,则初始化的位置只能是
- 定义时
- 在静态代码块中(不能在构造器中赋值)
-
final类不能继承,但是可以实例化对象
-
如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
-
一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
-
final不能修饰构造方法(即构造器)
-
final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
public static final int age = 16;
-
包装类(Integer,Double,Flout,Boolean等等都是final),String也是final类
-
抽象类
基础
- 当父类的一些方法不能确定时,考虑将该方法设计为抽象方法,所谓抽象方法就是没有实现的方法,所谓没有实现就是没有方法体
- 当一个类中存在抽象方法时,需要将该类声明为abstract类,用abstract来修饰该类就是抽象类
访问修饰符 abstract 类名 {
访问修饰符 abstract 返回类型 方法名(参数列表); (没有方法体)
}
-
一般来说,抽象类会被继承,由其子类来实现抽象方法
-
细节:
-
抽象类不能被实例化
-
抽象类不一定要包含abstract方法
-
一旦类包含了abstract方法,则这个类必须声明为abstract
-
abstract只能修饰类和方法,不能修饰属性和其他的
-
抽象类可以有任意成员【抽象类本质还是类】
-
抽象方法不能有主体,即不能实现
abstract void aaa() { } //错误,出现了{}这个方法体了
-
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法(重写),除非它自己也声明为abstract类
-
抽象方法不能使用private 、final和 static来修饰,因为这些关键字都是和重写相违背的
-
应用:模板设计模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVCUXUcQ-1676111541446)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221115184621474.png)]
接口
概念
interface 接口名{
属性
方法
}
class 类名 implements 接口{
自己的属性
自己的方法
必须实现的接口的抽象方法
}
-
在接口中,抽象方法,可以省略abstract关键字
-
在JDK7以前,接口里的所有方法都没有方法体,即都是抽象方法
-
在JDK8后,可以有静态方法,也可以有默认方法,普通方法需要使用default关键字修饰
-
细节:
- 接口不能被实例化
- 接口中的所有方法必须是public方法(没有修饰符默认是public),接口中的抽象方法,可以不用abstract修饰 ( void hi() 等价于 public abstract void hi() )
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口 (class 类名 implements 接口1,接口2())
- 接口中的属性,只能是final的,而且是public static final的修饰符。(比如:int a = 1; 实际上等价于public static final int a = 1;)
- 接口中属性的访问形式:接口名.属性名(因为默认是static的)
- 一个接口不能继承其他类,但是可以继承多个别的接口(interface A extends B,C {} )(普通类是单继承机制的)
- 接口的修饰符只能是 public 和 默认,这点和类的修饰符是一样的
实现接口vs继承类
- 可以简单来说,接口是Java单继承机制的一种补充,一个接口可以同时继承多个接口,一个类又可以实现多个接口
- 接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足like - a的关系
- 接口的价值主要在于:设计好各种规范(方法),让其他类去实现这些方法
接口多态特性
接口和实现接口的类同样和继承一样体现了多态,我们可以对照着看下面的例子
public class Test {
public static void main(String[] args) {
Usb[] usb = new Usb[3];
usb[0] = new Camera();
usb[1] = new Phone();
usb[2] = new Camera();
for (int i = 0; i < usb.length; i++) {
useWork(usb[i]);
if (usb[i] instanceof Phone) { //多态的向下转型
((Phone) usb[i]).call();
}
}
}
public static void useWork(Usb usb){ //多态的向上转型
usb.work(); //动态绑定
}
}
interface Usb {
void work();
}
class Camera implements Usb {
@Override
public void work() {
System.out.println("camera is working");
}
}
class Phone implements Usb {
@Override
public void work() {
System.out.println("phone is working");
}
public void call(){ //特有方法必须向下转型调用
System.out.println("l am calling");
}
}
- 接口动态传递
如果一个接口继承了另一个接口,而一个类实现了子接口,那么实际上相当于这个类也实现了父接口(继承知识),此时接口类型的变量也可以指向这个类的对象实例。这被称为“接口动态传递”
public class Test {
public static void main(String[] args) {
//接口类型的变量可以指向实现了该接口的类的对象实例
Usb1 usb1 = new Phone();
/*
如果一个接口继承了另一个接口,而一个类实现了子接口
那么实际上相当于这个类也实现了父接口(继承知识)
此时接口类型的变量也可以指向这个类的对象实例
这被称为“接口动态传递”
*/
Usb2 usb2 = new Phone();
}
}
interface Usb1 extends Usb2{}
interface Usb2{}
class Phone implements Usb1{ }
思考题
看看下面代码是否有错误
public class Test {
public static void main(String[] args) {
B b = new B();
b.getA();
}
}
interface A1 {
int a = 1; //等价于 public static final int a = 1;
}
class A2 {
int a = 2;
}
class B extends A2 implements A1 { //类的定义更加完善了
public void getA() {
System.out.println(a); //直接写a会报错,因为编译器无法判断调用哪个a
}
}
//修改:
System.out.println(A1.a + " " + super.a); //这样写编译器便可以区分开来
内部类
局部内部类
-
局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
-
作用域:仅仅在定义它的方法或代码块中。
-
局部内部类 访问 外部类的成员【访问方式:直接访问】
-
外部类 访问 局部外部类的成员
访问方式:创建对象,再访问
-
外部其他类不能访问局部内部类(因为局部内部类地位是以一个局部变量)
-
如果外部类和局部内部类重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
class A { int age = 10; public void work() { class B { //局部内部类 public void getAge() { int age = 20; System.out.println("age = " + age); //重名变量依旧按照就近原则 System.out.println(A.this.age); //访问重名属性得 类名.this.属性名(类名.this指代的是A类,直接this指带的是内部类) } } //局部内部类作用域:只在定义它的方法或代码块中 B b = new B(); //外部类访问局部类,在方法中创建对象,访问即可 b.getAge(); //局部内部类访问外部类成员,直接访问即可 } }
证明:如果外部类和局部内部类重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class Test { public static void main(String[] args) { A a = new A(); a.work(); System.out.println(a); } } class A { public void work() { class B { //局部内部类 public void getAge() { System.out.println("A.this "+ A.this); //证明A.this才代表A类的属性 System.out.println("this "+ this); //直接this指代的是内部类B } } B b = new B(); b.getAge(); } } //运行: A.this homework.test414.A@682a0b20 this homework.test414.A$1B@3d075dc0 homework.test414.A@682a0b20
-
匿名内部类
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
new 类/接口(参数列表){
};
public class Test {
public static void main(String[] args) {
new A().inClass();
}
}
class A {
public void inClass(){
/* 底层原理:
1.接口本来是不可以直接创建对象的,
但是这里的new B()其实系统会为它创建一个匿名内部类
这里的b的编译类型是B,运行类型其实是匿名内部类
2.编译器内部会创建一个匿名内部类去实现B接口:
class XXXX implements B{
public void cry() {
System.out.println("B is crying");
}
}
3.这里的类名是系统给的,所以被称为匿名内部类,其实XXXX是外部类名+$+第几个匿名内部类
比如这里就是A$1
4.jdk底层在创建A$1这个匿名内部类,马上就创建了A$1实例,并把地址返回给了b对象
*/
B b = new B(){
@Override
public void cry() {
System.out.println("B is crying");
}
}; //创建结束必须写上;
/*
编译器内部同样会创建一个匿名内部类,不过是去继承C类
其他和基于接口的匿名内部类完全一样
class XXXX extends C{
void work() {
super.work();
}
}
*/
C c = new C(10){ //基于类的匿名内部类(与接口类似)
@Override //上面的传入参数会直接传给C类的构造器
void work() {
super.work();
}
};
System.out.println(b.getClass()); //验证一下匿名内部类名称(getClass()方法会打印运行型)
System.out.println(c.getClass());
b.cry();
}
}
interface B {
void cry();
}
class C{
int age ;
public C(int age) {
this.age = age;
}
void work(){
}
}
//运行:
class homework.test416.A$1
class homework.test416.A$2
B is crying
-
细节:
- 匿名内部类不可以有构造方法
- 其他访问细节和局部内部类一样
-
实际使用
public class Test {
public static void main(String[] args) {
new cellPhone().alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
new cellPhone().alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell{
void ring();
}
class cellPhone{
public void alarmclock(Bell bell){
bell.ring();
}
}
成员内部类
-
成员内部类是定义在外部类的成员位置,并且没有static修饰
-
可以直接访问外部类的所有成员,包含私有的
-
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
-
作用域:和外部其他成员一样,为整个类体
-
成员内部类 访问 外部类成员【直接访问】
-
外部类 访问 成员内部类【创建对象,再访问】
-
外部其他类 访问 成员内部类【两种方法:见代码实例注释】
-
如果外部类和成员内部类重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class Test { public static void main(String[] args) { A a = new A(); /*外部其他类使用成员内部类的方式: 第一种方式: 把new B()看成是外部类对象a的成员 这就是一个语法,不要特别纠结 */ A.B b1 = a.new B(); //b1和b2本质一样 A.B b2 = new A().new B(); /*第二种方法: 在外部类中,编写一个方法,可以返回B类对象 */ A.B b3 = a.getB(); //b3和b4本质一样 A.B b4 = new A().getB(); } } class A { class B { public void work() { System.out.println("working"); } } public B getB(){ return new B(); } }
-
静态内部类
-
静态内部类是定义在外部类的成员位置,并且有static修饰
-
可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
-
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
-
静态内部类 访问 外部类(比如:静态属性) 【访问方式:直接访问所有静态成员】
-
外部类 访问 静态内部类【创建对象,再访问】
-
外部其他类 访问 静态内部类【三种方法:见代码实例注释】
-
如果外部类和静态内部类重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名…成员)去访问
public class Test { public static void main(String[] args) { //方法1:静态调用 A.B b1 = new A.B(); //方法2:通过方法调用 A.B b2 = new A().getB(); //方法3:通过静态方法调用 A.B b3 = A.getB_(); } } class A{ static class B{ } public B getB(){ //普通成员既可以直接调用静态方法,也可以调用其他 return new B(); } public static B getB_(){ //静态成员可以直接调用静态方法 return new B(); } }
-
枚举和注解
枚举
将类的属性固定为几个固有的,防止被修改
自定义枚举
- 将构造器私有化,目的防止直接 new
- 去掉setXxx方法,防止属性被修改
- 在Season内部,直接创建固定的对象(并对外暴露对象public static final)
- 优化,可以加入final修饰符
public class Test {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUMN);
System.out.println(Season.WINTER);
}
}
class Season {
private String name;
private String desc;
public static final Season SPRING = new Season("春天","温和");
public static final Season SUMMER = new Season("夏天","热烈");
public static final Season AUTUMN = new Season("秋天","凉爽");
public static final Season WINTER = new Season("冬天","寒冷");
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
enum枚举类
public class Test {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUMN);
System.out.println(Season.WINTER);
}
}
enum Season {
/*
使用enum来实现枚举类
1. 使用关键字enum替代class
2.直接使用SPRING ("春天","温和")代替创建对象初始化
3.如果有多个常量(对象),使用,号间隔即可
4.如果使用enum来实现枚举,要求将定义常量对象 写在最前面
*/
SPRING ("春天","温和"),SUMMER ("夏天","热烈"),AUTUMN ("秋天","凉爽"),WINTER ("冬天","寒冷");
private String name;
private String desc;
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
-
细节:
- 当我们使用enum关键字开发一个枚举类时,默认会继承Enum类,而且是一个final类(可以用javap反编译查看类的继承关系)
- 如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用, 间隔,最后有一个分号结尾
- 枚举对象必须放在枚举类的行首
-
Enum成员方法
- name()返回对象实例名,最好用toString();
- ordinal()返回 枚举对象的次序/编号,从0开始
- values() 返回的是枚举类里所有枚举信息的数组
- compareTo() 比较前后两个对象的枚举对象的编号差(前 - 后)
public class Test { public static void main(String[] args) { Week[] days = Week.values(); //values() System.out.println("===所有星期的信息如下=="); for(Week i: days){ //小tips:增强for(变量a:数组b) 将数组b里的成员一个一个赋给变量a System.out.println(i); } System.out.println(days[0].compareTo(days[2]));//比较前后两个对象的枚举对象的编号差 } } enum Week{ MONDAY("星期一"),TUESDAY("星期二"),WEDNSDAY("星期三"),THUESDAY("星期四"),FRIDAY("星期五"); private String desc; private Week(String desc){ this.desc = desc; } @Override public String toString() { return desc ; } } //运行: ===所有星期的信息如下== 星期一 星期二 星期三 星期四 星期五 -2
注解
@Override
- @Override注解放在Xxx方法上,说明子类的Xxx方法时重写了父类的Xxx方法
- 如果你写了@Override注解,编译器就会去检验该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
- 如果看到@interface 表示一个注解类
- @Override的注解原码为@Target(ElementType.METHOD)说明只能修饰方法
- @Target是修饰注解的注释,被称为源注解
@Deprecated
- @Deprecated修饰某个元素,表示该元素已经过时
- 即不再推荐使用,但是仍然可以使用
- 可以修饰 方法,类,字段,包,参数 等等
- @Deprecated 可以做版本升级过渡
@SuppressWarnings()
-
当我们不希望看到这些警告的时候,可以使用@SuppressWarnings()注解来抑制警告信息
-
在{ “ ” } 中,可以写入你希望抑制(不显示)警告信息
关键字 用途 all to suppress all warnings (抑制所有警告) boxing to suppress warnings relative to boxing/unboxing operations (抑制装箱、拆箱操作时候的警告) cast to suppress warnings relative to cast operations (抑制映射相关的警告) dep-ann to suppress warnings relative to deprecated annotation (抑制启用注释的警告) deprecation to suppress warnings relative to deprecation (抑制过期方法警告) fallthrough to suppress warnings relative to missing breaks in switch statements (抑制确在switch中缺失breaks的警告) finally to suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告) hiding to suppress warnings relative to locals that hide variable(抑制相对于隐藏变量的局部变量的警告) incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句) nls to suppress warnings relative to non-nls string literals( 忽略非nls格式的字符) null to suppress warnings relative to null analysis( 忽略对null的操作) rawtypes to suppress warnings relative to un-specific types when using generics on class params( 使用generics时忽略没有指定相应的类型) restriction to suppress warnings relative to usage of discouraged or forbidden references( 抑制禁止使用劝阻或禁止引用的警告) serial to suppress warnings relative to missing serialVersionUID field for a serializable class( 忽略在serializable类中没有声明serialVersionUID变量) static-access to suppress warnings relative to incorrect static access( 抑制不正确的静态访问方式警告) synthetic-access to suppress warnings relative to unoptimized access from inner classes( 抑制子类没有按最优方法访问内部类的警告) unchecked to suppress warnings relative to unchecked operations( 抑制没有进行类型检查操作的警告) unqualified-field-access to suppress warnings relative to field access unqualified( 抑制没有权限访问的域的警告) unused to suppress warnings relative to unused code( 抑制没被使用过的代码的警告) -
关于@SuppressWarnings()作用范围是和你放置的位置相关。 比如@SuppressWarnings()放置在main方法,那么抑制警告的范围就是main,通常我们可以放置具体的语句,方法,类
@SuppressWarnings({"unchecked","unused"," finally"})
元注解
- 修饰注解的注释
- Retention //指定注解的作用范围
- Target //指定注解可以在哪些地方使用
- Document //指定该注解能否在jacadoc体现(Java文档注释)
- Inherited //子类会继承父类注解
异常
-
Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不俗异常)
-
将代码块选中,输入快捷键ctrl + alt + t 选择 try - catch 就可以快速捕获异常
-
如果进行程序异常处理,那么即使程序出现异常,程序也可以继续执行
-
执行过程中所发生的异常事件可分为两大类
- Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误,资源耗尽等严重情况。比如:StackOverflowError【栈溢出】和OOM(out of memory),Error是严重错误,程序会崩溃
- Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常【程序运行时,发生的异常】和编译时异常【编程时,编译器检查出的异常】
异常体系图
- 异常分为两大类,运行时异常和编译时异常.
- 运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
- 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程
序的可读性和运行效率产生影响 - 编译时异常,是编译器要求必须处置的异常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4k6KDyBW-1676111541452)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221120222833488.png)]
五大运行时异常
- NullPointerException 当程序在需要对象的地方使用null时,抛出该异常
- ArithmeticException 当出现异常的运行条件时,抛出该异常,例如除法运算时,分母等于0
- ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常,如果索引为负或大于等于数组大小,则为非法索引
- ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常(向下转型错误)
- NumberFormatException 当程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
编译异常
-
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
-
SQLException//操作数据库时,查询表可能发生异常
-
IOException//操作文件时,发生的异常
-
FileNotFoundException//当操作一个不存在的文件时,发生异常
-
ClassNotFoundException//加载类,而该类不存在时,异常
-
EOFException/操作文件,到文件末尾,发生异常
-
llegalArguementException//参数异常
-
异常处理
- 对于编译异常,程序中必须处理,比如 try-catch 或 throws
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
try-catch-finally
try {
代码块(可能有异常)
} catch (Exception e) {
捕获到异常
1.当异常发生时
2.系统将异常封装成 Exception 对象e ,传递给catch
3.得到异常对象后,程序员自己处理
4.注意:如果没有发生异常,catch代码块不执行
} finally {
1.不管try代码块是否有异常发生,始终要执行finally
2.所以,通常将释放资源代码,放在finally
}
public class Test {
public static void main(String[] args) {
/*
1.如果try代码块有可能有多个异常
2.可以使用多个catch 分别捕获不同的异常,相应处理
3.要求子类异常写在前面,父类异常写在后面
*/
try {
int a = 10;
int b = 0;
A a1 = new A();
System.out.println(a1.getName());
System.out.println(a / b);
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
} catch (NullPointerException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("执行finally");
}
System.out.println("程序继续执行");
}
}
class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
throws
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSX5kTuu-1676111541453)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221121093802984.png)]
public void f2() throws FileNotFoundException ,NullPointerException ,Exception{
/*
1. 这里的异常是一个FileNotFoundException 编译异常
2. 使用throws,抛出异常,让调用f2方法的调用者(方法)处理
3. throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
4. throws关键字后也可以是异常列表,即可以抛出多个异常(中间通过,间隔)
*/
FileInputStream fis = new FileInputStream("d://aa.txt");
}
- 细节:
- 使用throws,抛出异常,让方法的调用者(方法)处理
- throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法所抛出的异常类型要么和父类抛出的异常一致,要么为父亲抛出的异常类型的子类型
自定义异常
public class Homework02 {
public static void main(String[] args) {
int age = 10;
if(age < 20 || age > 30){
throw new AgeException("年龄错误");
}
}
}
/*
如果继承RuntimeException 就是运行异常,如果继承Exception 就是编译异常了
*/
class AgeException extends RuntimeException {
public AgeException(String message) {
super(message);
}
}
throw VS throws
意义 | 位置 | 后面跟的东西 | 使用示例 | |
---|---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 | public void f2() throws Exception{ } |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常 | throw new 自定义异常名(初始化); |
常用类
包装类
基础
- 针对8种基本数据类型 相应的引用类型 ——包装类
基本数据类型 | 包装类 |
---|---|
boolean | Boolean |
char | Character |
byte | Byte |
int | Integer |
long | Long |
float | Float |
double | Double |
short | Short |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKZUfr27-1676111541453)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221122160652898.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTE5MmPR-1676111541454)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221122160731008.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mePEpEaI-1676111541454)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221122160616766.png)]
装箱与拆箱
public class Test {
public static void main(String[] args) {
/*
JDK5前类型转换时需要手动装箱和拆箱
手动装箱:基本数据类型->引用数据类型
手动拆箱:引用数据类型->基本数据类型
*/
int a = 10;
Integer integer =Integer.valueOf(a); //手动装箱
a = integer.intValue(); //手动拆箱
/*
JDK5(包含JDK5)后面就可以自动装箱和拆箱
*/
int b = 100;
Integer n1 = b; //自动装箱(系统自动调用Integer.valueOf()方法)
b = n1; //自动拆箱(系统自动调用intValue()方法)
}
}
包装类方法
public class Homework {
public static void main(String[] args) {
/*
包装类—>String
*/
Integer a = 100; //自动装箱
//方式1
String str1 = a + "";
//方式2
String str2 = a.toString();
//方法3
String str3 = String.valueOf(a);
/*
String—>包装类
*/
Integer b = Integer.parseInt(str1); //自动装箱
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TeMHIcKT-1676111541454)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221122195725439.png)]
String类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CQL9lgrQ-1676111541454)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221122213550259.png)]
public class Homework01 {
public static void main(String[] args) {
/*
1. String 对象用于保存字符串,也就是一组字符序列
2. 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
3. String 类有很多构造器,构造器的重载
常见的有 String s = new String();
String s1 = new String(String original);
String s2 = new String(char[] a);
String s3 = new String(char[] a, int startIndex, int count);
String s4 = new String(byte[] b);
4. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
实现了接口 Comparable【String 对象可以比较大小】
5.String 是final类,不能被其他的类继承
6.String 有属性 private final char value[]; 用于存放字符串内容
7.一定要注意:value 是一个final类型,不可以修改地址(值可以改变)
*/
String a ;
}
}
构造方法细节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThELmxkW-1676111541455)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221123181040794.png)]
- 直接赋值是在常量池看是否有,没有直接创建再指向,调用构造器则是先在堆中创建,再搜索常量池,要是常量池中有,则直接指向常量池,没有再在堆中创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IiS8wvf7-1676111541455)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221123223409893.png)]
String方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnKNzTRH-1676111541455)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126170306693.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-auzmARU6-1676111541455)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221124152942273.png)]
import java.util.Scanner;
public class StringTimeCount {
public static void main(String[] args) {
System.out.println("请输入原始数据:(数据应满足时间为 xx:xx ,而不应该包含xx:xx:xx,也就是包含几小时)");
System.out.println("最后一行输入0表示输入结束");
Scanner scanner = new Scanner(System.in);
String str = "";
while (true) {
String str_ = scanner.next();
if (str_.equals("0"))
break;
str += str_;
}
while (true) {
int index = str.indexOf(":"); //获取字符在字符串中第一次出现的索引
if (index == -1) {
return;
}
String str_ = str.substring(index - 2, index + 3);
// System.out.println(str_); //截取时间段的字符串
/*
我这边的截取只是为了配合我的TimeCountApp进行时间计算而略去了':'字符
*/
System.out.println(str.substring(index - 2, index)+""+str.substring(index+1 , index+3));
/*获取后就将它抹去,好在下次循环定位新的时间索引*/
str = str.replace(str_ , "");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiNcYX1c-1676111541456)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221124163016869.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRx8hd8C-1676111541456)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126165456228.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQmh8J1a-1676111541456)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126161936588.png)]
StringBuffer类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-74TTcvr6-1676111541456)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221124172712758.png)]
构造器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mzyIvze-1676111541457)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221124172903319.png)]
与String进行转换
public class Test {
public static void main(String[] args) {
//String -> StringBuffer
String str = "abc";
//方法1:使用构造器
StringBuffer stringBuffer1 = new StringBuffer(str);
//方法2:使用append方法
StringBuffer stringBuffer2 = new StringBuffer();
stringBuffer2 = stringBuffer2.append(str);
//StringBuffer -> String
StringBuffer stringBuffer3 = new StringBuffer("acd");
//方法1:使用StringBuffer提供的 toString方法
String s1 = stringBuffer3.toString();
//方法2:使用构造器
String s2 = new String(stringBuffer3);
}
}
方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oReOqhLg-1676111541457)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221124183419049.png)]
StringBuilder
-
一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder 不是线程安全)。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类.因为在大多数实现中,它比 StringBuffer 要快。
-
String、StringBuffer 和StringBuilder的比较
-
StringBuilder 和 StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
-
String:不可变字符序列,效率低,但是复用率高。
-
StringBuffer:可变字符序列、效率较高(增删)、线程安全,看源码
-
StringBuilder:可变字符序列、效率最高、线程不安全
-
String使用注意说明:
string s=“a”;//创建了一个字符串
S += “b”;//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+“b”(也就是"ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>***结论:***如果我们对String 做大量修改,不要使用String
-
-
String、StringBuffer 和StringBuilder的选择
使用的原则,结论:- 如果字符串存在大量的修改操作,一般使用 StringBuffer 或Stringpuilder
- 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder
- 如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
- 如果我们字符串很少修改,被多个对象引用,使用String, 比如配置信息等StringBuilder 的方法使用和StringBuffer一样,不再说
Math类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TownTnSo-1676111541457)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221125173328297.png)]
- Math.random()方法
返回 [0,1)范围的随机数
我们可以将其扩列到求 [a,b] 之间的随机整数:求[a , a+(b-a) ]范围 我们只要将a +(b-a)表示为a+random()*(b-a+1)就可以表示[a,b] 之间了
Arrays类
Arrays.toString(); 打印
Arrays.sort(); 排序
public class Test {
public static void main(String[] args) {
Integer[] arr = {1,-4,-2,-1,0};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) { //匿名内部类重写让排序变为降序
return o2 - o1;
}
});
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-miUhtwN3-1676111541457)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221125204619324.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MYDHfwgS-1676111541458)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221125204939471.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxoJaNve-1676111541458)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221125205102061.png)]
System类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nh6LgG4v-1676111541458)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126095826201.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywCCxrme-1676111541458)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126095803275.png)]
大数处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGCPAi87-1676111541458)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126104254497.png)]
- 因为有小数的除以有小数的有可能变成无限小数,所以小数除法要保留精度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlS8AHE7-1676111541459)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126103757440.png)]
Date
第一代日期类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfTG0XD8-1676111541459)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126145936403.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jaG1uN5e-1676111541459)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126150001613.png)]
第二代日期类
- 第二代日期类,主要就是Calendar类(日历)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9vUEyr7-1676111541459)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126151336909.png)]
第三代日期类
- (JDK8加入)LocaIDate(日期/年月日)、LocalTime(时间/时分秒)、LocalDateTime(日期时间/年月日时分秒)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r4avt5lA-1676111541459)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126153125307.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4AN7uA2-1676111541460)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126153934894.png)]
集合
集合体系图
- 解读:
- 集合主要是两组(单列集合,双列集合)
- Collection接口有两个重要的子接口 List Set,他们的实现子类都是单列集合
- Map接口的实现子类是双列集合,存放的K-V
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIP1elGH-1676111541460)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126211513301.png)]
Collection接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kch9J9XR-1676111541460)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126214507970.png)]
迭代器遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HjKgGD3Z-1676111541460)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221126222907677.png)]
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(13224);
col.add("asd");
col.add(0);
col.add(true);
System.out.println(col);
Iterator iterator = col.iterator(); //初始化迭代器
while (iterator.hasNext()){ //判断下一个点是否为空
System.out.println(iterator.next());
}
iterator = col.iterator(); //重置迭代器
}
}
增强for
public class Test {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(13224);
col.add("asd");
col.add(0);
col.add(true);
System.out.println(col);
for(Object ob: col){ //增强for可以被看作一个简单的Iterator迭代器
System.out.println(ob);
}
int[] nums = {1,34,7,-1};
for (int i : nums) { //增强for也可以用在数组
System.out.println(i);
}
}
}
List接口独有方法(Set没有)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hfm3Jxm2-1676111541460)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127094006039.png)]
ArrayList
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8uNyOOwy-1676111541461)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127102535265.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l5YQVOj2-1676111541461)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127104659818.png)]
Vector
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k5UKELZ4-1676111541461)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127155556210.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8oUCQoC-1676111541461)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127155943433.png)]
LinkedList
底层其实是一个双向链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icVhl9fS-1676111541461)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127165154746.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmS1KZhB-1676111541462)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127180344405.png)]
Set接口
- 可以用迭代器和增强for循环遍历,但是不能用get(索引)方式遍历
HashSet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfzOtO4v-1676111541462)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221127200809099.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTYljnKQ-1676111541462)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128081534473.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-38AA8Cv4-1676111541462)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128082022296.png)]
源码解读时间仓促并未来的及看,有时间会回溯
LinkedHashSet
是HashSet的子类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-44oouZES-1676111541462)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128084712126.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1Oh7oEi-1676111541463)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128085118535.png)]
Map接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2lx16bby-1676111541463)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128093311436.png)]
还没学完,有时间重学
总结如何选择集合实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SiJMJkAh-1676111541463)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128094459993.png)]
Collections工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LyjRJq4g-1676111541463)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128095401174.png)]
还没学完,有时间重学
泛型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNFNtqgD-1676111541463)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128143654306.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pVONsNmp-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128153721167.png)]
语法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ej8pKDH-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128155654115.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AJyiRLUS-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128161538993.png)]
自定义泛型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zhqry8G8-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128184714295.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DPicyP3H-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128192356388.png)]
Java绘图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gVPOGvlA-1676111541464)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221128213608357.png)]
多线程基础
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCecgO0f-1676111541465)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130190845043.png)]
继承Thread类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2thV7Ijb-1676111541465)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130171221569.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NR7B5c3c-1676111541465)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130190628695.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIoyLBu2-1676111541465)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130190643200.png)]
实现Runnable接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kskbNIH5-1676111541465)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130221713528.png)]
public class Test {
public static void main(String[] args) throws InterruptedException {
new Thread(new B()).start();
new A().start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程"+i+"正在执行 "+ Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}
class A extends Thread {
int num = 0;
@Override
public void run() {
while (true) {
System.out.println("A进程:" + Thread.currentThread().getName() + "正在进行中 " + (++num));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (num == 30) {
break;
}
}
}
}
class B implements Runnable{
int num = 0;
@Override
public void run() {
while (true) {
System.out.println("B进程:" + Thread.currentThread().getName() + "正在进行中 " + (++num));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (num == 30) {
break;
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IAo2Q0CG-1676111541466)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130223735711.png)]
我们可以在运行时通过Terminal的jconsole
查看Java进程监视器
设计模式:代理模式
因为start()方法在Thread里面,我们可以通过public Thread(Runnable target){} 构造器将实现Runnable接口的线程传入,再调用 Thread里的start()方法
Thread VS Runnable
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1lIeh5g-1676111541466)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221130225018999.png)]
所以更建议使用Runnable接口
线程常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSoAxeY6-1676111541466)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201101011672.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y5hAmLw8-1676111541466)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201122913620.png)]
线程中断
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hE8dc8En-1676111541466)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201114115138.png)]
线程插队
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IW6x1tXP-1676111541467)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201123740911.png)]
守护线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HndLO2Xm-1676111541467)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201150903893.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-traSsksg-1676111541467)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201150946892.png)]
线程生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f35FTGOm-1676111541467)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201152235552.png)]
线程同步机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EY4qhMp7-1676111541467)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201170457219.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9QaD6nal-1676111541468)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201201118749.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6F8Sinp-1676111541468)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201201223227.png)]
释放锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpHJeULu-1676111541468)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201204518540.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pOzIEh2p-1676111541468)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221201204706022.png)]
IO
创建文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQlq9Lbv-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204110236489.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cn0dmLdq-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204111547099.png)]
文件常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuDI47yy-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204112043746.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEgwUYoS-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204115125730.png)]
IO流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PWl4X27d-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204125936513.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JzjXzYA-1676111541469)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204130430097.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOUE400m-1676111541470)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209101309696.png)]
FileInputStream和FileOutputStream
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStream_ {
public static void main(String[] args) throws IOException{
String path = "C:\\Users\\god\\Desktop\\readme.txt";
//只是创建了文件对象
File file = new File(path);
//真正创建实际的文件
file.createNewFile();
int filedate ;
byte [] fileString = new byte[8];
FileInputStream fileInputStream = new FileInputStream(file);
//一个字节一个字节读
while ((filedate = fileInputStream.read()) != -1){
System.out.print((char)filedate);
}
//这里不能直接new String(fileString),
// 会默认调用new String(fileString,0,fileString.length)
//而fileInputStream的读取比如一次最多读取8个,第一次读取8个,第二次只读取5个
//但是此时fileString[5],fileString[6],fileString[7]没有更新,依旧是第一次读取的内容
//所以应该用new String(fileString,0,filedate),将每次读取的数量传入
//多字节读取
while ((filedate = fileInputStream.read(fileString)) != -1){
System.out.print(new String(fileString,0,filedate));
}
//一定要记得关闭文件流,释放资源
fileInputStream.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsxKWqX4-1676111541470)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204191255898.png)]
String path = "C:\\Users\\god\\Desktop\\readme.txt";
//只是创建了文件对象
File file = new File(path);
FileOutputStream fileOutputStream = new FileOutputStream(file,true);
String str = "l am hero man";
fileOutputStream.write(str.getBytes());
//一定要记得关闭文件流,释放资源
fileOutputStream.close();
FileReader和FileWriter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9dsWa9of-1676111541470)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204201020561.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPNUnTlu-1676111541470)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221204203604385.png)]
节点流和处理流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B13bRZO2-1676111541470)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205102700320.png)]
BufferedReader与BufferedWriter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wgShjhz-1676111541471)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205114353756.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTd6XCvQ-1676111541471)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205160055417.png)]
序列化与反序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06YfW75H-1676111541471)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205191007422.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DuFquLMA-1676111541471)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205214058143.png)]
ObjectOutputStream与ObjectInputStream
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcXScsIY-1676111541471)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205212340778.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7sPg9WE-1676111541472)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205212749415.png)]
标准输入输出流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UJL1MpMg-1676111541472)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205215202594.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYTzASeA-1676111541472)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221205215340982.png)]
转换流
将 字节流 ——》字符流(可以指定编码方式)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6M4Ql7V-1676111541472)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209101351335.png)]
InputStreamReader与OutputStreamReader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tALZzeoy-1676111541472)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209103520463.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfyFcGsd-1676111541473)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209105642699.png)]
网络编程
基础知识
- ip地址 = 网络地址 + 主机地址 ,比如:192.168.16.19
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3GDkctiO-1676111541473)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221207152132533.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D3D1F8s4-1676111541473)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221207164521315.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7DYUand-1676111541473)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221210104113573.png)]
InetAddress类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XGGLgjC-1676111541473)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221208090509446.png)]
Socket通信
//如果使用字符流,需要手动刷新,否则数据不会写入数据通道
bufferedWriter.flush();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5UQHgAx-1676111541474)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209221520792.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rKUjaJKL-1676111541474)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221208195523656.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0LCQqO5m-1676111541474)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221208223506430.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFcckovj-1676111541474)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209112321097.png)]
package JavaLearning;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 写一个简单的Socket本地连接服务器端
*/
public class SocketTCPServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1. 在本机监听端口9999
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务器,在9999端口监听,等待连接...");
//2. 当没有客户端连接9999端口时,程序会阻塞,等待连接
// 如果有客户端连接,则会返回Socket对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("连接成功!");
System.out.println("服务器端socket =" + socket.getClass());
//3.通过Socket.getInputStream()接受信息
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1)
{
System.out.println(new String(buf,0,readLen));
}
OutputStream outputStream = socket.getOutputStream();
outputStream.write(("l am Server".getBytes()));
//4. 设置结束写入标记
socket.shutdownOutput();
//5. 关闭流对象
inputStream.close();
socket.close();
serverSocket.close();
}
}
package JavaLearning;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 写一个简单的Socket本地连接客户端
*/
public class SocketTCPClient {
public static void main(String[] args) throws IOException, InterruptedException {
//1. 连接服务器ip + 端口
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端Socket = " + socket.getClass());
//2. 连接上,生成Socket,通过Socket.getOutputStream()传输信息到服务器
OutputStream outputStream = socket.getOutputStream();
outputStream.write(("l am client".getBytes()));
Thread.sleep(2000);
outputStream.write(("bye".getBytes()));
//3. 设置结束写入标记
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1)
{
System.out.println(new String(buf,0,readLen));
}
//4. 关闭流对象
outputStream.close();
socket.close();
}
}
网络上传文件
//如果使用字符流,需要手动刷新,否则数据不会写入数据通道
bufferedWriter.flush();
- 实例:客户端上传图片到服务器端
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lwqRqz0F-1676111541474)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209203650610.png)]
package JavaLearning;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 接受图片的服务器端
*/
public class TCPFileUploadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
byte[] str = StreamUtils.streamToByteArray(bufferedInputStream);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("服务器回复:已收到图片");
//如果使用字符流,需要手动刷新,否则数据不会写入数据通道
bufferedWriter.flush();
//4. 设置结束写入标记
socket.shutdownOutput();
String path = "C:\\Users\\god\\Desktop\\CopyTiger.jpg";
FileOutputStream fileOutputStream = new FileOutputStream(path);
fileOutputStream.write(str);
fileOutputStream.close();
bufferedInputStream.close();
bufferedWriter.close();
socket.close();
serverSocket.close();
}
}
package JavaLearning;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 发送图片的客户端
*/
public class TCPFileUploadClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
String path = "C:\\Users\\god\\Desktop\\Tiger.jpg";
OutputStream outputStream = socket.getOutputStream();
//发送图片
FileInputStream fileInputStream = new FileInputStream(path);
byte[] bytes = StreamUtils.streamToByteArray(fileInputStream); //图片转换成字符数组
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(bytes);
//4. 设置结束写入标记
socket.shutdownOutput();
//接收回复
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str;
while ( (str = bufferedReader.readLine()) != null){
System.out.println(str);
}
bufferedOutputStream.close();
fileInputStream.close();
socket.close();
}
}
package JavaLearning;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
*简单的工具包
*/
@SuppressWarnings("all")
public class StreamUtils {
/**
* 功能将输入流转成字符,即可以将文件中的内容读入到byte[]
* @param is
* @return
* @throws IOException
*/
public static byte[] streamToByteArray(InputStream is) throws IOException {
//创建输出流对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1){
byteArrayOutputStream.write(b , 0 ,len);
}
byte[] array = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return array;
}
}
netstat
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qSHUfbZt-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209204520963.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JdUCmzzI-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221209220251464.png)]
UDP网络通讯编程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CazqUY5x-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221210102911303.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1p5Rt28-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221210102528156.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X3PGjO1T-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221210150615253.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BK4gcpza-1676111541475)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221210150707924.png)]
正则表达式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-441V6GFJ-1676111541476)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221230145356940.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21qOhOPJ-1676111541476)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221230150300315.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFoFYu5e-1676111541476)(C:\Users\god\AppData\Roaming\Typora\typora-user-images\image-20221230152846922.png)]