day1

java基础编程

​ 对第一个java程序进行总结

  1. java程序编写-编译-运行的过程

    编写:我们将编写的java代码保存在以".java"结尾的源文件中

    编译:使用javac.exe命令编译我们的java源文件。格式:javac 源文件名.java

    运行:使用java.exe命令解释运行我们的字节码文件。 格式:java 类名

  2. 在一个java源文件中可以声明多个class。但是,只能最多有一个类声明为public的。

    而且要求声明为public的类的类名必须与源文件名相同。

  3. 程序的入口是main()方法。格式是固定的。

  4. 输出语句:

    System.out.println():先输出数据,然后换行

    System.out.print():只输出数据

  5. 每一行执行语句都以";"结束。

  6. 编译的过程:编译以后,会生成一个或多个字节码文件。字节码文件的文件名与java源文件中的类名相同。

public class Hello {
	public static void main(String[] args) {//public static void main(String a[]) {
	System.out.print("Hello World!");
	System.out.println();//换行
	System.out.println("Hello World!");
	}
}
class Person{

}
class Animal{

}

基本语法

关键字与保留字

定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)

特点:关键字中所有字母都为小写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7zQDELFU-1621514494137)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201126154834137.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1PQ9l7TI-1621514494139)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201126154851012.png)]

Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使

用。自己命名标识符时要避免使用这些保留字

goto 、const

标识符的使用

  1. 标识符:凡是自己可以起名字的地方都叫标识符。
    比如:类名、变量名、方法名、接口名、包名…

  2. 标识符的命名规则:–> 如果不遵守如下的规则,编译不通过!需要大家严格遵守

  • 由26个英文字母大小写,0-9 ,_或 $ 组成
  • 数字不可以开头。不可以使用关键字和保留字,但能包含关键字和保留字。
  • Java中严格区分大小写,长度无限制。
  • 标识符不能包含空格。
  1. Java中的名称命名规范: —>如果不遵守如下的规范,编译可以通过!建议大家遵守

    包名:多单词组成时所有字母都小写:xxxyyyzzz
    类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
    变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
    常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ

  2. 注意1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
    注意2:java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。

class IdentifierTest{
	public static void main(String[] args){
		//int myNumber = 1001;
		//System.out.println(myNumber);
		int mynumber = 1002;
		System.out.println(mynumber);
		//int 学号 = 1003;
		//System.out.println(学号);
	}
}

变量

一、变量按照数据类型来分:

基本数据类型:
整型:byte \ short \ int \ long
浮点型:float \ double
字符型:char
布尔型:boolean

引用数据类型:
类(class)
接口(interface)
数组(array)

基本数据类型
class VariableTest1 {
	public static void main(String[] args) {
		//1. 整型:byte(1字节=8bit) \ short(2字节) \ int(4字节) \ long(8字节)
		//① byte范围:-128 ~ 127
		byte b1 = 128;
		byte b2 = -128;
		//b2 = 128;//编译不通过
		System.out.println(b1);
		System.out.println(b2);
		// ② 声明long型变量,必须以"l"或"L"结尾
		// ③ 通常,定义整型变量时,使用int型。
		short s1 = 128;
		int i1 = 1234;
		long l1 = 3414234324L;
		System.out.println(l1);

		//2. 浮点型:float(4字节) \ double(8字节)
		//① 浮点型,表示带小数点的数值
		//② float表示数值的范围比long还大

		double d1 = 123.3;
		System.out.println(d1 + 1); //124.3
		//③ 定义float类型变量时,变量要以"f"或"F"结尾
		float f1 = 12.3F;
		System.out.println(f1);  //12.3
		//④ 通常,定义浮点型变量时,使用double型。

		//3. 字符型:char (1字符=2字节)
		//① 定义char型变量,通常使用一对'',内部只能写一个字符
		char c1 = 'a';
		//编译不通过
		//c1 = 'AB';
		System.out.println(c1);

		char c2 = '1';
		char c3 = '中';
		char c4 = 'ス';
		System.out.println(c2);
		System.out.println(c3);
		System.out.println(c4);

		//② 表示方式:1.声明一个字符 2.转义字符 3.直接使用 Unicode 值来表示字符型常量
		char c5 = '\n';//换行符
		c5 = '\t';//制表符
		System.out.print("hello" + c5);
		System.out.println("world");

		char c6 = '\u0043';
		System.out.println(c6);

		//4.布尔型:boolean
		//① 只能取两个值之一:true 、 false
		//② 常常在条件判断、循环结构中使用
		boolean bb1 = true;
		System.out.println(bb1);

		boolean isMarried = true;
		if(isMarried){
			System.out.println("你就不能参加单身party了!很遗憾");
		}else{
			System.out.println("你可以多谈谈女朋友!");
		}

	}
}

数据类型变量间的转换

基本数据类型之间的运算规则:

前提:这里讨论只是7种基本数据类型变量间的运算。不包含boolean类型的。

  1. 自动类型提升
    结论:当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。
    byte 、char 、short --> int --> long --> float --> double

    特别的:当byte、char、short三种类型的变量做运算时,结果为int型

  2. 强制类型转换:见VariableTest3.java

说明:此时的容量大小指的是,表示数的范围的大和小。比如:float容量要大于long的容量

class VariableTest2 {
	public static void main(String[] args) {
		byte b1 = 2;
		int i1 = 129;
		//编译不通过
		//byte b2 = b1 + i1;
		int i2 = b1 + i1;
		long l1 = b1 + i1;
		System.out.println(i2);

		float f = b1 + i1;
		System.out.println(f);

		short s1 = 123;
		double d1 = s1;
		System.out.println(d1);//123.0

		//***************特别地*********************
		char c1 = 'a';//97
		int i3 = 10;
		int i4 = c1 + i3;
		System.out.println(i4);

		short s2 = 10;
		//char c2  = c1 + s2;//编译不通过

		byte b2 = 10;
		//char c3 = c1 + b2;//编译不通过
		//short s3 = b2 + s2;//编译不通过
		//short s4 = b1 + b2;//编译不通过
	}
}

class VariableTest3 {
	public static void main(String[] args) {
		double d1 = 12.9;
		//精度损失举例1
		int i1 = (int)d1;//截断操作
		System.out.println(i1);
		
		//没有精度损失
		long l1 = 123;
		short s2 = (short)l1;
		
		//精度损失举例2
		int i2 = 128;
		byte b = (byte)i2;
		System.out.println(b);//-128
	}
}
class VariableTest4 {
	public static void main(String[] args) {
		
		long l = 123213;         //长整型 不加L或l 默认数据类型为int
		System.out.println(l);
		//编译失败:过大的整数
		//long l1 = 21332423235234123;
		long l1 = 21332423235234123L;

		//****************
		//编译失败
		//float f1 = 12.3;    //单精度的数据不加F或f 默认数据类型为double型数据
		float f1 = (float)12.3;
		//2.编码情况2:
		//整型常量,默认类型为int型
		//浮点型常量,默认类型为double型
		byte b = 12;
		//byte b1 = b + 1;//编译失败
		//float f1 = b + 12.3;//编译失败
	}
}
基本数据类型与String间转换

String类型变量的使用

  1. String属于引用数据类型,翻译为:字符串
  2. 声明String类型变量时,使用一对""
  3. String可以和8种基本数据类型变量做运算,且运算只能是连接运算:+
  4. 运算的结果仍然是String类型
class StringTest{
	public static void main(String[] args) {
		String s1="a";
		String s2="";    //字符串可以声明一个空,字符不行
		//char c1='';

	    int i=102;
		char c='a';
		String s3="hello";
		System.out.println(i+c+s3); // 199hello
		System.out.println(i+s3+c); // 102helloa
        
		//练习
		//*	*(字符在内存是以ASCII码存在)
		System.out.println("*	*");            //*   *
		System.out.println('*' + '\t' + '*');   //93  做加法运算
		System.out.println('*' + "\t" + '*');   //*   *
		System.out.println('*' + '\t' + "*");   //51*
		System.out.println('*' + ('\t' + "*")); //*   *

		String s4=3.5F+"";
		System.out.println(s4); //3.5
        
        String s1="123";
        int i=int(s1);  //编译错误
	}
}

