栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA学习

基本概念
  • 一个源文件只能有一个public类,其他类的个数不限。编译后,每一个类都对应生成一个 .class 文件
  • 源文件名与public类同名
  • javadoc注释
  • 文档注释用终端生成html文件命令:
javadoc -d 文件夹地址 javadoc标签 XXX.java
  • Java程序中 "+" 号的使用
    • 当左右两边都是数值型时,做加法运算
    • 当左右两边有一方为字符串,则做拼接运算
System.out.println(100+98);		//198
System.out.println("100"+98);		//10098
System.out.println(100+"Hello");		//100Hello
System.out.println("Hello"+100+98);		//Hello10098
数据类型 引用类型
  • 类
  • 接口
  • 数组
浮点数
  • Java默认浮点数是double类型,float类型要在数值后加 f/F
  • Java浮点类型保留小数点后位数
    • float - 保留小数点后7位 - C语言中保留6位
    • double - 保留小数点后16位 - C语言中保留15位
		double a = 0.92381930109121314334452222222;	
		System.out.println(a);	//0.9238193010912131
		float b = 0.213123123123f;	
		System.out.println(b);	//0.21312313
        //一些小细节
        double a = 2.7;
		double b = 8.1 / 3;	//实际结果为2.7
		System.out.println(a);	//2.7
		System.out.println(b);	//2.6999999999999997
		if(a == b){
			System.out.println("相等");	
		}else{
			System.out.println("不相等");		//输出不相等
		}
		//由此我们可以得到一个重要结论
		//当对两个运算结果为小数的进行判断比较是,应当小心
		//应该用两个小数的差值的绝对值小于某个很小的范围时,认为两个运算结果相等
		//应按照如下写法
		if(Math.abs(a - b) < 0.0001){
			System.out.println("相等");		//输出相等
		}
  • 字符和编码时一一对应的
  • Java中不可以用0和非0来替代false和true,这点与C语言不同。
    • Boolean只能是true或者false 不能是0或者1
基本数据类型转换 自动类型转换
  • 低精度向高精度自动转换
  • byte==>short==>int==>long==>float==>double
  • char==>int==>long==>float==>double
//细节
int n1 = 10;
float d1 = n1 + 1.1;		//错误,右边其实结果是double类型
float d1 = n1 + 1.1F;		//正确写法
System.out.println(d1);

int n2 = 1.0; //错误	右边是double 高精度不能自动转换为低精度

//byte和short、char之间不发生自动类型转换
//byte和int自动类型转换的一些小细节
byte b1 = 10;			//可以,具体数复制给byte时,先判断数值范围,再判断类型	10在byte范围内:-128-127
int a = 1;
b1 = a;	//变量a在内存中占四个字节,是不能把他赋给byte,因为byte只有一个字节的空间
char c1 = 'a';
b1 = c1;	//错误,byte和char之间不能自动类型转换

//byte、short、char之间可以计算,在计算时首先转换为int类型

byte b2 = 1;
byte b3 = 1;
short s1 = 1;
short s2 = s1 + b2; // 错误,b2 + s1 在计算时转换为了int型
int b = s1 + b2; //正确
byte b4 = b2 + b3; //错误,b2 + b3 ==> int
//boolean 不参与类型的自动转换
//自动提升原则:表达式结果的类型自动提升为操作数中最大的类型
byte b5 = 1;
short s4 = 100;
int c = 1;
double num = 1.1;
float f1 = 1.1f;
double num1 = b5 + s4 + c + num; //必须用double类型的变量接收右边的运算结果,因为右边运算结果的类型以及提升到double类型
double num2 = b5 + s4 + c + f1; //这样可以,右边运算结果为float,满足自动转换原则
强制类型转换
  • 高精度类型向低精度类型转换,由编写代码的人使用强制转换符号**()**进行类型转换,同时转换过程中会造成精度损失
  1. C语言中直接通过赋值号就能进行类型转换,但是也会造成精度损失,编译可以通过,但是编译器会提示警告。C语言也可以用强制类型转换符 ( 进行有精度损失的强制类型转换)
  2. Java中高精度类型向低精度类型转换必须使用强制转换符号(),会造成精度损失,否则编译无法通过,直接提示错误。
  3. 强制类型转换符只对最近的操作数有效,这点与C语言相同,C语言中是因为**()**运算符优先级高。