运算符

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。

  • ​ 算术运算符 赋值运算符 比较运算符(关系运算符) 逻辑运算符 位运算符 三元运算符


一:算术运算符

加(+)减(-)乘(*)除(/) (前)++ (后)++ (前)-- (后)-- 连接(+)

class AriTest {
	public static void main(String[] args) {
		int num = 12;
		int num2 = 5;
		int result1 = num / num2;
		System.out.println(result1); //2

		double result2 = num / num2;     //2.0
		double result3 = (double)num / num2; //2.4
		  //double result5 = num / double(num2);  编译不成功
		System.out.println(result3);
        
		// %:取余运算
		//结果的符号与被模数的符号相同
		//开发中,经常使用%来判断能否被除尽的情况。
		int m1 = 10;
		int n1 = 3;
		System.out.println("m1 % n1 = " + m1 % n1);

		int a1 = 10;
		int b1 = ++a1;  //先运算后赋值
		System.out.println("a1 = " + a1 +",b1 = " + b1);

		int a2 = 10;
		int b2 = a2++;  //先赋值后运算
		System.out.println("a2 = " + a2 + ",b2 =" + b2);

		short s1 = 10;
		//short s1 = s1 + 1;  编译不成功
		s1++;    //自增1不会改变本身变量的数据类型
		System.out.println(s1);
	}
}
二:赋值运算符

= += -= *= /= %=

class SetValueTest {
	public static void main(String[] args) {
		int i = 5, j = 7;  //连续赋值

		int num1 = 5;
		num1 %= 2; //等价于num1 = num1 % 2;
		System.out.println(num1); //输出1

		short s1 = 10;
		//s1 = s1 + 2;//编译失败
		s1 += 2;//结论:不会改变变量本身的数据类型
		System.out.println(s1);

		//开发中,如果希望变量实现+2的操作,有几种方法?(前提:int num = 10;)
		//方式一:num = num + 2;
		//方式二:num += 2; (推荐)
		
		//开发中,如果希望变量实现+1的操作,有几种方法?(前提:int num = 10;)
		//方式一:num = num + 1;
		//方式二:num += 1; 
		//方式三:num++; (推荐)

		//练习1
		int a = 1;
		a *= 0.1; 
		System.out.println(a);  //输出0
		a++;
		System.out.println(a);  //输出1
		//练习2
		int m = 2;
		int n = 3;
		n *= m++; //等价与 n = n * m++;
		System.out.println("m = "+ m);  //输出3
		System.out.println("n = "+ n);  //输出6
		//练习3
		int n1 = 10;
		n1 += (n1++) + (++n1);//等价与 n1 = n1 + (n1++) + (++n1);
		System.out.println("n1 = " + n1);  //输出32
	}
}

三:比较运算符

== != > < >= <= instanceof

结论:
1.比较运算符的结果是boolean类型
2.区分 == 和 =

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

		int a = 10;int b = 20;
		System.out.println(a == b); //false
		System.out.println(a = b);  //20

		boolean m = true;
		boolean n = false;
		System.out.println(m == n); //false
		System.out.println(n = m);  //true
	}
}
四: 逻辑运算符

& 逻辑与 | 逻辑或 ! 逻辑非 && 短路与 || 短路或 ^ 逻辑异或

说明:逻辑运算符操作的都是boolean类型的变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WRWUv3M5-1621514494140)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201126235126549.png)]

区分& 与 &&
相同点1:& 与 && 的运算结果相同
相同点2:当符号左边是true时,二者都会执行符号右边的运算
不同点:当符号左边是false时,&继续执行符号右边的运算。&&不再执行符号右边的运算。
开发中,推荐使用&&

		boolean a = true;
		a = false;
		int num = 10;

		if( a & (num++) > 0)
			System.out.println("明天好好学习");
		else
			System.out.println("明天打机");
		System.out.println("num = " + num); //输出11 明天打机

		boolean b = true;
		int num2 = 10;
		b = false;
		if(b && (num2++ >0 ))
			System.out.println("明天好好学习");
		else
			System.out.println("明天打机");
		System.out.println("num2 = " + num2);//输出10 明天打机

区分:| 与 ||
相同点1:| 与 || 的运算结果相同
相同点2:当符号左边是false时,二者都会执行符号右边的运算
不同点3:当符号左边是true时,|继续执行符号右边的运算,而||不再执行符号右边的运算
开发中,推荐使用||

		boolean b3 = false;
		b3 = true;
		int num3 = 10;
		if(b3 | (num3++ > 0)){
			System.out.println("明天好好学习");
		}else{
			System.out.println("明天打机");
		}
		System.out.println("num3 = " + num3); //11 明天好好学习

		boolean b4 = false;
		b4 = true;
		int num4 = 10;
		if(b4 || (num4++ > 0)){
			System.out.println("明天好好学习");
		}else{
			System.out.println("明天打机");
		}
		System.out.println("num4 = " + num4); //10 明天好好学习
五: 位运算符

结论:

位运算符操作的都是整型的数据

<<(左移) >>(右移) >>>(无符号右移 ) & 与运算 | 或运算 ^ 异或运算

  1. << :在一定范围内,每向左移1位,相当于 * 2

  2. 在一定范围内,每向右移1位,相当于 / 2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8jXTLzY-1621514494142)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201127002556609.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYtoLhey-1621514494143)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201203151109834.png)]

		//面试题:最高效方式的计算2 * 8 ?  2 << 3  或 8 << 1
		int i = 21;
		System .out.println(" i << 2 = " + (i << 2) );; //84
		//加号的运算级别 比 左移位运算符 高 i<<2要加括号,不然得到的结果就是字符串的左移

		int i1 = 12;
		int i2 = 5;
		System.out.println("i1 & i2 =" + (i1 & i2));   //4
		System.out.println("i1 | i2 = " + (i1 | i2));  //13
		System.out.println("i1 ^ i2 = " + (i1 ^ i2));  //9

//练习:交换两个变量的值
		int num1 = 10;
		int num2 = 20;
		System.out.println("num1 = " + num1 + ",num2 = " + num2);

		//方式一:定义临时变量的方式
		//推荐的方式
		int temp = num1;
		num1 = num2;
		num2 = temp;

		//方式二:好处:不用定义临时变量  
		//弊端:① 相加操作可能超出存储范围 ② 有局限性:只能适用于数值类型
		//num1 = num1 + num2;
		//num2 = num1 - num2;
		//num1 = num1 - num2;

		//方式三:使用位运算符
		//有局限性:只能适用于数值类型
		//num1 = num1 ^ num2;
		//num2 = num1 ^ num2;
		//num1 = num1 ^ num2;

六:三元运算符

结构:(条件表达式)? 表达式1 : 表达式2

根据条件表达式真或假,决定执行表达式1,还是表达式2.

  • 如果表达式为true,则执行表达式1。

  • 如果表达式为false,则执行表达式2。

    /*如果程序既可以使用三元运算符,又可以使用if-else结构,
    	那么优先选择三元运算符。原因:简洁、执行效率高。*/
    		//比较三个数的最大值
    		int i = 50;
    		int j = 3;
    		int k = -40;
    		int max1 = (i > j) ? i :j;
    		int max2 = (max1 > k) ? max1 :k;
    		System.out.println("最大值 = " + max2);
    

流程控制

顺序结构:程序从上到下逐行地执行

分支结构:根据条件,选择性地执行某段代码。有if…else和switch-case两种分支语句。

循环结构:根据循环条件,重复性的执行某段代码。有while、do…while、for三种循环语句。

​ JDK1.5提供了foreach循环,方便的遍历集合、数组元素。

如何从键盘获取不同类型的变量:需要使用Scanner类

/*
具体实现步骤:
1.导包:import java.util.Scanner;
2.Scanner的实例化:Scanner scan = new Scanner(System.in);
3.调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量*/

//导包
import java.util.Scanner;
class ScannerTest{
	public static void main(String[] args) {
		//Scanner的实例化
		Scanner scan = new Scanner (System.in);
		//调用Scanner类的相关方法

		System.out.println("请输入你的名字:");
		String name = scan.next();
		
		System.out.println("请输入你的年龄:");
		int age = scan.nextInt();

		System.out.println("请输入你的体重:");
		double weight = scan.nextDouble();

		System.out.println("你是否相中了我 (true/false)");
		boolean isLove = scan.nextBoolean();

        //对于char型的获取,Scanner没有提供相关的方法。只能获取一个字符串
		System.out.println("请输入你的性别(男/女)");
		String gentle = scan.next();    
		char gentleChar = gentle.charAt(0);//获取索引为0位置上的字符

System.out.println(name + " " + age + " " + weight + " " + isLove +" " + gentleChar);
	}
}

分支结构:if-else

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KIxi97pk-1621514494145)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201208165652020.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ULkjhd6y-1621514494146)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201208165728939.png)]

/*
编写程序:由键盘输入三个整数分别存入变量num1、num2、num3,
对它们进行排序(使用 if-else if-else),并且从小到大输出。*/
			/*if(num1 >= num2){
				if(num3 >= num1)
					System.out.println(num2 + " "+ num1 + "" + num3 );
				else if (num3 <= num2)
					System.out.println(num3 + " " + num2 + " " +num1  );
				else 
					System.out.println(num2 + " "+ num3 + " " + num1 );
				
			}else {
				if(num1 >= num3)
					System.out.println(num3 + " "+ num1 + " " + num2 );
				else if (num2 <= num3)
					System.out.println(num1 + " " + num2+ " " +num3  );
				else 
					System.out.println(num1 + " "+ num3 + " " + num2 );
				}*/
     //方法二
                if (num1 > num2){
                    int num4 = num1;
                    num1 =num2;
                    num2 = num4;
                }
                if (num1 > num3){
                    int num4 = num1;
                    num1 = num3;
                    num3 = num4;
                }
                if(num2 > num3){
                    int num4 = num2;
                    num2 = num3;
                    num3 = num4;
                }
                System.out.println(num1 + " " + num2 + " " + num3 );

如何获取一个随机数:10 - 99

		int value = (int)(Math.random() * 90 + 10);// [0.0,1.0) --> [0.0,90.0) --->[10.0, 100.0) -->[10,99]
		System.out.println(value);
		//公式:[a,b]  :  (int)(Math.random() * (b - a + 1) )+ a
分支结构:switch-case

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YJZVfUkA-1621514494147)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20201208170306880.png)]

说明:

  1. break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构
  2. switch结构中的表达式,只能是如下的6种数据类型之一:
    byte 、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增)
  3. case 之后只能声明常量。不能声明范围。
  4. break关键字是可选的。
  5. default:相当于if-else结构中的else. default结构是可选的,而且位置是灵活的
//小写a,b,c,d,e转换为A,B,C,D,E		
		System.out.println("请输入你要转换的字符");
		String word = scan.next();
		char ch = word.charAt(0);
			switch (ch){
			case 'a':
				System.out.println("a的大写为"+'A');
				break;
			case 'b':
				System.out.println("b的大写为"+'B');
				break;
			case 'c':
				System.out.println("c的大写为"+'C');
				break;
			case 'd':
				System.out.println("d的大写为"+'D');
				break;
			case 'e':
				System.out.println("e的大写为"+'E');
				break;
			default:
				System.out.println("other");
		}
//对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”
			switch(score/60){
				case 0:
					System.out.println("成绩不及格");
					break;
				case 1:
					System.out.println("成绩合格");
					break;
				default:
					System.out.println("输入有误");
//编写程序:从键盘上输入2019年的“month”和“day”,要求通过程序 输出输入的日期为2019年的第几天。
//1/3/5/7/8/10/12月都是31天,2月28天,闰年2月29天,其他月份30天,
             System.out.println("请输入2019年的month");
			int month = scan.nextInt();
			System.out.println("请输入2019的day");
			int day = scan.nextInt();
			int sumdays=0;

			switch(month){
				case 12:
					sumdays += 30;
				case 11:
					sumdays += 31;
				case 10:
					sumdays += 30;
				case 9:
					sumdays += 31;
				case 8:
					sumdays += 31;
				case 7:
					sumdays += 30;
				case 6:
					sumdays += 31;
				case 5:
					sumdays += 30;
				case 4:
					sumdays += 31;
				case 3:
					sumdays += 29;
				case 2:
					sumdays += 31;
				case 1:
					sumdays += day;
					System.out.println("输入的日期为2019年的第"+ sumdays+"天");
			}
循环结构

一、循环结构的4个要素
① 初始化条件
② 循环条件 —>是boolean类型
③ 循环体
④ 迭代条件

for循环的结构

for(①;②;④){

}

执行过程:① - ② - ③ - ④ - ② - ③ - ④ - … - ②

while循环的结构

while(②){
③;
④;
}
执行过程:① - ② - ③ - ④ - ② - ③ - ④ - … - ②

说明:
1.写while循环千万小心不要丢了迭代条件。一旦丢了,就可能导致死循环!
2.我们写程序,要避免出现死循环。
3.for循环和while循环是可以相互转换的!
区别:for循环和while循环的初始化条件部分的作用范围不同。

do-while循环结构:

do{
③;
④;

}while(②);

执行过程:① - ③ - ④ - ② - ③ - ④ - … - ②

说明:
1.do-while循环至少会执行一次循环体!
2.开发中,使用for和while更多一些。较少使用do-while

不在循环条件部分限制次数的结构:for(;😉 或 while(true)**

结束循环有几种方式?
方式一:循环条件部分返回false
方式二:在循环体中,执行break

break和continue关键字的使用

使用范围循环中使用的作用(不同点)相同点
breakswitch-case ,循环结构中结束当前循环关键字后面不能声明执行语句
continue循环结构中结束当次循环关键字后面不能声明执行语句
/* 嵌套循环说明
① 内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
② 假设外层循环需要执行m次,内层循环需要执行n次。此时内层循环的循环体一共执行了m * n次

4. 技巧:外层循环控制行数,内层循环控制列数*/

		/*			i(行号)		j(*的个数)   规律:i + j = 5 换句话说:j = 5 - i;
		****		1			4
		***			2			3
		**			3			2
		*			4			1
		*/	

		for(int i = 1;i <= 4;i++){
			for(int j = 1;j <= 5 - i;j++){
				System.out.print("*");	
			}
			System.out.println();
		}

		//获取最大公约数
		//1.获取两个数中的较小值
		int min = (m <= n)? m : n;
		//2.遍历
		for(int i = min;i >= 1 ;i--)
			if(m % i == 0 && n % i == 0)
				System.out.println("最大公约数为:" + i);
				break;//一旦在循环中执行到break,就跳出循环
					//获取最小公倍数
                
		//1.获取两个数中的较大值
		int max = (m >= n)? m : n;
		//2.遍历
		for(int i = max;i <= m * n;i++)
			if(i % m == 0 && i % n == 0)
				System.out.println("最小公倍数:" + i);
				break;
/*
100000以内的所有质数的输出。实现方式一
质数:素数,只能被1和它本身整除的自然数。-->从2开始,到这个数-1结束为止,都不能被这个数本身整除。
*/
class PrimeNumberTest1 {
	public static void main(String[] args) {
		
		boolean isFlag = true;//标识i是否被j除尽,一旦除尽,修改其值
		int count = 0;//记录质数的个数

		//获取当前时间距离1970-01-01 00:00:00 的毫秒数
		long start = System.currentTimeMillis();

		for(int i = 2;i <= 100000;i++){//遍历100000以内的自然数
			
			//优化二:对本身是质数的自然数是有效的。
			//for(int j = 2;j < i;j++){
			for(int j = 2;j <= Math.sqrt(i);j++){//j:被i去除
				
				if(i % j == 0){ //i被j除尽
					isFlag = false;
					break;//优化一:只对本身非质数的自然数是有效的。
				}
			}
			//
			if(isFlag == true){
				//System.out.println(i);
				count++;
			}
			//重置isFlag
			isFlag = true;
		}
		//获取当前时间距离1970-01-01 00:00:00 的毫秒数
		long end = System.currentTimeMillis();
		System.out.println("质数的个数为:" + count);
		System.out.println("所花费的时间为:" + (end - start));//17110 - 优化一:break:1546 - 优化二:13

	}
}
//使用标签
	lable:	for(int i = 2;i <= 100000;i++){
                for(int j = 2;j <= Math.sqrt(i);j++){
                    if(i % j ==0){
                        continue lable;
                    }
                }
        	count++;
            }
			System.out.println(count);

数组

一维数组

数组的理解:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。

一维数组的使用

① 一维数组的声明和初始化

② 如何调用数组的指定位置的元素

③ 如何获取数组的长度

④ 如何遍历数组

    //1. 一维数组的声明和初始化
		int num;//声明
		num = 10;//初始化
		int id = 1001;//声明 + 初始化
		
		
		//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行
		int[]ids = new int[]{1001,1002,1003,1004};
		//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
		String[] names = new String[5];

		int[] arr4 = {1,2,3,4,5};//类型推断

		//2.如何调用数组的指定位置的元素:通过角标的方式调用。
		//数组的角标(或索引)从0开始的,到数组的长度-1结束。
		names[0] = "王铭";
		names[1] = "王赫";
		names[2] = "张学良";
		names[3] = "孙居龙";
		names[4] = "王宏志";//charAt(0)

		//3.如何获取数组的长度。
		//属性:length
		System.out.println(names.length);//5

        //4.如何遍历数组
		for(int i = 0;i < names.length;i++){
			System.out.println(names[i]);
		}
		

⑤ 数组元素的默认初始化值

  •  数组元素是整型:0
    
  •  数组元素是浮点型:0.0
    
  •  数组元素是char型:0或'\u0000',而非'0'
    
  •  数组元素是boolean型:false
    
  •  数组元素是引用数据类型:null
    

⑥ 数组的内存解析 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0kozw5k-1621514494148)(D:\note\tu\一维数组内存解析.png)]