int a = (int)10+ 2.1*2;	//错误,右边其实还是double型
//正确写法
int a = (int)(10+ 2.1*2);
  1. char类型可以保存 int 类型的常量值,但是不能保存 int 类型的变量值(需要强制转换)。这点与C语言不同
  2. C语言中** 只要被char保存的值在其范围内,就可以保存,无论变量值还是常量值**;这点原因还是因为C语言中直接通过赋值号就能进行类型转换
  3. byte进行运算时,当作int类型处理,运算结果用int接收
  4. Short进行运算时,当作int类型处理,运算结果用int接收
  5. char进行运算时,当作int类型处理,运算结果用int接收

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-881UZUUn-1652873405290)(https://cdn.jsdelivr.net/gh/MCXXHYK/PicGo/202205121717674.png#crop=0&crop=0&crop=1&crop=1&id=HDf9U&originHeight=896&originWidth=1356&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]

基本数据类型和字符串的转换

1.** 基本数据类型转字符串**:加 “”

int n1 = 100;
float f1 = 4.5F;
double d1 = 1.2;
boolean b1 = true;
String s1 = n1 + "";
String s2 = f1 + "";
String s3= d1 + "";
String s4 = b1 + "";
System.out.println(s1 + " " + s2 + " " + s3 + " " + s4);	//100 4.5 1.2 true    字符串
  1. **字符串转基本数据类型 **

    • 语法:通过基本数据类型的包装类调用parseXXX方法即可
int a =Integer.parseInt("123");
double b = Double.parseDouble("123");
float f = Float.parseFloat("123");
short s = Short.parseShort("12");
long l = Long.parseLong("2131");
boolean b = Boolean.parseBoolean("true");
byte by = Byte.parseByte("11");
//字符串==>取出字符
String s1 = "123";
char c1 = s1.charAt(0); //1
char c2 = s1.charAt(1); //2
char c3 = s1.charAt(2); //3
关系运算符 instanceof:检查是否是类的对象,返回值为true和false
boolean judge = "hh" instanceof String;	//true
短路与 && 、逻辑与 & 区别
  • 短路与有一个条件为假,后面条件直接不用判断,且后面代码不再执行,结果返回假;
  • 逻辑与必须要把所有条件都判断完,才会返回结果 效率低
短路或 ||、逻辑或 | 区别
  • 短路或有一个条件为真,后面条件直接不用判断,且后面代码不再执行,结果返回真;
  • 逻辑或必须要把所有条件都判断完,才会返回结果 效率低

tips:**复合赋值运算符再进行计算时会进行类型转换 **

byte b1 = 123;
b1 += 1;	//等价于 b1 = (byte)(b1 + 1); 
//分析:按理说 b1 += 1;右边是 b1 +1 ==>int 赋值给左边byte会发生错误,
																			//但是这里其实复合赋值运算符进行运算是会自动进行类型转换的
//b1++也是如此
//b1++等价于 b1 = (byte)(b1+1);	也会发生类型转换

//细节
int i = 1;
i = i++;		//1) temp = i ; 2) i = i +1; 3) i = temp;  执行顺序是这样的
System.out.println(i); //1
int j = 0;
j = i++;		//1) temp = i ; 2) i = i +1; 3) j = temp;  执行顺序是这样的	j=1

int a = 1;
a = ++a;
System.out.println(a); //2	执行顺序 1) a = a +1; 2) temp = a ; 3) a = temp;
键盘输入
import java.util.Scanner;	//导包

public class UserScanner {

	public static void main(String[] args){
		//1.创建对象
		Scanner sc = new Scanner(System.in);
    //2.调用方法
		String s1 = sc.next();		//字符串
		int n1 = sc.nextInt();		//整型值
		double d1 = sc.nextDouble();//浮点型输入
    char c1 = sc.next().charAt(0);	// 字符型输入
		System.out.println("==================");
		System.out.println(s1);
		System.out.println(n1);
		System.out.println(d1);
    System.out.println(c1);
		// System.out.println(s1);
	}
}
金字塔


public class Pyramid {

	void disPlayPyramid () {
		int totol = 5;
		//i控制层数
		for (int i = 1 ; i <= totol ; i++) {
			//每次打印star之前先打印空格
			for (int k = 1; k <= totol-i; k++){
				System.out.print(" ");
			}
			//j控制每一层打印的star个数
			for (int j = 1;j <= 2*i-1;j++){
				if(j == 1 || j == 2*i-1 || i == totol){
					System.out.print("*");						
				}else{
					System.out.print(" ");
				}
			}

			System.out.println("");
		}
	}
	public static void main(String[] args) {
		Pyramid py = new Pyramid();
		py.disPlayPyramid();
	}
}
原码、反码、补码 规则:
  • 最高位为符号位,0 ==> 正数 ,1 ==> 负数
  • 正数 ==> 原码 = 反码 =补码
  • 0的原码 = 反码 =补码 = 0
  • 负数的原码 符号位不变,其余按位取反 -----> 反码 加一----> 补码 减一------> 反码 取反 ------>得原码的绝对值
  • 补码转原码时,若其符号为 1 时,说明其是负数,所转的原码是该该负数的绝对值
  • 计算机运算(存储)时都是以补码形式运算的
  • 看运算结果时,要看的是其原码(运算结果是补码时,要转成原码才能打印出来)
位运算
  • **按位与&、按位或|、按位异或 ^、按位取反 ~ **
    • 按位与&:两个数全为1,结果为1 ;否则取0 ;
    • 按位或|:两个数全为0,结果为0 ;否则为1;
    • 按位异或 ^:两个数不同时为1(1 0);相同时为0;(0 0 、1 1);
    • 按位取反 ~:0 ==> 1 ; 1 ==> 0 ;

//2   0010    3		0011      2 & 3 = 0010     ==> 2
//-2  原码:10000000 00000000 00000000 00000010  
//		补码:11111111 11111111 11111111 11111110  
//		~-2	 00000000 00000000 00000000 00000001	这是补码,运算结果要转成原码
//   运算结果的原码:00000000 00000000 00000000 00000001
//		~-2 ==> 1
  • **算数右移 >> 、算数左移 << 、逻辑右移 >>> **
    • 算数右移 >>: 低位溢出,符号位不变,并用符号位补溢出的高位 a >> b ===>本质:a / 2/ 2/……/2 除以b个2
    • 算数左移 <<:符号位不变,低位补0 a << b ====>本质:a _ 2 _ 2 _ ……_2 乘以b个2
    • 逻辑右移 >>>:低位溢出,高位补0 ;逻辑右移 >>>也叫无符号右移
随机数生成
Double d1 = Math.random();	//  返回值时介于(0.00,1.00)的Double值
//随机生成1-100之间的随机整数
int n1 = (int)(Math.random()*100)+1	//(int)(Math.random()*100) 0-99
循环控制 break
  • break出现在多层循环中,可以通过指定循环标签,来终止哪一层循环,但是实际开发中尽量避免使用标签;break label1;
    • 如果break在多层循环中,没有指定标签,则break终止的是离他最近的一层循环
  • 字符串比较 String1.equal(String2);
    • 把String1写成被计较的值,可以避免控制异常
	//实现登录验证,有三次机会,如果用户名为”胡永康“,密码为”123456“,则提示登录成功;
	//否则提示登录失败,并显示还有几次机会
	void loginValidate(){
		int n = 3;
		Scanner sc = new Scanner(System.in);
		for( int i = 1; i <=n; i++){
			System.out.print("请输入用户名:");
			String uname = sc.next();
			System.out.println("");
			System.out.print("请输入密码:");
			String password = sc.next();
			if("胡永康".equals(uname) && "123456".equals(password)){	
				System.out.println("登录成功....");
				break;
			}else{	
				System.out.println("登录失败,还剩"+(n-i)+"次机会!");
			}
		}
	}
continue
  • continue出现在多层循环中,可以通过指定循环标签,来直接进行哪一层的下一次循环,但是实际开发中尽量避免使用标签;continue label1;
  • 如果continue在多层循环中,没有指定标签,则continue直接进行的是离他最近的一层循环的下一次循环
return
  • return 可以返回方法的返回值,当在方法中直接使用 **return;**时,则该方法直接结束,后面不再执行;
  • return 用在主方法main上时,表示程序直接终止
数组 数组长度(大小):arr.length 数组两种写法
//1.动态初始化
int[] a = new int[5];	//数组动态初始化
int a[] = new int[5];	//两种写法均可
//也可以先声明再初始化
int[] a;				//  先只声明,而不分配空间
a = new int[5];	//此时分配空间
//2.静态初始化
int a[] = {2,5,6,2,5,7,9};
数组使用注意事项
  • 数组创建后没有赋值,有默认值
int  byte  short  long ==> 0				float ==> 0.0			double ==> 0.0
char ==> /u0000		string ==> null boolean ==> false
  • 数组下标必须在指定范围内使用
  • 数组属于引用类型,数组型数据是对象
数组扩容(动态扩容) 基本思路
  • 创建新数组,
  • 使新数组的长度等于原来的数组加上要扩容的长度,
  • 把原来的数组内容拷贝过来,
  • 把新内容填入新的数组,
  • 并让原来的数组名指向新的数组。
//扩容arr数组
int[] arr = {1,2,3};
//1.创建新的数组
int[] arrNew = new int[arr.length + 1]; //给新的数组创建原来的数组长度加一的空间
//2.遍历将原来的数组内容依次拷贝过来
for( int i = 0; i < arr.length; i++){
  arrNew[i] = arr[i];
}
//3.把新内容填入新的数组
arrNew[arr.length] = 4;
//4.让原来的数组名指向新的数组
arr = arrNew; //arrNew[]自动被销毁(垃圾回收机制)
优化版本
import java.util.Scanner;
public class ArrayAdd {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		//原来的数组
		int[] arr = {1,2,3};
		do {
			//1.创建新数组
			int arrNew[] = new int[arr.length+1];
			//2.拷贝原来数组内容到新数组
			for( int i = 0; i < arr.length; i++) {
				arrNew[i] = arr[i];
			}
			//3.询问用户是否添加新内容到数组
			System.out.println("是否添加新内容到数组?(Y/N)");
			char input = sc.next().charAt(0);
			if( input == 'N') {
				break;	
			}else if ( input == 'Y') {
				System.out.println("请输入你想添加的数值:");
				//3.获取要添加的数值
				Integer addNum = sc.nextInt();
				arrNew[arr.length] = addNum;
				//4.将原来的的数组名指向新的数组
				arr = arrNew;
				System.out.println("添加成功!===========");
				//5.打印数组中当前数据
				for ( int i = 0; i < arr.length; i++ ) {
				System.out.print("arr["+i+"]==>"+arr[i]+"t");
				}
			}else{
				System.out.println("添加失败!");
				break; 
			}
		}while(true);
	}
}
数组缩减
import java.util.Scanner;
public class ArrayReduce {
	public static void main(String[] args) {
		//1.定义一个数组
		int[] arr = {1,2,3};

		//2.创建输入环境
		Scanner sc = new Scanner(System.in);
		//3.进入数组缩减
		do {
			//4.定义一个新的数组
			int[] arrNew = new int[arr.length - 1];
			System.out.println("是否要缩减数组?(y/n)");
			char input = sc.next().charAt(0);
			if( input == 'n'){
				break;
			}else if ( input == 'y' && arr.length > 1) {
				//5.拷贝前length-1个元素到新数组
				for ( int i = 0; i < arrNew.length; i++) {
					arrNew[i] = arr[i];
				}
				//6.原来数组指向新的数组
				arr = arrNew;
				//7.打印缩减后的数组
				System.out.println("缩减后的数组==================");
				for ( int j = 0; j < arr.length ; j++) {
					System.out.print(arr[j]+"t");
				}
			}else if ( arr.length <= 1 ){
				System.out.println("不能再缩减了!");
				break;
			}
		}while(true);
	}
}
排序 1.冒泡排序 算法思想:
//将其:57,12,63,7,4,9,12,52使用冒泡排序按照从小到大的顺序排序
 public class BubbleSort {
 	public static void main(String[] args){
 		//1.定义一个数组存放数据,静态初始化数组
 		int[] arr = {57,12,63,7,4,9,12,52};
 		//2.冒泡排序	arr.length -1 趟排序 每趟排序确定一个最大值 i ==> 趟数 ==> 已排好序的数个数
 		for (int i = 0 ; i < arr.length - 1 ; i++ ) {
 			int flag = 1;
 			if( arr.length == 0) {
 				System.out.println("排序失败,数组长度为0!");
 				break;
 			}
 			//3.一趟冒泡排序 只给前面未被排序的元素排序 后面 i 个元素已经排好序 <==> int j = 0; j < arr.length - 1 - i
 			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;
 					flag = -1;
 				}
 			}
 			//如果一趟排序之后flag没变化,说明数组已经有序,无需在进行下次排序,直接跳出循环
	 		if(flag == 1){
	 			break;
	 		}else{
	 			System.out.println("n第"+(i+1)+"趟排序后的数组===========");
	 			for ( int k = 0; k < arr.length; k++ ) {
	 			System.out.print(arr[k]+"t");
	 			}
 			}
 		}
 		//4.打印排序后的数组
 		System.out.println("n全部排序完成后的数组===========");
 		for ( int k = 0; k < arr.length; k++ ) {
 			System.out.print(arr[k]+"t");
 		}
 	}
}
总结:
  • 一共发生arr.length - 1 趟排序,
  • 每趟排序确定一个最大值,
  • 通过相邻元素比较进行交换,
  • 每趟排序发生 arr.length - 1 - i 次比较