二维数组

对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。

  1. 二维数组的使用:

① 二维数组的声明和初始化

② 如何调用数组的指定位置的元素

③ 如何获取数组的长度

④ 如何遍历数组

		//静态初始化
		int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
		//动态初始化1
		String[][] arr2 = new String[3][2];
		//动态初始化2
		String[][] arr3 = new String[3][];
		//也是正确的写法:
		int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}};
		int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};

		//2.如何调用数组的指定位置的元素
		System.out.println(arr1[0][1]);//2
		System.out.println(arr2[1][1]);//null

		//3.获取数组的长度
		System.out.println(arr4.length);//3
		System.out.println(arr4[0].length);//3
		System.out.println(arr4[1].length);//4

		//4.如何遍历二维数组
		for(int i = 0;i < arr4.length;i++){
			
			for(int j = 0;j < arr4[i].length;j++){
				System.out.print(arr4[i][j] + "  ");
			}
			System.out.println();
		}

⑤ 数组元素的默认初始化值

 *   ⑤ 数组元素的默认初始化值 
 *   针对于初始化方式一:比如:int[][] arr = new int[4][3];
 *      外层元素的初始化值为:地址值
 *      内层元素的初始化值为:与一维数组初始化情况相同
 *      
 *   针对于初始化方式二:比如:int[][] arr = new int[4][];
 *   	外层元素的初始化值为:null
 *      内层元素的初始化值为:不能调用,否则报错。

⑥ 数组的内存解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuDhInLa-1621514494149)(D:\note\tu\内存解析.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xNswHtWR-1621514494149)(D:\note\tu\二维数组内存解析.png)]

面向对象(上)

面向对象思想编程内容的三条主线分别是什么

① 类及类的成员:属性、方法、构造器;代码块、内部类

② 面向对象的三大特征:封装、继承、多态

③ 其它关键字:this,super,abstract,interface,static,final,package,import

类和对象

类(Class)和对象(Object)是面向对象的核心概念。

可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人

面向对象程序设计的重点是类的设计,类的设计,其实就是类的成员的设计

Java类及类的成员(属性和方法)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f5UogBzV-1621514494150)(D:\note\tu\5.png)]

类和对象的使用(面向对象思想落地的实现):

  • 1.创建类,设计类的成员
  • 2.创建类的对象
  • 3.通过“对象.属性”或“对象.方法”调用对象的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYrVw0MT-1621514494151)(D:\note\tu\6.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRaP4pSY-1621514494151)(D:\note\tu\7.png)]

对象的创建与对象的内存解析

说明:如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QlJxLKL9-1621514494152)(D:\note\tu\8.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CS5ACIfM-1621514494153)(D:\note\tu\9.png)]

理解"万事万物皆对象"
1.在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构

Scanner,String等文件:File 网络资源:URL
2.涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。

/*
 * 一、设计类,其实就是设计类的成员
 * 
 *  属性 = 成员变量 = field = 域、字段
 *  方法 = 成员方法 = 函数 = method
 * 
 *  创建类的对象 = 类的实例化 = 实例化类
 * 
 * 二、类和对象的使用(面向对象思想落地的实现):
 *  1.创建类,设计类的成员
 *  2.创建类的对象
 *  3.通过“对象.属性”或“对象.方法”调用对象的结构
 *   
 * 四、对象的内存解析
 */
//测试类
public class PersonTest {
	public static void main(String[] args) {
		//2. 创建Person类的对象
		Person p1 = new Person();
		//Scanner scanner = new Scanner(System.in);
		
		//调用对象的结构:属性、方法
		//调用属性:“对象.属性”
		p1.name = "Tom";
		p1.isMale = true;
		System.out.println(p1.name);
		
		//调用方法:“对象.方法”
		p1.eat();
		p1.sleep();
		p1.talk("Chinese");
		
		//*******************************
		Person p2 = new Person();
		System.out.println(p2.name);//null
		System.out.println(p2.isMale);
		//*******************************
		//将p1变量保存的对象地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
		Person p3 = p1;
		System.out.println(p3.name);//Tom
		
		p3.age = 10;
		System.out.println(p1.age);//10
		
	}
}

//1.创建类,设计类的成员
class Person{
	
	//属性
	String name;
	int age = 1;
	boolean isMale;
	
	//方法
	public void eat(){
		System.out.println("人可以吃饭");
	}
	
	public void sleep(){
		System.out.println("人可以睡觉");
	}
	
	public void talk(String language){
		System.out.println("人可以说话,使用的是:" + language);
	}
	
}

类的结构之一:属性

属性(成员变量) vs 局部变量

相同点:

​ 定义变量的格式:数据类型 变量名 = 变量值

​ 先声明,后使用

​ 变量都有其对应的作用域

不同点:

​ 在类中声明的位置的不同

​ 属性:直接定义在类的一对{}内

​ 局部变量:声明在方法内方法形参、代码块内、构造器形参、构造器内部的变量

class User{
	//属性(或成员变量)
	String name;
	public int age;
	boolean isMale;
	
	
	public void talk(String language){//language:形参,也是局部变量
		System.out.println("我们使用" + language + "进行交流");
		
	}
	
	public void eat(){
		String food = "烙饼";//局部变量
		System.out.println("北方人喜欢吃:" + food);
	}

补充:回顾变量的分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLfC6V4l-1621514494153)(D:\note\tu\10.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ypFxd0Yl-1621514494154)(D:\note\tu\11.png)]

类的结构之二:方法
/*
 * 类中方法的声明和使用
 * 
 * 方法:描述类应该具有的功能。
 * 比如:Math类:sqrt()\random() \...
 *     Scanner类:nextXxx() ...
 *     Arrays类:sort() \ binarySearch() \ toString() \ equals() \ ...
 * 
 * 1.举例:
 * public void eat(){}
 * public void sleep(int hour){}
 * public String getName(){}
 * public String getNation(String nation){}
 * 
 * 2. 方法的声明:权限修饰符  返回值类型  方法名(形参列表){
 * 					方法体
 * 			  }
 *   注意:static、final、abstract 来修饰的方法,后面再讲。
 *   
 * 3. 说明:
 * 		3.1 关于权限修饰符:默认方法的权限修饰符先都使用public
 * 			Java规定的4种权限修饰符:private、public、缺省、protected  -->封装性再细说
 * 
 * 		3.2 返回值类型: 有返回值  vs 没有返回值
 * 			3.2.1  如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
 *                return关键字来返回指定类型的变量或常量:“return 数据”。
 * 				  如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要
 *               使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。
 * 
 *      
 *      3.4 形参列表: 方法可以声明0个,1个,或多个形参。
 *         3.4.1 格式:数据类型1 形参1,数据类型2 形参2,...
 *    		
 * 
 *  4.return关键字的使用:
 *  	1.使用范围:使用在方法体中
 *  	2.作用:① 结束方法
 *            ② 针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。
 *      3.注意点:return关键字后面不可以声明执行语句。
 *      
 *  5. 方法的使用中,可以调用当前类的属性或方法
 *  		特殊的:方法A中又调用了方法A:递归方法。
 *     方法中,不可以定义方法。
 */
package com.hhj;

public class StudentTest1 {
	public static void main(String[] args) {
	/*
	 * 4. 对象数组题目:
	定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
	 创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
	问题一:打印出3年级(state值为3)的学生信息。
	问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息

	提示:
	1) 生成随机数:Math.random(),返回值类型double;  
	2) 四舍五入取整:Math.round(double d),返回值类型long。

	 * 
	 */
		Student1 [] stus = new Student1[20]; //先声明Student类 ,在声明一个student类型的数组
		
		for(int i = 0;i < stus.length;++i) {
			
			//给数组元素赋值
			stus[i] = new Student1(); 
			
			//给Student类的属性赋值 通过对象(数组).属性
			stus[i].number = i+1; //学号1-20
			stus[i].state = (int)(Math.random()*(6-1+1)+1);    //年级1-6
			stus[i].score = (int)(Math.random()*(100-0+1)+0);  //分数100-0
		}
		StudentTest1 test = new StudentTest1();
		//遍历 通过对象(数组).方法
	
		test.print(stus);
		System.out.println("********************");
//		问题一:打印出3年级(state值为3)的学生信息。
		
		test.searchState(stus, 3);
		System.out.println("********************");
		
		
//		问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
		test.sort(stus);
		test.print(stus);
	}
	public void print(Student1[] stus) {
		for(int i = 0;i < stus.length;++i) {
			stus[i].info();
		}
	}
	
	public void searchState(Student1[] stus,int state){
		for(int i = 0;i <stus.length;i++){
			if(stus[i].state == state){
		    stus[i].info();
			}
		}
	}
	public void sort(Student1[] stus) {
		for(int i = 0;i < stus.length; ++i) {
			for(int j = 0;j < stus.length-1-i;++j) {
				if(stus[j].score > stus[j + 1].score) {
					Student1 temp = stus[j]; //交换的值是人 Student类型 而不是分数 (int)
 					stus[j] = stus[j + 1];
					stus[j + 1] = temp;
				}
			}
			
		}
	}
}		

class Student1{
	int number;
	int state;
	int score;
	
	public void info(){
		System.out.println("学号 : " + number + "分数 :" + score + "年级 :"+state);
	}
}

方法的重载

方法的重载的概念
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。

  • 总结:“两同一不同”:同一个类、相同方法名
    参数列表不同:参数个数不同,参数类型不同

    跟方法的权限修饰符、返回值类型、形参变量名、方法体都没关系!

    //如下的4个方法构成了重载
    	public void getSum(int i,int j){
    		System.out.println("1");
    	}
    	
    	public void getSum(double d1,double d2){
    		System.out.println("2");
    	}
    	
    	public void getSum(String s ,int i){
    		System.out.println("3");
    	}
    	
    	public void getSum(int i,String s){
    		System.out.println("4");
    	}
    
可变个数形参的方法
1.使用说明:
 * 1.jdk 5.0新增的内容
 * 2.具体使用:
 *   2.1 可变个数形参的格式:数据类型 ... 变量名
 *   2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1,2个,。。。
 *   2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
 *   2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
 *   2.5 可变个数形参在方法的形参中,必须声明在末尾
 * 	 2.6  可变个数形参在方法的形参中,最多只能声明一个可变形参。

2.举例说明:
 	public void show(int i){
		
	}
	
	public void show(String s){
		System.out.println("show(String)");
	}
	
	public void show(String ... strs){
		System.out.println("show(String ... strs)");
		
		for(int i = 0;i < strs.length;i++){
			System.out.println(strs[i]);
		}
	}
	//不能与上一个方法同时存在
//	public void show(String[] strs){
//		
//	}
调用时:
		test.show("hello");
		test.show("hello","world");
		test.show();
		
		test.show(new String[]{"AA","BB","CC"});
java的值传递机制
1.针对于方法内变量的赋值举例:
		System.out.println("***********基本数据类型:****************");
		int m = 10;
		int n = m;
		
		System.out.println("m = " + m + ", n = " + n);
		
		n = 20;
		
		System.out.println("m = " + m + ", n = " + n);
		
		System.out.println("***********引用数据类型:****************");
		
		Order o1 = new Order();
		o1.orderId = 1001;
		
		Order o2 = o1;//赋值以后,o1和o2的地址值相同,都指向了堆空间中同一个对象实体。
		
		System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
		
		o2.orderId = 1002;
		
		System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
		

规则:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。

2.针对于方法的参数概念
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据

3.java中参数传递机制:值传递
规则:
 * 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
 * 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。

推广:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。

典型例题与内存解析:

面向对象的特征之一:封装性

1.隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

2.封装性思想具体的代码体现:

体现一:将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值

                    private double radius;
                    public void setRadius(double radius){
                        this.radius = radius;
                    }
                    public double getRadius(){
                        return radius;
                    }

体现二:不对外暴露的私有的方法

体现三:单例模式(将构造器私有化)

image-20210321000025956

体现四:如果不希望类在包外被调用,可以将类设置为缺省的。

3.Java规定的四种权限修饰符

3.1 权限从小到大顺序为:private < 缺省 < protected < public

3.2 具体的修饰范围:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPdGen2r-1621514494167)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20210320235320915.png)]

3.3 权限修饰符可用来修饰的结构说明:
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public

类的结构之三:构造器

构造器的作用:

  • 1.创建对象
  • 2.初始化对象的信息

2.使用说明:

  • 1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
  • 2.定义构造器的格式:权限修饰符 类名(形参列表){}
  • 3.一个类中定义的多个构造器,彼此构成重载
  • 4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
  • 5.一个类中,至少会有一个构造器。
//构造器
	   //创建类的对象:new + 构造器
		Person p = new Person();

	public Person(){
		System.out.println("Person().....");
	}

	public Person(String n){
		name = n;
		
	}
	
	public Person(String n,int a){
		name = n;
		age = a;
	}
属性赋值的顺序

总结:属性赋值的先后顺序

① 默认初始化

② 显式初始化

③ 构造器中初始化

④ 通过"对象.方法" 或 "对象.属性"的方式,赋值

以上操作的先后顺序:① - ② - ③ - ④

JavaBean的概念

所谓JavaBean,是指符合如下标准的Java类:
>类是公共的
>一个无参的公共的构造器
>属性,且对应的get、set方法

关键字(this、package、import)
关键字this

1.关键字this:可以调用的结构:属性、方法;构造器

​ this调用属性、方法:
​ this理解为:当前对象 或 当前正在创建的对象

2.1 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。

  • 但是,通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式

    的使用"this.变量"的方式,表明此变量是属性,而非形参。

2.2 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。

  • 但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。

3.this调用构造器:

① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)“方式调用自己
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)”
④ 规定:"this(形参列表)“必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器

public Person(){
//		this.eat();
		String info = "Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)";
		System.out.println(info);
	}
	public Person(String name){
		this();
		this.name = name;
		
	}
	public Person(int age){
		this();
		this.age = age;
	}
	public Person(String name,int age){
		this(age);
		this.name = name;
		//this.age = age;
		//Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)
	}
关键字package

使用说明:

  • 1.为了更好的实现项目中类的管理,提供包的概念
  • 2.使用package声明类或接口所属的包,声明在源文件的首行
  • 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
  • 4.每"."一次,就代表一层文件目录。
关键字 import

import(导入)的使用:

    1. 在源文件中显式的使用import结构导入指定包下的类、接口
    1. 声明在包的声明和类的声明之间
    1. 如果需要导入多个结构,则并列写出即可
    1. 可以使用"xxx.*"的方式,表示可以导入xxx包下的所结构
    1. 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
    1. 如果使用的类或接口是本包下定义的,则可以省略import结构
    1. 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
    1. 使用"xxx.*"方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
    1. import static:导入指定类或接口中的静态结构:属性或方法。