二维数组 基本介绍
  • 每个元素是一个一维数组的数组
  • 如果要放二维数组的第 i 个元素中第 j 个值,应该这样访问: arr[i-1][j-1]; 因为数组下标都是从0开始的
public static void main(String[] args){
  int[][] arr = {{1, 2, 3, 4, 5}, {0, 0, 1, 2, 3}, {1, 2, 5, 3, 7}, {4, 5, 6, 2, 1}, {0, 0, 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("");
  }
}
二维数组的初始化 动态初始化01
public static void main(String[] args){
  // int[][] arr = new int[i][j];		i 代表这个数组初始化为含有 i 个一维数组的数组,j代表每个一维数组含有j个元素
  int[][] arr = new int[5][4]; //5 代表这个数组初始化为含有5个一维数组的数组,4代表每个一维数组含有4个元素
}
动态初始化02(列数不确定)
public static void main(String[] args){
  // int[][] arr = new int[i][];		i 代表这个数组初始化为含有 i 个一维数组的数组,但是每个一维数组还没有开辟空间
  int[][] arr = new int[3][]; //3 代表这个数组初始化为含有5个一维数组的数组,但是每个一维数组还没有开辟空间
 	for( int i = 0; i < arr.length; i++) {
    //给二维数组中的每一个一维数组开辟内存空间;如果不给每个一维数组分配空间,那么每个一维数组都为 null,即arr[i] == null
    arr[i] = new int[i + 1];
    //给一维数组遍历赋值
    for( int j = 0; j < arr[i].length; j++) {
      arr[i][j] = i+1;
    }
  } 
  System.out.println("====arr元素=====");
  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("");
  }
}
杨辉三角形
public class YangHui {
	public static void main(String[] args) {
		//动态创建一个二维数组
		int[][] arr = new int[10][];
		//遍历二维数组
		for( int i = 0; i < arr.length; i++) {
			//给每一个一维数组开辟内存空间
			arr[i] = new int[i + 1];
			//遍历一维数组
			for( int j = 0; j < arr[i].length; j++) {
				if( j == 0 || j == arr[i].length - 1) {
					arr[i][j] = 1;
				}else{
					arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
				}
			}
		}
		// 打印二维数组
		for( int i = 0; i < arr.length; i++) {
			for( int j = 0; j < arr[i].length; j++) {
				System.out.print(arr[i][j]+"t");
			}
			System.out.println("");
		}
	}
}
类与对象(基础) 基本介绍
  • 对象:属性 + 行为;是一个具体的实例。
  • 类:一种数据类型
  • 类的五大成员:属性、方法、构造器、代码块、内部类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1Hkpi4B-1652873405294)(https://raw.githubusercontent.com/MCXXHYK/PicGo/main/image-20220503200153147.png#crop=0&crop=0&crop=1&crop=1&id=EVclS&originHeight=1086&originWidth=2484&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHktv11U-1652873405297)(https://raw.githubusercontent.com/MCXXHYK/PicGo/main/image-20220503200311717.png#crop=0&crop=0&crop=1&crop=1&id=QwwBU&originHeight=1058&originWidth=2458&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]

  • Java内存分配机制:
    • 栈内存:一般存放基本数据类型(局部变量)
    • 堆内存:存放对象、数组等
    • 方法区:常量池(常量,例如字符串)、类加载信息
方法 细节和注意事项
  1. 一个方法只能有一个返回值,如何返回多个返回值?可用数组解决
//一个方法只能有一个返回值,如何返回多个返回值?可用数组解决
class AA {
	public int[] getSumAndSub(int num1, int num2) {
    	int[] resArr = new int[2];
    	resArr[0] = num1 + num2;
   		resArr[1] = num1 - num2;
    	//用数组返回多个返回值
    	return resArr;
  }
}
  1. 方法调用细节
    1. 同一个类中调用类中的方法,直接调用即可;
    2. 跨类调用方法时,需要创建该方法类的对象,用对象调用该方法。例如:A类中调用B类方法,需要先创建B类的对象,再用B类的对象调用B类的方法。
    3. 跨类的方法调用与方法的访问修饰符有关
  2. 成员方法传参细节
    1. 基本数据类型传参,是值拷贝,不可以通过形参修改实参
    2. 引用类型传参,传递的是地址,可以通过形参修改实参
递归

递归必须向退出递归的条件逼近,否则就会无限递归,造成栈溢出。

迷宫问题
public class MiGong {
    //初始化一个二维数组
    static int[][] initTwoDemensionArray( int i, int j) {
        //1.开辟二维数组的内存空间
        int[][] arr = new int[i][j];
        //2.初始化数组
		
        for( int k = 0; k < i; k++) {
            for( int t = 0; t < j; t++) {
                if(k > 0 && t >0 && k < i-1 && t < j-1) {
                    arr[k][t] = 0;
                }else{
                    arr[k][t] = 1;
                }
            }
            arr[3][1] = 1;
            arr[3][2] = 1;
        }
        return arr;
    }
    //打印二维数组
    static void printTwoDemensionArray(int[][] arr) {
        for( int i = 0; i < arr.length; i++) {
            for( int j = 0; j 
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
    }
    //寻找路径 策略:下 ==> 右 ==> 上 ==> 左
    boolean findWay(int[][] arr, int i, int j) {
        //判断是否到达终点	避坑:这里到达终点条件不要写成 i == 4 && j == 5 ,因为这样写的话arr[4][5]就不会插旗 
        if( arr[4][5] == 2) {
            return true;
        }else {
            //1.判断当前位置状态( 0, 1, 2, 3 )
            // 0代表此位置可以走,但是没有插旗,将其划定为路径
            if (arr[i][j] == 0) {
                //插旗 假定可以周围可以走通
                arr[i][j] = 2;
                //探路 下 ==> 右 ==> 上 ==> 左
                if (findWay(arr, i + 1, j)) {					//向下走
                    return true;
                } else if (findWay(arr, i, j + 1)) {	//向右走
                    return true;
                } else if (findWay(arr, i - 1, j)) {	//向上走
                    return true;
                } else if (findWay(arr, i, j - 1)) {	//向左走
                    return true;
                } else {
                    arr[i][j] = 3;
                    return false;
                }
            } else {    //当前位置为 1 / 2 / 3
                return false;
            }
        }
    }
    public static void main(String[] args) {

        //1.初始化迷宫
        int[][] arr = initTwoDemensionArray(6,7);
        //2.打印迷宫
        printTwoDemensionArray(arr);
        //3.从(1,1)出发找寻路径,终点为(4,5)
        MiGong m1 = new MiGong();
        m1.findWay(arr, 1, 1);
        //4.打印插好旗的迷宫 (迷宫路径)
        System.out.println("n====迷宫路径=====n");
        m1.printTwoDemensionArray(arr);
    }
汉诺塔问题
public class HanoiTower {
	public static void main(String[] args) {
		T t = new T();
		// System.out.println("=========一个盘子============");
		// t.move(1, 'A', 'B', 'C');
		// System.out.println("=========两个盘子============");
		// t.move(2, 'A', 'B', 'C');
		// System.out.println("=========三个盘子============");
		// t.move(3, 'A', 'B', 'C');
		System.out.println("=========5个盘子============");
		t.move(5, 'A', 'B', 'C');	
	}
}
class T {
	int count = 1;	//记录移动次数
	//汉诺塔移动	num ==> 盘子数 source ==> 从哪里移动	via ==> 经过		distinct ==> 到哪里
	public void move(int num, char source, char via, char distinct) {
		//如果塔上只有一个盘子,直接把盘子 source -> distinct
		if(num == 1) {
			System.out.println("从"+source+"->"+distinct+"t"+(count++));
		}else{	//塔上有多个盘子	看出两个盘子
			move(num - 1 , source, distinct, via);	//把source上面的 num - 1 个盘子先通过 distinct 移动到 via
			System.out.println("从"+source+"->"+distinct+"t"+(count++));	// 最后一个盘子直接 source -> distinct
			move(num - 1, via, source, distinct);	//把via上面的 num - 1 个盘子先通过 source 移动到 distinct
		}
	}
}
 
方法重载 
细节和注意事项 
  • 方法名必须相同
  • 形参列表必须不同,可以区分。参数顺序,类型,个数等等。(参数名无要求)
  • 返回类型无要求。(两个**仅是返回类型不同**的方法会造成重复定义,并不会构成重载)
public int sum(int n1, int n2) {
  	return n1 + n2;
}
public void sum(int n1, int n2) {  
  	int res = n1 + n2;
}
//上面两个方法重复定义,并不没有构成方法重载
可变参数
  • Java中允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
  • 语法:访问修饰符 返回类型 方法名(数据类型**…** 形参名){
    }
class HspMethod {
  //形参列表: 数据类型 + 三个点 + 参数名  nums可以当作数组来使用
  public int sum(int... nums) {
    int sum = 0;
    System.out.println("接收了"+nums.length+"个参数");
    for( int i = 0; i < nums.length; i++) {
      return sum += nums[i];
    }
  }
}
可变参数细节
  • 可变参数的实参可以为0个或任意多个
  • 可变参数的实参可以为数组
  • 可变参数的本质就是数组
  • 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  • —个形参列表中只能出现一个可变参数
作用域
  • 全局变量(属性)可以不赋值,直接使用,因为有默认值,
  • 局部变量必须赋值后,才能使用,因为没有默认值
作用域细节
  • 全局变量(属性)和局部变量可以重名,访问时遵循就近原则。
  • 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
  • 全局变量(属性)生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡
  • 局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中。
  • 作用域范围不同
  • 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
  • 局部变量:只能在本类中对应的方法中使用
  • 修饰符不同
    • 全局变量/属性可以加修饰符
    • 局部变量不可以加修饰符
构造器(构造方法)

用来创建对象时,就对对象进行初始化。

语法
 
注意事项 
  1. 构造器的修饰符可以默认,也可以是public protected private
  • 构造器没有返回值
  • 方法名 和类名字必须一样,严格相同,首字母都是大写
  • 参数列表 和 成员方法一样的规则
  • 构造器的调用,由系统完成
public class Constructor01 {
	public static void main(String[] args) {
		Person p = new Person(18, "hyk");
		System.out.println("名字:"+p.name+"t"+"年龄:"+p.age);
	}
}
class Person {
	int age;
	String name;

	public Person(int pAge, String pName){
		age = pAge;
		name = pName;
	}
}
构造器使用细节
  1. 一个类可以定义多个不同的构造器,即构造器重载
    比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
  2. 构造器名和类名要相同
  3. 构造器没有返回值
  4. 构造器是完成对象的初始化,井不是创建对象
  5. 在创建对象时,系统自动的调用该类的构造方法
  6. 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也
    叫默认构造器),比如 Dog(){}。 使用javap指令反编译看看
  7. 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无
    参构造器,除非_显式的定义一下_,即:Person(){}
//看一个案例
class Person{//类Person
  int age=90;
  String name;
  Person(String n,int a){//构造器
    name=n;//给属性赋值
    age=a; 
  }
}
public class Constructor {
	Person p=new Person("小倩",20);
}

this 关键字:代表当前对象
public Dog(String name, int age){//构造器
    //this.name 就是当前对象的属性name
    this.name = name:
    //this.age 就是当前对象的属性age
		this.age = age;
}

//哪个对象调用这个构造器,这个this就指的是哪个对象
使用细节
  1. this关键字可以用来访问本类的属性、方法、构造器
  2. this用于区分当前类的属性和局部变量
  3. 访向成员方法的语法:this.方法名(参数列表)
  4. 访问构造器语法:this(参数列表);注意只能在构造器中使用 (即只能在构造器中访问另外一个构造器)
public T() {	// 无参构造
  this("yk", 18);		//在构造器中访问另外一个构造器 且此语句需放置构造器的第一条
}
public T(String name, int age) {	// 无参构造
  System.out.println("有参构造");
}
  1. this不能在类定义的外部使用,只能在类定义的方法中使用
public class ThisExercise {

	public static void main(String[] args) {
		Person p1 = new Person(12,"张三");
		Person p2 = new Person(12,"张三");
		boolean res = p1.compareTo(p2);
		System.out.println(res);		//true
	}
}
class Person {

	//属性
	int age;
	String name;

	//有参构造
	public Person(int age, String name) {
		this.name = name;
		this.age = age;
	}

	//比较方法
	public boolean compareTo(Person p) {
		return this.name.equals(p.name) && this.age == p.age;
	}
}
包 作用
  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类「看Java API 文档]
  3. 控制访问范围
语法
**包基本语法**
package com.hspedu;	需放在每个.java文件第一行
说明:
1. package 关键字,表示打包。
2. com.hspedu:表示包名
  • java.lang.* //lang包是基本包,默认引入,不需要再引入
  • java.util.* //util 包,系统提供的工具包,工具类,使用 Scanner
  • java.net. * //网络包,网络开发
  • java.awt.* //是做java的界面开发,GUI
访问修饰符 基本介绍

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

  • 公开级别:用public 修饰,对外公开
  • 受保护级别:用protected修饰,对子类和同一个包中的类公开
  • 默认级别:没有修饰符号,向同一个包的类公开
  • 私有级别:用private修饰,只有类本身可以访问,不对外公开
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zp5fmpp9-1652873405298)(https://raw.githubusercontent.com/MCXXHYK/PicGo/main/image-20220505091456922.png#crop=0&crop=0&crop=1&crop=1&id=Ddnae&originHeight=270&originWidth=762&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)]
注意事项
  • 修饰符可以用来修饰类中的属性,成员方法以及类
  • 只有默认的和public才能修饰类,并且遵循上述访问权限的特点。
  • 因为没有学习继承,因此关于在子类中的访问权限,我们讲完子类后,再回头讲解
  • 成员方法的访问规则和属性完全一样
面向对象三大特征 封装 封装的实现步骤(三部曲)
  1. 将属性进行私有化private 【不能直接修改属性】
  2. 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){	//Xxx 表示某个属性
		//加入数据验证的业务逻辑
		属性=参数名;
}
  1. 提供一个公共的(public)get方法,用于获取属性的值
public 数据类型 getXxx(){ 		//权限判断,Xxx 某个属性
		return xx;
}
 

PS:有参构造器可以和set方法一起使用,用来控制权限

继承 基本介绍

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

语法
class 子类 extends 父类{
  
}
//1)子类就会自动拥有父类定义的属性和方法
//2)父类又叫超类,基类.
//3)子类又叫派生类.
细节
  1. 子类继承了所有的属性和方法,非私有属性可以直接访问但是私有属性和方法不能在子类直接访向,要通过父类所提供的公共的方法间接去访问
  2. 子类必须调用父类的构造器,完成父类的初始化(子类构造器中隐藏了super() )
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
  4. 如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过.
#【解读】:
#1.父类中有无参构造器时,子类中不管是有参构造器还是无参构造器都会隐藏一个super() ,默认调用父类的无参构造器。
#2.父类中没有无参构造器时,
#	 即父类中有参构造器将默认无参构造器覆盖掉时,
#	 且没有在父类中另外重新声明无参构造器,
#	 则在子类的构造器中必须用super去指定使用父类的哪个构造器
#	 完成对父类的初始化,否则编译无法通过。
#【另注】:初始化子类时,父类也会被初始化,且父类先被初始化
  1. 如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表),默认调用的是无参构造器
  2. super在使用时,需要放在构造器第一行
  3. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  4. java所有类都是Object类的子类,Object 是所有类的基类。
  5. 父类构造器的调用不限于直接父类!将一直往上追湖直到Object类(顶级父类),从上往下调用构造器
  6. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制
  7. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