面向对象(中)

面向对象的特征二:继承性

1、为什么要有类的继承性?(继承性的好处)

  • ① 减少了代码的冗余,提高了代码的复用性
  • ② 便于功能的扩展
  • ③ 为之后多态性的使用,提供了前提

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rwddc3rD-1621514494168)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20210327211926498.png)]

2、继承性的格式:
class A extends B{}

​ A:子类、派生类、subclass

​ B:父类、超类、基类、superclass

3、子类继承父类以后有哪些不同?
3.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性方法

​ 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为

​ 封装性的影响,使得子类不能直接调用父类的结构而已。

3.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。

​ 子类和父类的关系,不同于子集和集合的关系。

​ extends:延展、扩展

4、Java中继承性的说明
1.一个类可以被多个子类继承。
2.Java中类的单继承性:一个类只能有一个父类
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUoBNRUD-1621514494168)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20210327212344411.png)]

5.java.lang.Object类的理解

  1. 如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类
  2. 所的java类(除java.lang.Object类之外都直接或间接的继承于java.lang.Object类
  3. 意味着,所的java类具有java.lang.Object类声明的功能。
方法的重写

1.什么是方法的重写(override 或 overwrite)?
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作

2、应用:
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。

3.举例:

class Circle{
public double findArea(){}//求面积
}
class Cylinder extends Circle{
public double findArea(){}//求表面积
}
***************
class Account{
public boolean withdraw(double amt){}
}
class CheckAccount extends Account{
public boolean withdraw(double amt){}
}

4.重写的规则:

方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型

{

​ 方法体

}

子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写,要么都声明为static的(不是重写)

5.面试题:区分方法的重写和重载?

答:① 二者的概念,② 重载和重写的具体规则③ 重载:不表现为多态性。重写:表现为多态性。

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为**“早绑定”“静态绑定”**;

而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为**“晚绑定”“动态绑定”。**

关键字:super

1.super 关键字可以理解为:父类的

2.可以用来调用的结构:属性、方法、构造器

3.super调用属性、方法:

  • 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
  • 特殊情况**:当子类和父类中定义了同名的属性时**,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
  • 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。

4.super调用构造器:

  • 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
  • "super(形参列表)"的使用,必须声明在子类构造器的首行
  • 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现
  • 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
  • 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
子类对象实例化全过程

1.从结果上看:继承性

子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所父类中声明的属性。

2.从过程上看:

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IvDzNYUh-1621514494169)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20210328152918643.png)]

3.强调说明:

虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

image-20210328153012017
面向对象的特征三:多态性

1.多态性的理解:可以理解为一个事物的多种形态。

2.何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

举例:
Person p = new Man();
Object obj = new Date();

3.多态性的使用:虚拟方法调用

有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。

4.多态性的使用前提:

① 类的继承关系 ② 方法的重写

5.多态性的应用举例:

举例一:
	public void func(Animal animal){//Animal animal = new Dog();
		animal.eat();
		animal.shout();
	}

6.多态性使用的注意点:
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)

7.关于向上转型与向下转型:

向上转型:多态

向下转型:为什么使用向下转型?

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。

如何实现向下转型:如何实现向下转型:()

使用时的注意点:① 使用强转时,可能出现ClassCastException的异常。(类型转换异常)
② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。

需要注意什么问题?如何解决此问题?

Person p = new Man();
使用强转符:()
Man m = (Man)p;

可能ClassCastException异常。

使用instanceof在进行向下转型前判断。
if(p instanceof Man){
Man m = (Man)p;
}

8.instanceof的使用:

① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。

image-20210402200939857

9.谈谈你对多态性的理解 多态是运行时行为

① 实现代码的通用性。
② Object类中定义的public boolean equals(Object obj){ }
JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)

Object类的使用

java.lang.Object类的说明:

1.Object类是所Java类的根父类

2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

3.Object类中的功能(属性、方法)就具通用性。

​ 属性:无

    方法:equals() / toString() / getClass() /hashCode() / clone() / finalize() wait() 、 notify()、notifyAll()

Object类只声明了一个空参的构造器

equals()的使用:(开发中自动生成)

  1. 是一个方法,而非运算符

  2. 只能适用于引用数据类型

  3. Object类中equals()的定义:

    public boolean equals(Object obj) {
    	        return (this == obj);
    	  }
    //说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
    
  4. String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个**对象的"实体内容"**是否相同。

  5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写

    重写的原则:比较两个对象的实体内容是否相同

    == 运算符的使用:

    1. 可以使用在基本数据类型变量和引用数据类型变量中
    1. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)

    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

  • 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

toString()方法 ()

作用是返回一个值为对象的内存地址的字符串

toString()的使用:

  1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

  2. Object类中toString()的定义:

    public String toString() {
       return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    

    3.像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息

    4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"

如何重写toString()

//自动实现
	@Override
	public String toString() {
		return "Customer [name=" + name + ", age=" + age + "]";
	}
单元测试方法

Java中的JUnit单元测试

步骤:

1.当前工程 - 右键择:build path - add libraries - JUnit 4 - 下一步

2.创建Java类,进行单元测试。

此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器

3.此类中声明单元测试方法。

此时的单元测试方法:方法的权限是public,没返回值,没形参

4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;

5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。

6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test

说明:

1.如果执行结果没任何异常:绿条

2.如果执行结果出现异常:红条

包装类的使用

1.为什么要有包装类(或封装类)

​ 为了使基本数据类型的变量具有类的特征,引入包装类。

2.基本数据类型与对应的包装类:

image-20210402205150571

3.需要掌握的类型间的转换:(基本数据类型、包装类、String)

JDK 5.0 新特性:自动装箱 与自动拆箱

//自动装箱:基本数据类型 --->包装类
		int num2 = 10;
		Integer in1 = num2;//自动装箱

//自动拆箱:包装类--->基本数据类型
		System.out.println(in1.toString());
		
		int num3 = in1;//自动拆箱

基本数据类型、包装类—>String:调用String重载的valueOf(Xxx xxx

//方式1:连接运算
		String str1 = num1 + "";
		//方式2:调用String的valueOf(Xxx xxx)
		float f1 = 12.3f;
		String str2 = String.valueOf(f1);//"12.3"

		Double d1 = new Double(12.4);
		String str3 = String.valueOf(d1);

String—>基本数据类型、包装类:调用包装类的parseXxx(String s)

int num2 = Integer.parseInt(str1)
boolean b1 = Boolean.parseBoolean(str2);

面向对象(下)

关键字:static(静态的)

1.可以用来修饰的结构:主要用来修饰类的内部结构
属性、方法、代码块、内部类(除了构造器)

2.static修饰属性:静态变量(或类变量)

1、属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)

静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

2、 static修饰属性的其他说明:

  • ① 静态变量随着类的加载而加载。可以通过**“类.静态变量”**的方式进行调用

  • ② 静态变量的加载要早于对象的创建。

  • ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。

    ​ 类变量 实例变量

    ​ 类 yes no

    ​ 对象 yes yes

3.静态变量内存解析:

image-20210412102437163

4.static修饰方法:静态方法、类方法

随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
静态方法 非静态方法

​ 类 yes no

​ 对象 yes yes
​ 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性

  1. static的注意点:
    5.1 在静态的方法内,不能使用this关键字、super关键字
    5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。

  2. 如何判定属性和方法应该使用static关键字:
    6.1 关于属性

属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static

6.2 关于方法

操作静态属性的方法,通常设置为static的
工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections

7.使用举例:

举例一:Arrays、Math、Collections等工具类
举例二:单例模式
举例三:

class Circle{
	
	private double radius;
	private int id;//自动赋值
	
	public Circle(){
		id = init++;
		total++;
	}
	
	public Circle(double radius){
		this();
//		id = init++;
//		total++;
		this.radius = radius;
		
	}
	
	private static int total;//记录创建的圆的个数
	private static int init = 1001;//static声明的属性被所对象所共享
	
	public double findArea(){
		return 3.14 * radius * radius;
	}

	public double getRadius() {
		return radius;
	}

	public void setRadius(double radius) {
		this.radius = radius;
	}

	public int getId() {
		return id;
	}

	public static int getTotal() {
		return total;
	}

}
单例模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。

饿汉式1class Bank{
	
	//1.私化类的构造器
	private Bank(){
		
	}
	
	//2.内部创建类的对象
	//4.要求此对象也必须声明为静态的
	private static Bank instance = new Bank();
	
	//3.提供公共的静态的方法,返回类的对象
	public static Bank getInstance(){
		return instance;
	}
}


饿汉式2:使用了静态代码块
class Order{
	
	//1.私化类的构造器
	private Order(){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Order instance = null;

	static{
		instance = new Order();
 }
	
	//3.声明public、static的返回当前类对象的方法
	public static Order getInstance(){
		return instance;
	}
	
}
懒汉式:
class Order{
	
	//1.私化类的构造器
	private Order(){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Order instance = null;
	
	//3.声明public、static的返回当前类对象的方法
	public static Order getInstance(){
		
		if(instance == null){
			
			instance = new Order();
			
		}
		return instance;
	}
	
}
main()的使用说明
    1. main()方法作为程序的入口
    1. main()方法也是一个普通的静态方法
    1. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)

如何将控制台获取的数据传给形参:String[] args?
运行时:java 类名 “Tom” “Jerry” “123” “true”

​ sysout(args[0]);//“Tom”
​ sysout(args[3]);//“true” -->Boolean.parseBoolean(args[3]);
​ sysout(args[4]);//报异常

小结:一叶知秋

public static void main(String[] args){//方法体}

权限修饰符: private 缺省 protected pubilc ---->封装性
修饰符:static \ final \ abstract \native 可以用来修饰方法
返回值类型: 无返回值 / 有返回值 -->return
方法名:需要满足标识符命名的规则、规范;“见名知意”
形参列表:重载 vs 重写;参数的值传递机制;体现对象的多态性
方法体:来体现方法的功能

类的结构之四:代码块(初始块)

1.代码块的作用:用来初始化类、对象的信息

2.分类:代码块要是使用修饰符,只能使用static
静态代码块 vs 非静态代码块

静态代码块:

内部可以输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

非静态代码块:

内部可以输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

  1. 实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:

    由父及子,静态先行。

属性的赋值顺序

  • ①默认初始化

  • ②显式初始化/⑤在代码块中赋值

  • ③构造器中初始化

  • ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值

    执行的先后顺序:① - ② / ⑤ - ③ - ④

关键字:final

可以用来修饰:类、方法、变量

​ final 用来修饰一个类:此类不能被其他类所继承。

​ 比如:String类、System类、StringBuffer类

​ final 用来修饰方法:表明此方法不可以被重写

​ 比如:Object类中getClass();

​ final 用来修饰变量:此时的"变量"就称为是一个常量

​ final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化

​ final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。

static final 用来修饰属性:全局常量

关键字:abstract(抽象的)

1.可以用来修饰:类、方法

2.具体的:

abstract修饰类:抽象类

  •  > 此类不能实例化
    
  •  > 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
    
  •  > 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 --->抽象的使用前提:继承性
    

abstract修饰方法:抽象方法

  •  > 抽象方法只方法的声明,**没方法体**
    
  •  > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
    
  •    > 若子类重写了父类中的所的抽象方法后,此子类方可实例化
    
  •      >  若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
    

3.注意点:

​ 1.abstract不能用来修饰:属性、构造器等结构

​ 2.abstract不能用来修饰私方法、静态方法、final的方法、final的类

static修饰方法:会使这个方法成为整个类所公有的方法,可以用" 类名.方法名 “访问。
abstract修饰方法:会使这个方法变成抽象方法,也就是只有声明而没有实现,实现部分以”;"代替,需要子类继承实现。
试想:static修饰的方法会有具体的实现,而abstract肯定没有具体的实现,两个完全冲突的修饰关键词 怎么可能会放在一起使用呢!

4.abstract的应用举例:

举例一:

image-20210412152542761

举例二:

abstract class GeometricObject{
public abstract double findArea(); //抽象方法的声明
}
class Circle extends GeometricObject{
private double radius;
public double findArea(){
		return 3.14 * radius * radius;
};
}
关键字:interface(接口)

1.接口使用interface来定义

2.Java中,接口和类是并列的两个结构

3.如何定义接口:定义接口中的成员

3.1 JDK7及以前:只能定义全局常量和抽象方法

  •    	全局常量:public static final的.但是书写时,可以省略不写
    
  • 抽象方法:public abstract的,但是书写时,可以省略不写

  •    3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略
    
  1. 接口中不能定义构造器的!意味着接口不可以实例化

  2. Java开发中,接口通过让类去实现(implements)的方式来使用.如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化.如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类

  3. Java类可以实现多个接口 —>弥补了Java单继承性的局限性

    ​ 格式:class AA extends BB implements CC,DD,EE

    7.接口与接口之间可以继承,而且可以多继承 ,接口的具体使用,体现多态性,接口,实际上可以看做是一种规范

/*
 * 接口的使用
 * 1.接口使用上也满足多态性
 * 2.接口,实际上就是定义了一种规范
 * 3.开发中,体会面向接口编程!
 * 
 */
public class USBTest {
	public static void main(String[] args) {
		
		Computer com = new Computer();
		//1.创建了接口的非匿名实现类的非匿名对象
		Flash flash = new Flash();
		com.transferData(flash);
		
		//2. 创建了接口的非匿名实现类的匿名对象
		com.transferData(new Printer());
		
		//3. 创建了接口的匿名实现类的非匿名对象
		USB phone = new USB(){

			@Override
			public void start() {
				System.out.println("手机开始工作");
			}

			@Override
			public void stop() {
				System.out.println("手机结束工作");
			}
			
		};
		com.transferData(phone);
		
		
		//4. 创建了接口的匿名实现类的匿名对象
		
		com.transferData(new USB(){
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}

			@Override
			public void stop() {
				System.out.println("mp3结束工作");
			}
		});
	}
}
	
class Computer{
	
	public void transferData(USB usb){//USB usb = new Flash();
		usb.start();
		
		System.out.println("具体传输数据的细节");
		
		usb.stop();
	}

}

interface USB{
	//常量:定义了长、宽、最大最小的传输速度等
	
	void start();
	
	void stop();
	
}

class Flash implements USB{

	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}

	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}
	
}

class Printer implements USB{
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}

	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}
	
}

Java8中关于接口的新规范

  • 知识点1:接口中定义的静态方法,只能通过接口来调用。

  • 知识点2:通过实现类的对象,可以调用接口中的默认方法。
    如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法

  • 知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。–>类优先原则

  • 知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
    那么在实现类没重写此方法的情况下,报错。–>接口冲突。
    这就需要我们必须在实现类中重写此方法

  • 知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法

    public void myMethod(){
    		method3();//调用自己定义的重写的方法
    		super.method3();//调用的是父类中声明的
    		//调用接口中的默认方法
    		CompareA.super.method3();
    		CompareB.super.method3();
    	}
    

    面试题:抽象类和接口的异同?

    相同点:不能实例化;都可以包含抽象方法的。(接口中 不能定义构造器,抽象类中可以)
    不同点:
    1)把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明
    2)类:单继承性 接口:多继承
    类与接口:多实现

代理模式

解决的问题:代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。


public class NetWorkTest {
	public static void main(String[] args) {
		Server server = new Server();
//		server.browse();
		ProxyServer proxyServer = new ProxyServer(server);
		
		proxyServer.browse();  //输出 联网之前的检查工作
        					   //在输出 真实的服务器访问网络
		
	}
}

interface NetWork{
	
	public void browse();
	
}

//被代理类
class Server implements NetWork{

	@Override
	public void browse() {
		System.out.println("真实的服务器访问网络");
	}

}
//代理类
class ProxyServer implements NetWork{
	
	private NetWork work;
	
	public ProxyServer(NetWork work){
		this.work = work;
	}
	

	public void check(){
		System.out.println("联网之前的检查工作");
	}
	
	@Override
	public void browse() {
		check();
		
		work.browse();
		
	}
	
}
类的结构之五:内部类

定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

内部类的分类:
成员内部类(静态、非静态 ) vs 局部内部类(方法内、代码块内、构造器内)