继承本质
  1. 子类对象访问属性或调用方法时注意事项:
    • 首先看子类是否有该属性
    • 如果子类中有该属性,并且可以直接访问(没有private),则返回信息
    • 如果子类中没有该属性,就看其直接父类(上一层父类)有没有该属性,如果有,就停止查找,并且该属性可以直接访问(没有private),则返回信息(如果这里属性如果被私有化了,别的类的不能访问,则子类可以通过父类提供的公共get/set方法访问该属性)
    • 如果直接父类中还没有该属性,则再向上找,直到有该属性并且可以访问的父类(就近原则,此"近"是指距离子类的距离)
package com.test.extend_;

public class extend_test {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println(son.name);   //小头儿子
        System.out.println(son.age);    //30
        System.out.println(son.hobby);  //旅游
    }
}

class GrandPa {
    String name = "超大头爷爷";
    String hobby = "旅游";
}

class Father extends GrandPa {
    String name = "大头爸爸";
    int age = 30;
}

class Son extends Father {
    String name = "小头儿子";
}

//如果这里属性如果被私有化了,别的类的不能访问,则子类可以通过父类提供的公共get/set方法访问该属性。

        System.out.println(a.sum());    //40 -> 30
        
        System.out.println(a.sum1());   //30 -> 20
    }
}
class A {   //父类
    public int i = 10;

    public int sum() {
        return getI() + 10;
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i;
    }
}
class B extends A {//子类
    public int i = 20;

//    public int sum() {
//        return i + 20;
//    }

    public int getI() {
        return i;
    }

//    public int sum1() {
//        return i + 10;
//    }
}
多态的应用
  1. 多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
package com.poly.polyArray;

public class Poly_Array {
    public static void main(String[] args) {
        Person[] person = new Person[5];
       person[0] = new Person("yk", 18);
       person[1] = new Student("小明", 8, 59.5);
       person[2] = new Student("小刚", 9, 99.5);
       person[3] = new Teacher("smith", 30, 10000);
       person[4] = new Teacher("武忠祥",60,200000);
        for (int i = 0; i < person.length ; i++) {
            //如果数组中元素不是Student类或者其子类
            if( person[i] instanceof Student) {
                Student stu = (Student) person[i];
                stu.study();
                //等价于
                //((Student) person[i]).study();
            }else if (person[i] instanceof Teacher) {
                ((Teacher) person[i]).teach();
            }else if (person[i] instanceof Person) {

            }else{
                System.out.println("你的类型有误,请检查!");
            }
//            System.out.println(person[i].say());
        }
    }
}
  1. 多态参数:方法定义的形参类型为父类类型,实参允许为子类类型。
Object类详解
  • equals()方法
    • equals() 和 == 的对比:
      • ==:既可以判断基本类型,又可以判断引用类型
      • ==:如果判断基本类型,判断的是值是否相等。示例:int i=10;double d=10.0;
      • ==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
      • equals():是Object类中的方法,只能判断引用类型
      • equals():默认判断的是地址是否相等, 子类中往往重写该方法,用于判断内容是否相等。
package com.poly.equals;

public class Equals_Detail {
    public static void main(String[] args) {
        Equals_Test t1= new Equals_Test(18,"jack",20000);
        Equals_Test t2= new Equals_Test(18,"jack",20000);
        System.out.println(t1.equals(t2));;
    }
}
package com.poly.equals;

public class Equals_Test {
    private int age;
    private String name;
    private int id;

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

    public boolean equals(Object o) {
        if( o != null && o == this ) {
            return true;
        }else if(o instanceof Equals_Test) {
            Equals_Test e = (Equals_Test) o;
            return e.age == age && e.name.equals(name) && e.id == id;
        }
        return false;
    }
}

      
  • hashCode()方法:返回该对象的哈希码值
    • 提高具有哈希结构的容器的效率!
    • 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
    • 两个引用,如果指向的是不同对象,则哈希值是不一样的
    • 哈希值主要根据地址来的,但是不能完全将哈希值等价于地址。
      • 案例演示IHashCode_javal: obj。hashCode0[测试:A obj1= new A0; A obj2 = new A0: A
        obj3 = obj1]
    • 后面在集合,中hashCode 如果需要的话,也会重写
  • toString()方法
    • 基本介绍
      • 默认返回:全类名+@+哈希值的十六进制, 子类往往重写toString方法,用于返回对象的属性信息
        重写toString方法**,打印对象或拼接对象时,都会自动调用该对象的toString形式**.
      • 当直接输出一个对象时,toString 方法会被默认的调用,比如
        System.out.printin(monster);
        就会默认调用 monster.toString()

@Override
    public String toString() {
        return "Monster{" +
                "name='" + name + ''' +
                ", job='" + job + ''' +
                ", sal=" + sal +
                '}';
    }
  • finalize()方法
    • 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,
      做一些释放资源的操作
    • 什么时候被回收:当某个对象没有任何引用时,则 jvm就认为这个对象是一个垃
      圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。
    • 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc() 主动触发垃圾
      回收机制
  • 断点调试
    • F7(跳入)F8(跳过) shift+ F8(跳出) F9( resume,执行到下一个断点 )
    • F7**:跳入方法内**
    • F8:逐行执行代码.
    • shift+F8:跳出方法
房屋出租系统 项目需求:

实现基于文本界面的《房屋出租软件》。能够实现对房屋信息的添加、修改和删除(用数组实现),并能够打印房屋明细表。

类变量(静态变量) 定义:

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的
对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改
的也是同一个变量。

说明:
  • 类变量由关键字static修饰,其最大的特点就是会被类中所有对象共享
  • 类变量可以通过类名直接访问
  • 类变量在类加载时就生成了
类变量内存布局 定义类变量
【访问修饰符】 static 【数据类型】 变量名(推荐)
  或者
 static 【访问修饰符】【数据类型】 变量名
访问类变量
类名.变量名	(推荐)
  或者
对象名.变量名
使用细节
  • 什么时候需要用类变量
    当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态
    变量)
  • 类变量与实例变量(普通属性)区别
    类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
  • 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
    类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐
    我们使用 类名.类变量名方式访问。【前提是满足访问修饰符的访问权限和范围】
  • 实例变量不能通过 类名.类变量名 方式访问。
  • 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,
    就可以使用类变量了。
  • 类变量的生命周期是随类的加载开始,随着类消亡而销毁