一方面,作为外部类的成员:

  •  	>调用外部类的结构
    
  •  	>可以被static修饰
    
  •  	>可以被4种不同的权限修饰
    

另一方面,作为一个类:

  •  	> 类内可以定义属性、方法、构造器等
    
  •  	> 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
    
  •  	> 可以被abstract修饰
    

成员内部类:

4.1如何创建成员内部类的对象?(静态的,非静态的)
//创建静态的Dog内部类的实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();

//创建非静态的Bird内部类的实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();

4.2如何在成员内部类中调用外部类的结构?

class Person{
	String name = "小明";
public void eat(){
}
//非静态成员内部类
	class Bird{
		String name = "杜鹃";
		public void display(String name){
			System.out.println(name);//方法的形参
			System.out.println(this.name);//内部类的属性
			System.out.println(Person.this.name);//外部类的属性
		//Person.this.eat();
		}
	}
}

局部内部类的使用:

//返回一个实现了Comparable接口的类的对象
	public Comparable getComparable(){
		
		//创建一个实现了Comparable接口的类:局部内部类
		//方式一:
//		class MyComparable implements Comparable{
//
//			@Override
//			public int compareTo(Object o) {
//				return 0;
//			}
//			
//		}
//		
//		return new MyComparable();
        
//方式二:
		return new Comparable(){

			@Override
			public int compareTo(Object o) {
				return 0;
			}
			
		};
		

注意点:

在局部内部类的方法中(比如:show如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。

jdk 7及之前版本:要求此局部变量显式的声明为final的
jdk 8及之后的版本:可以省略final的声明

总结:
成员内部类和局部内部类,在编译以后,都会生成字节码文件。
格式:成员内部类:外部类 内 部 类 名 . c l a s s 局 部 内 部 类 : 外 部 类 内部类名.class 局部内部类:外部类 .class数字 内部类名.class

异常处理

异常
  1. 异常的体系结构

java.lang.Throwable

​ |-----java.lang.Error:一般不编写针对性的代码进行处理。

​ |-----java.lang.Exception:可以进行异常的处理

​ |------编译时异常(checked)

​ |-----IOException

​ |-----FileNotFoundException

​ |-----ClassNotFoundException

​ |------运行时异常(unchecked,RuntimeException)

​ |-----NullPointerException

​ |-----ArrayIndexOutOfBoundsException

​ |-----ClassCastException

​ |-----NumberFormatException

​ |-----InputMismatchException

​ |-----ArithmeticException

image-20210413184935012

2.从程序执行过程,看编译时异常和运行时异常

image-20210413185014498

编译时异常:执行javac.exe命名时,可能出现的异常

运行时异常:执行java.exe命名时,出现的异常

常见的异常类型

//******************以下是运行时异常***************************
	//ArithmeticException
	@Test
	public void test6(){
		int a = 10;
		int b = 0;
		System.out.println(a / b);
	}
	
	//InputMismatchException
	@Test
	public void test5(){
		Scanner scanner = new Scanner(System.in);
		int score = scanner.nextInt();
		System.out.println(score);
		
		scanner.close();
	}
	
	//NumberFormatException
	@Test
	public void test4(){
		
		String str = "123";
		str = "abc";
		int num = Integer.parseInt(str);
		
		
		
	}
	
	//ClassCastException
	@Test
	public void test3(){
		Object obj = new Date();
		String str = (String)obj;
	}
	
	//IndexOutOfBoundsException
	@Test
	public void test2(){
		//ArrayIndexOutOfBoundsException
//		int[] arr = new int[10];
//		System.out.println(arr[10]);
		//StringIndexOutOfBoundsException
		String str = "abc";
		System.out.println(str.charAt(3));
	}
	
	//NullPointerException
	@Test
	public void test1(){
		
//		int[] arr = null;
//		System.out.println(arr[3]);
		
		String str = "abc";
		str = null;
		System.out.println(str.charAt(0));
		
	}

	//******************以下是编译时异常***************************
	@Test
	public void test7(){
//		File file = new File("hello.txt");
//		FileInputStream fis = new FileInputStream(file);
//		
//		int data = fis.read();
//		while(data != -1){
//			System.out.print((char)data);
//			data = fis.read();
//		}
//		
//		fis.close();
		
	}
异常的处理

过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。

并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。

关于异常对象的产生:① 系统自动生成的异常对象、② 手动的生成一个异常对象,并抛出(throw)

过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws

1.异常处理方式一:try-catch-finally

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bp0zMBss-1621514494170)(C:\Users\黄晓丽\AppData\Roaming\Typora\typora-user-images\image-20210413185920200.png)]

说明:

  1. finall是可的。

  2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配

  3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码

  4. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。

​ catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错

  1. 常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()

  2. 在try结构中声明的变量,再出了try结构以后,就不能再被调用

  3. try-catch-finally结构可以嵌套

总结:如何看待代码中的编译时异常和运行时异常?

体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现

体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。

finally的再说明:

1.finally是可的

2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。

3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

2.异常处理方式二 throws + 异常类型

"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

  • 对比两种处理方式

try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。

  • 体会开发中应该如何选择两种处理方式?

​ 5.1 如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。

​ 5.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

手动抛出异常对象
  1. 在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。

2 . throw 和 throws区别:
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。

3.典型例题

public class StudentTest {
	public static void main(String[] args) {
		
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
			
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}

}
class Student{
	
	 private int id;
	 public void regist(int id ) throws Exception {
		 if(id > 0) {
			 this.id = id;
		 }
		 else {
			 
//			 System.out.println("您输入的数据非法");
			 
//			 手动抛出异常
//			 throw new RuntimeException("您输入的数据非法");
//			 throw new Exception("您输入的数据非法1")
			 //手动抛出自定义异常
			 throw new MyException("不能输入负数");
			
		 }
	 }
	 
	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
}
自定义异常类
/*
 * 如何自定义异常类?
 * 1. 继承于现的异常结构:RuntimeException 、Exception
 * 2. 提供全局常量:serialVersionUID
 * 3. 提供重载的构造器
 * 
 */
public class MyException extends Exception{
	
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg);
	}
}

/ }
//
// fis.close();

}

#### 异常的处理

过程一:"抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。

并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。

关于异常对象的产生:① 系统自动生成的异常对象、② **手动的生成一个异常对象,并抛出(throw)**

过程二:"抓":可以理解为异常的处理方式:① try-catch-finally  ② throws



##### 1.异常处理方式一:try-catch-finally

[外链图片转存中...(img-Bp0zMBss-1621514494170)]

说明:

1. finall是可的。

2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配

3. **一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码**

4. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。

​       catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错

5. 常用的异常对象处理的方式: ① String  getMessage()    ② printStackTrace()

6. 在try结构中声明的变量,再出了try结构以后,就不能再被调用

7. try-catch-finally结构可以嵌套



总结:如何看待代码中的编译时异常和运行时异常?

体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,**延迟到运行时出现**。

体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对**运行时异常编写try-catch-finally了**。针对于编译时异常,我们说一定要考虑异常的处理。



finally的再说明:

1.finally是可的

2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。

3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。



##### 2.异常处理方式二 throws + 异常类型

"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!



- 对比两种处理方式

try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。



- 体会开发中应该如何选择两种处理方式?

​       5.1 如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。

​       5.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

#### 手动抛出异常对象

1. 在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。

2  . throw 和  throws区别:
      throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
      throws 属于异常处理的一种方式,声明在方法的声明处。



3.典型例题

```java
public class StudentTest {
	public static void main(String[] args) {
		
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
			
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}

}
class Student{
	
	 private int id;
	 public void regist(int id ) throws Exception {
		 if(id > 0) {
			 this.id = id;
		 }
		 else {
			 
//			 System.out.println("您输入的数据非法");
			 
//			 手动抛出异常
//			 throw new RuntimeException("您输入的数据非法");
//			 throw new Exception("您输入的数据非法1")
			 //手动抛出自定义异常
			 throw new MyException("不能输入负数");
			
		 }
	 }
	 
	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
}
自定义异常类
/*
 * 如何自定义异常类?
 * 1. 继承于现的异常结构:RuntimeException 、Exception
 * 2. 提供全局常量:serialVersionUID
 * 3. 提供重载的构造器
 * 
 */
public class MyException extends Exception{
	
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值