类方法(静态方法) 形式
【访问修饰符】 static 【数据类型】 方法名 {}(推荐)
  或者
 static 【访问修饰符】【数据类型】 方法名	{}
类方法调用
类名.类方法名	(推荐)
  或者
对象名.类方法名
  【前提】:满足访问修饰符的访问权限
细节
  • 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数
  • 普通方法中隐含着this的参数
  • 类方法可以通过类名调用,也可以通过对象名调用。
  • 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调
    用
  • 类方法中**不允许使用和对象有关的关键字,比如this和super,**普通方法(成员方法)可以。
  • 类方法中只能访问静态变量或静态方法
  • **普通成员方法,既可以访问 非静态成员,也可以访问静态成员 **。
main方法 深入理解main方法
public static void main(String[] args) {
  
}
  • main方法时虛拟机调用
  • java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
  • java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
  • 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所
    运行的类的参数
  • java 执行的程序 参数1参数2 参数3
javac Hello.java
java Hello tom jack smith			#这里tom jack smith 就是传入String数组的参数,执行命令时传入
特别提示
  • 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。
  • 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能
    通过这个对象去访问类中的非静态成员
代码块 基本介绍
  • 代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句
    封装在方法体中,通过{ }包围起来。
  • 但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或
    类显式调用,而是加载类时,或创建对象时隐式调用
基本语法
[修饰符]{
	代码
}

说明注意:

  • 修饰符要么不写,要么用static
  • 代码块分为两类,使用static 修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。
  • 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
  • 结尾括号的分号可以写上,也可以省略。
  • 调用构造器时会先调用代码块中的内容
    • 使用场景:多个构造器中有相同的重复语句,可以将其抽取到代码块中。
    • 普通代码块可以理解为是对构造器的补充机制,可以做初始化操作
注意事项
  • static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且
    只会执行一次。如果是普通代码块,每创建一个对象,就执行。
  • 类什么时候被加载?
    • 创建对象实例时(new)
    • 创建子类对象实例,父类也会被加载;而且父类先被加载,子类后被加载
    • 使用类的静态成员时(静态属性,静态方法)
  • 普通的代码块,在创建对象实例时,才会被隐式的调用。被创建一次,就会调用一次。
  • 如果只是使用类的静态成员时,普通代码块并不会执行。
  • 创建一个对象时,在一个类 调用顺序是:(重点,难点)
    1. 调用静态代码块和静态属性初始化(注意:静态代码块和静态
      属性初始化调用的优先级一样,如果有多个静态代码块和多个静
      态变量初始化,则按他们定义的顺序调用)[举例说明]
    2. 调用普通代码块和普通属性的初始化(注意:普通代码块和普
      通属性初始化调用的优先级一样,如果有多个普通代码块和多
      个普通属性初始化,则按定义顺序调用)
    3. 调用构造方法。
  • 构造器的最前面其实隐含了 supe()和 调用普通代码块;
  • 静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
package com.ykstudy.code_block;

public class CodeBlock {
    public static void main(String[] args) {
        new BB();
        
    }
}

class AA {

    {
        System.out.println("AA的代码块");
    }

    public AA() {
        //(1)super()
        //(2)本类的普通代码块
        System.out.println("AA的构造器被调用");
    }
}

class BB extends AA {

    {
        System.out.println("BB的代码块");
    }

    public BB() {
        //(1)super()
        //(2)本类的普通代码块
        System.out.println("BB的构造器被调用");
    }
}
  • 我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
    1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    3. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    4. 父类的构造方法
    5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    6. 子类的构造方法
  • 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
class A02 { //父类
    private static int n1 = getVal01();
    static {
        System.out.println("A02的一个静态代码块..");//(2)
    }
    {
        System.out.println("A02的第一个普通代码块..");//(5)
    }
    public int n3 = getVal02();//普通属性的初始化
    public static int getVal01() {
        System.out.println("getVal01");//(1)
        return 10;
    }
 
    public int getVal02() {
        System.out.println("getVal02");//(6)
        return 10;
    }
 
    public A02() {//构造器
        //隐藏
        //super()
        //普通代码和普通属性的初始化......
        System.out.println("A02的构造器");//(7)
    }
}
 
class B02 extends A02 { //
 
    private static int n3 = getVal03();
 
    static {
        System.out.println("B02的一个静态代码块..");//(4)
    }
    public int n5 = getVal04();
    {
        System.out.println("B02的第一个普通代码块..");//(9)
    }
 
    public static int getVal03() {
        System.out.println("getVal03");//(3)
        return 10;
    }
 
    public int getVal04() {
        System.out.println("getVal04");//(8)
        return 10;
    }
    //一定要慢慢的去品..
    public B02() {//构造器
        //隐藏了
        //super()
        //普通代码块和普通属性的初始化...
        System.out.println("B02的构造器");//(10)
    }
}
 
public Class Code_Block_Detail {
  public static void main(String[] args) {
    new B02();//分析代码执行顺序
    
  }
}
设计模式 单例设计模式
  • 说明:从运行到结束的过程中,某个类只有一个实例存在
1. 饿汉式
  • 步骤
    1. 构造器私有化 => 防止直接 new
    2. 类的内部创建对象
    3. **向外暴露一个静态的公共方法。**例如:public void getInstance(){ }
2. 懒汉式
  • 步骤
    1. 构造器私有化 ==> 防止直接new
    2. 定义一个私有静态变量(不初始化)保存类的对象
    3. 提供一个公共静态方法返回类的对象
    4. 判断类中保存对象的静态变量有没有被初始化
package com.ykstudy.single_;


public class Single_ton01 {
    public static void main(String[] args) {
        //Girlfriend{name='小红'} 饿汉式
        System.out.println(Girlfriend01.getInstance());
        //Girlfriend02{name='美羊羊'} 懒汉式
        System.out.println(Girlfriend02.getInstance());

    }
}

//饿汉式
class Girlfriend01 {

    private String name;

    //2.在类的内部定义私有的静态变量保存新创建的对象
    private static Girlfriend01 gf = new Girlfriend01("小红");

    //1.构造器私有化
    private Girlfriend01(String name) {
        this.name = name;
    }

    //3.对外部提供一个公有的静态方法
    public static Girlfriend01 getInstance() {
        return gf;
    }

    @Override
    public String toString() {
        return "Girlfriend{" +
                "name='" + name + ''' +
                '}';
    }
}
//懒汉式
class Girlfriend02 {

    private String name;
    //2.定义一个私有静态变量(不初始化)保存类的对象
    private static Girlfriend02 gf;

    //1.构造器私有化
    public Girlfriend02(String name) {
        this.name = name;
    }
    //3.提供一个公共静态方法返回类的对象
    public static Girlfriend02 getInstance() {
        //4.判断类中保存对象的静态变量有没有被初始化
        if(gf == null) {
            gf = new Girlfriend02("美羊羊");
        }
        return gf;
    }

    @Override
    public String toString() {
        return "Girlfriend02{" +
                "name='" + name + ''' +
                '}';
    }
}
两种方式存在的问题(饿汉式 || 懒汉式)
  • 饿汉式的问题:在类加载时候就创建,可能存在资源浪费问题
  • 懒汉式的问题:线程安全问题,后面我们学了线程后,在进行完善。
final关键字

final 中文意思:最后的,最终的.

final 可以修饰类、属性、方法和局部变量.

在某些情况下,程序员可能有以下需求,就会使用到final:

  1. **当不希望类被继承时,**可以用final修饰.
  2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
  3. 当不希望类的的某个属性的值被修改,可以用final修饰.
  4. 当不希望某个局部变量被修改,可以使用final修饰
注意事项
  1. final修饰的属性又叫常量,一般用 XX_XX_XX来命名
  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
    • 定义时:如 public final double TAX RATE=0.08;
    • 在构造器中
    • 在代码块中
  3. 如果final修饰的属性是静态的,则初始化的位置只能是
    • 定义时
    • 在静态代码块
    • 但是不能在构造器中赋值。
  4. final类不能继承,但是可以实例化对象。
  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
  6. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
  7. final不能修饰构造方法(即构造器)
  8. final 和 static 往往搭配使用,效率更高,修饰基本数据类型时不会导致类加载.底层编译器做了优化处理。
class Demo{
public static final int i=16; //

static{
  System.out.printiln(”韩顺平教育~");
}
  1. 包装类(Integer,Double, Float,Boolean等都是final类),String也是final类。
抽象类(Abstract)

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,然后用abstract 来修饰该类就是抽象类。

//我们看看如何把Animal做成抽象类,并让子类Cat类实现。 
abstract class Animal{
    String name;
    int age;
		abstract public void cry(); //抽象方法没有方法体,写成抽象方法后,类也要加abstract修饰
}
细节和注意事项
  1. 抽象类不能被实例化
  2. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract 只能修饰类和方法,不能修饰属性和其它的。
  5. 抽象类可以拥有任意成员【抽象类的本质还是类】
  6. 抽象方法没有方法体,即不能在本类实现。
  7. 抽象类一旦被继承,其子类必须实现其所有抽象方法。除非其子类也被abstract修饰。
  8. 抽象方法不能使用private、 final 和 static来修饰,因为这些关键字都是和重写相违背的
模版设计模式——抽象类最佳实践
public class Template_Mode {

    public static void main(String[] args) {
        B b = new B(); 
        b.calculateTime();
    }
}

abstract class A {
    //定义一个抽象方法,子类重写
    public abstract void job();
		//把抽象方法塞到这个普通方法里面形成模板
    public void calculateTime() {
        //开始时间
        long start = System.currentTimeMillis();
        //实现任务
        job();  //动态绑定机制
        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("任务时间==" + (end - start));	//137
    }
}

class B extends A {
 	
    @Override
    public void job() {
        long num = 0;
        for (long i = 0; i < 100000000; i++) {
            num *= i;
        }
    }
}
接口
  • 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。

语法:

interface 接口名{
  
  //属性
  //方法(默认抽象方法;default修饰的方法;static修饰的方法)
  

  int num;
  //接口中方法默认为抽象方法
  public void cry();
  
  //jdk8以后接口中方法加关键词 static default修饰时方法可以有方法体
  static void smile() {
    System.out.println("haha");
  }
  
  default void play() {
    System.out.println("playing now");
  }
}

class 类名 implements 接口名{ 
	  
}

小结:

  1. 在Jdk7.0前接口里的所有方法都没有方法体。
    2.Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
注意事项
  1. 接口不能被实例化

  2. 接口中所有的方法是 public方法,接口中抽象方法,可以不用abstract 修饰

     void aa()
    
     实际上是 public abstract void aa();
    
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现。

  4. 抽象类实现接口,可以不用实现接口的方法。

  5. 一个类同时可以实现多个接口

  6. 接口中的属性,只能是final的,而且是 public static final 修饰符。比如:
    int a=1;实际上是 public static final int a=1;(必须初始化)
    接口中属性的访问形式:接口名.属性名

  7. 一个接口不能继承其它的类,但是可以继承多个别的接口

interface A extends B, C {

}

interface B {
  
}

interface C {
  
}
  1. 接口的修饰符只能是 public 和默认,这点和类的修饰符是一样的。
实现接口和继承类
  1. 实现接口是对Java单继承的一种补充
  2. 当子类继承了父类,就自动的拥有了父类的功能
  3. 如果子类需要扩展功能,可以通过实现接口的方式扩展
  4. 继承的价值:解决代码的复用性和可维护性
  5. 接口的价值:设计,设计好各种规范(方法),让其他类去实现这些方法
  6. 接口比继承更加灵活
    继承是is a的关系,而接口只需要满足like a的关系
  7. 接口在一定程度上实现代码的解耦【即:接口规范性 + 动态绑定】
    比喻:猴子继承爬树,可以实现鱼一样的游泳,可以实现小鸟一样飞翔
接口多态特性
  1. 接口多态参数:接口类型的变量可以指向实现了此接口的对象实例
public class A {
    public static void main(String[] args) {
        //接口类型的变量可以指向实现了此接口的对象实例 
        IF if =  new B();	
    }
}

Interface IF {
    
}
class B implements IF {

} 
  1. 接口多态数组
  2. 接口多态传递
public class InterfacePolyPass {

public static void main(String[] args) {

    //接口类型的变量可以指向,实现了该接口的类的对象实例
 	IG ig = new Teacher();
    //如果IG 继承了 IH 接口,而Teacher 类实现了IG接口 
    //那么,实际上就相当于 Teacher 类也实现了IH接口
	IH ih =  new Teacher();
	}
}
interface IH { }

interface IG extends IH{ }

class Teacher implements IG {
}
内部类 基本介绍

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class), 嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员.

  • 【思考】:类的五大成员是哪些?[属性,方法、构造器、代码块、内部类],
  • 内部类最大的特点就是可以直接访问私有属性,井且可以体现类与类之间的包含关系,
    • 注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.
基本语法
class Outer{ //外部类

class Inner{ //内部类

}


class Other{ //外部其他类

}
内部类的分类 1. 局部内部类

【说明】:局部内部类是定义在外部类的局部位置,比如方法中,代码块中,并且有类名。

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
  3. 作用域:仅仅在定义它的方法或代码块中。
  4. 局部内部类—访问 ==> 外部类的成员 [访问方式:直接访问]
  5. 外部类—访问===>局部内部类的成员
    访问方式:创建对象,再访问(注意:必须在作用域内)
  6. 外部其他类—不能访问====>局部内部类(因为局部内部类地位是一个局部变量)
  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

【演示】

//Outero2.this 本质就是外部类的对象,即哪个对象调用了m1, Outero2.this就是哪个对象
System.out printin("外部类的n2-”+外部类名.this.n2);
2. 匿名内部类(重要‼️)

【说明】:匿名内部类是定义在外部类的局部位置,比如方法中,井且没有类名
(1) 本质是类;
** (2)内部类**
** (3)没有名字(系统底层分配名字)**
** (4)同时还是一个对象!!!**

  • 匿名内部类的基本语法
new 类或接口(参数列表){

    类体

};

【细节】:

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
  3. 作用域:仅仅在定义它的方法或代码块中。
  4. 局部内部类—访问 ==> 外部类的成员 [访问方式:直接访问]
  5. 外部类—访问===>局部内部类的成员
    访问方式:创建对象,再访问(注意:必须在作用域内)
  6. 外部其他类—不能访问====>局部内部类(因为局部内部类地位是一个局部变量)
  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

【特点】:

  • 没有类名的局部内部类(一切特征都与局部内部类相同)
  • 必须继承一个父类或者实现一个接口
  • 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象
  • 优点:减少代码量
  • 缺点可读性较差
// 使用匿名内部类优化(相当于创建了一个局部内部类)
Usb usb = new Usb(){ // Usb为一个接口
  @Override
  public void service(){
    sout("连接电脑成功,fan开始工作")
  }
};
usb.service();
匿名内部类最佳实践
  • 匿名内部类用作实参直接传递
public class AnoymousInnerClass {

    public static void main(String[] args) {
        Cellphone cellphone = new Cellphone();
        
        cellphone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了!");
                System.out.println(this.getClass());    //class AnoymousInnerClass$1
            }
        });
        cellphone.alarmClock(new Bell() {

            @Override
            public void ring() {
                System.out.println("小伙伴下课了!");
            }
        });
    }
}


interface Bell {

    void ring();
}

class Cellphone {

    public void alarmClock(Bell bell) {
        bell.ring();
    }
}
3. 成员内部类

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

  1. 可以直接访问外部类的所有成员,包含私有的
class Outer01{ //外部类 private int n1 = 10;

public String name = "张三”;

    class Innter01{

        public void say(){

            System.out.println("Outer01 的n1 =" + n1 
                               + "outer01 的 name "+  name );

        }
    }
}
  1. 可以添加任意访问修饰符(public. protected、默认、Private),因为它的地位就是一个成员。
  2. 作用域:和外部类的其他成员一样,为整个类体

比如前面案例,**在外部类的成员方法中创建成员内部类对象,再调用方法. **

  1. 成**员内部类—访问---->外部类(比如:属性)[访问方式:直接访问] **
  2. 外部类—访问------>内部类(说明) 访问方式:创建对象,再访问
  3. 外部其他类—访问---->成员内部类
//外部其他类访问成员内部类两种方式
//1.第一种方法	outter.new Inner() 相当于把new Inner() 当作outter的成员
Outter.Inner inner = outter.new Inner();
//2.在外部类中编写一个方法可以返回 Inner 对象

class Outer{ //外部类 private int n1 = 10;

    public String name = "张三”;

    class Innter{

        public void say(){

            System.out.println("Outer01 的n1 =" + n1 
                               + "outer01 的 name "+  name );

        }
    }
    //第二种方式:在外部类中编写一个方法可以返回 Inner 对象
    public Inner getInner() {
        return new Inner();
    }
    
}

public class Test {
    public static void main(String[] args) {
        Outter outter = new Outter();
        //第一种方式
        Outter.Inner inner = outter.new Inner();
    }
}

  1. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
4. 静态内部类

【说明】:静态内部类是定义在外部类的成员位置,井且有static修饰

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
  2. 可以添加任意访问修饰符(public. protected、默认、private),因为它的地位就是一个成员。
  3. 作用域**:同其他的成员,为整个类体**
class Outer02{//外部类 private int n1 = 10;

    private static String name = “张三”;
    static class Inner02{

        public void say(){

        System.out.println(name);

        //不能直接访问外部类的非静态成员 
        //System.out.println(n1);
        }
    }

    public void show(){
    //外部类使用内部类 new Inner020.say();
    }
}
  1. 静态内部类—访问---->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
  2. 外部类—访问----->静态内部类访问方式:创建对象,再访问
  3. 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
枚举和注解 枚举

【说明】:

  1. 枚举对应英文(enumeration, 简写 enum)
  2. 枚举是一组常量的集合。
  3. 可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
自定义实现枚举
  1. 构造器私有化
  2. 本类内部创建一组对象
  3. 对外暴露对象(通过为对象添加public final static修饰符)
  4. 可以提供 get()方法,但是不要提供 set()方法
package com.yk.enum_;


public class Enumeration01 {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);
    }
}

class Season {

    private String name;
    private String weather;
    //2.本类创建一组公开常量对象 3.对外暴露对象(通过对象添加 public static final 修饰符)
    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("冬天","寒冷");

    //1.构造器私有化
    private Season(String name, String weather) {
        this.weather = weather;
        this.name = name;
    }
    //4.可以提供get()方法 但是不要提高set()方法
    public String getName() {
        return name;
    }

    public String getWeather() {
        return weather;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + ''' +
                ", weather='" + weather + ''' +
                '}';
    }
}
关键字 enum 实现枚举
  1. 使用关键字 enum 替代 class
  2. public static final Season SPRING = new Season(“春天”,“温暖”)直接使用 SPRING(“春天”,“温暖”)

解读:常量名(实参列表)

  1. 如果有多个常量(对象),使用** , **号间隔即可
  2. 使用enum实现枚举, 要求将定义常量对象写在最前面
package com.yk.enum_;


public class Enumeration01 {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);
    }
}
//1. class ==> enum
enum Season {
    //2.public static final Season SPRING = new Season("春天","温暖")
    //直接使用 SPRING("春天","温暖") 	枚举常量对象写在最前面
    SPRING("春天" ,"温暖") ,
    SUMMER("夏天","炎热"),
    AUTUMN("秋天","凉爽"),
    WINTER("冬天","寒冷");

    private String name;
    private String weather;


    //构造器私有化
    private Season(String name, String weather) {
        this.weather = weather;
        this.name = name;
    }
    //可以提供get()方法 但是不要提高set()方法
    public String getName() {
        return name;
    }

    public String getWeather() {
        return weather;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + ''' +
                ", weather='" + weather + ''' +
                '}';
    }
}

enum 实现枚举类注意事项
  1. 当我们使用enum 关键字实现一个枚举类时,默认会继承Enum类,并且此时该枚举类是一个final修饰的类[如何证明:使用javap 工具反编译]
  2. 传统的 public static final Season SPRING = new Season(“春天”,"温暖 ");简化成 SPRING("春天””温暖”),这里必须知道,它调用的是哪个构造器,通过实参可以看出来.
  3. 如果使用无参构造器创建枚举对象,则实参列表和 () 都可以省略
  4. 当有多个枚举对象时,使用,间隔,最后有一个分号结尾
  5. 枚举对象必须放在枚举类的行首
  6. 枚举类中构造器是私有的
  7. enum 实现的枚举类虽然不能继承其他类了(因为存在隐式继承,继承Enum类),但是可以实现接口
enum 常用方法

package com.yk.enum_;


public class Enumeration01 {
    public static void main(String[] args) {
        //toString: Season{name='春天', weather='温暖'}
        System.out.println(Season.SPRING);
        //values: 返回当前枚举类中所有的常量,返回的是 enum[] 类型
        Season[] seasons = Season.values();
        //增强for循环 从数组seasons中依次取出元素
        for (Season season : seasons) {
            System.out.println(season);
        }
        //name: 返回当前对象名(常量名),子类中不能重写
        System.out.println(Season.SPRING.name());   //SPRING
        //ordinal:返回当前对象的位置号,默认从0开始
        System.out.println(Season.SPRING.ordinal()); // 0
        //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!
        Season season1 = Season.valueOf("AUTUMN");
        System.out.println(season1);
        //compareTo:比较两个枚举常量,比较的就是编号!
        System.out.println(Season.SPRING.compareTo(Season.SUMMER)); //-1
    }
}

enum Season {


    SPRING("春天" ,"温暖") ,
    SUMMER("夏天","炎热"),
    AUTUMN("秋天","凉爽"),
    WINTER("冬天","寒冷");


    private String name;
    private String weather;


    //构造器私有化
    private Season(String name, String weather) {
        this.weather = weather;
        this.name = name;
    }
    //可以提供get()方法 但是不要提高set()方法
    public String getName() {
        return name;
    }

    public String getWeather() {
        return weather;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + ''' +
                ", weather='" + weather + ''' +
                '}';
    }
}
注解(Annotation)

【说明】:

  1. 注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、 类、方法、属性、构造器、局部变量等数据信息。
  2. 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
  3. 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。
1. @Override 使用说明
  • @Override 表示指定重写父类的方法(从编译层面验证),如果父类没有fly方法,则会报错
  • 如果不写@Override 注解,而父类仍有 public void fly(){},仍然构成重写
  • @Override 只能修饰方法,不能修饰其它类,包,属性等等
  • 查看@Override注解源码为 @Target(ElementType.METHOD),说明只能修饰方法
  • @Target 是修饰注解的注解,称为元注解
2. @Deprecated 使用说明
  • 表示某个元素(类或者方法)已过时
  • 过时并不代表不能用,只是不建议用

【说明】:

  1. 用于表示某个程序元素(类,方法等)己过时
  2. 可以修饰方法,类,字段,包,参数等等
  3. @Target(value={CONSTRUCTOR, FIELD, LOCAL VARIABLE, METHOD. PACKAGE, PARAMETER, TYPE})
  4. @Deprecated 的作用可以做到新旧版本的兼容和过渡
3. @SuppresWarnings :抑制警告 元注解
  1. **@Retention ** //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
  2. @Target // 指定注解可以在哪些地方使用
  3. @Documented //指定该注解是否会在javadoc体现
  4. **@Inherited **//子类会继承父类注解
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/915484.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号