javaSE总结

文章深入介绍了Java编程的基础知识,包括JDK安装、Java程序的编译与运行机制、数组与数据结构操作、方法定义与重载、类与对象的构造、封装原则、接口应用、内部类详解以及String类与异常处理等关键概念,并通过实例演示了算法应用如求质数、最大公约数等。
摘要由CSDN通过智能技术生成

目录

JAVA

一、Java概述

二、Java的加载与执行

三、开始一个JAVA程序

注释

字面值

变量

数据类型

运算符

四、结构化编程

五、数组

一维数组的使用

一维数组的练习题:

多维数组的使用

六、方法的使用

1.方法的定义以及使用

2.掌握方法传参

3.掌握方法重载

4.掌握递归

七、类和对象

this引用

对象的构造和初始化

八、封装

包的概念

private

static静态成员变量

static静态成员方法

代码块

         继承和多态

继承:

多态

抽象类

九、接口

什么是接口?

接口使用:

接口的使用案例

接口间的继承

Clonable接口和深拷贝

Object类

十、内部类

实例内部类

静态内部类

局部内部类

匿名内部类

十一、String类

String中有两个成员变量

方法:

String类的比较

十一、异常

异常的捕获:

finally

面试题:

异常处理


JAVA

一、Java概述

1995年诞生

1>什么是JDK

JAVA开发工具包

该工具从 Oracle | Cloud Applications and Cloud Platform 下载 下载11

选择源代码,路径(别安在桌面)环境配置

需要配置的环境变量:

1.新增一个变量JAVA_HOME,值为安装JDK的路径,C:\Java\jdk-XX.XX.XX

2.修改path变量,新增一个值为%JAVA_HOME%\bin 注意是修改,不要去除原来的配置

打开命令窗口

javac -version java - version

2>JAVA包括三大类

--JAVASE (JAVA标准版) 基础

--JAVAEE (JAVA企业版) 主攻方向

--JAVAME (JAVA微型版)

3>JAVA语言特性(开源,免费,纯面向对象,跨平台)

*简单性 例如:JAVA中不再支持多继承,c++是支持多继承的;C++中有指针,JAVA无

*面向对象

*可移植性 :一次编译,到处运行(可运行至windows或者Linux操作系统)

结论:显然JAVA程序不能直接和操作系统打交道,所以让JAVA在虚拟机(JVM:Java虚拟机)上运行

---Java虚拟机分为可适用于windows版的和Linux版的

*多线程 *健壮性 *安全性

二、Java的加载与执行

*Java程序的运行分为两个阶段:编译和运行

编译:

--编译阶段主要任务是检查Java源程序是否符合Java语法,若符合Java语法规则会生成正常的字节码文件(.java 文件编译成 .class文件);若不符合无法生成字节码文件

--字节码文件中不是纯粹的二进制,这种文件无法在操作系统当中直接执行

编译阶段的过程:

*程序员需要在硬盘的某个位置<位置随意>新建一个.java扩展名的文件,该文件被称为Java源文件,源文件中编写的是Java源代码/源程序。而这个源程序是不能随意编写的,必须符合Java语法规则

*Java程序员需要使用JDK当中自带的javac.exe命令进行Java程序的编译

Javac怎么用的?在哪用?

--在DOS命令窗口中使用 ​ --javac的使用规则: javac java源文件的路径

javac是一个Java编译器工具/命令

*一个Java源文件可以生成多个.class文件

*编译结束后可将class文件拷贝到其他操作系统当中运行『跨平台』

运行:

--JDK安装之后,除了自带一个javac.exe之外,还有另一个工具/命令,叫做java.exe,主要负责运行阶段

--Java.exe 怎么用?在哪用

在DOS窗口上使用

用法:java 类名

例如:如果硬盘上有一个A.class,那么就这样用: java A ​ (注意:不能写成 java A.class 错误的)

运行步骤:

*输入:java A

*java.exe命令会启动java虚拟机(JVM),JVM会启动类加载器classLoader

*ClassLoader会去硬盘上搜索A.class文件,找到该文件将该字节码文件装载到JVM当中

*JVM将A.class字节码文件解释成二进制010001001这样的数据

*然后操作系统执行二进制和底层硬件平台进行交互

三、开始一个JAVA程序

1.安装JDK,在官网下载

2.JDK,JRE,JVM的关系搞清楚

3.JDK目录的介绍

JDK/bin:该目录下存放很多命令,例如javac.exe和Java.exe ​ javac.exe负责编译 ​ java.exe负责运行

4.开发helloworld.java源程序

public class HelloWorld{
    public static void main (string[] args) {
        system.out.println("Hello World!")
    }
}

5.将helloworld.java源程序通过javac工具进行编译:

--首先需要解决的问题是:javac命令是否可用

--打开dos命令窗口,直接输入javac,回车

补充:

cmd:windows操作系统如何搜索硬盘上某个命令?

1.首先会从当前目录下搜索 ​ 2.如果目录搜索不到的话,会从环境变量path指定的路径中搜索某个命令 ​ 3.如果都搜索不到,就会报错‘不是内部或外部命令’

6.Javac命令怎么用

注释

--出现在Java源程序当中,对Java源程序的解释说明,增强程序的可读性

--单行注释: //,只注释当前行 --多行注释: /* */ --javadoc注释:/** * */ 其中数据可被javadoc.exe提取出来,比较专业的注释

public class和class的区别

字面值

*字面值

--10、100 --3.14 --“abc" --true false

*字面值就是数据

*字面值是java源程序的组成部分之一,包括标识符和关键字他们都是源程序的组成部分。

*数据在现实世界中是分门别类的,所以数据在计算机编程语言中也是有类别的,称为数据类型

*注意:Java语言当中所有的字符串型字面值必须使用双引号括起来

Java语言当中所有的字符型字面值必须用单引号括起来

变量

1.什么是变量? 字面值是数据类型的一部分

变量本质上来说是内存上的一块空间,这块空间有数据类型、名字、字面值

变量包含三个部分:数据类型、名称、字面值

变量是内存中存储数据的最基本的单元

2.数据类型的作用

不同的数据有不同的数据类型,不同的数据类型底层分配不同大小的空间

数据类型是指导程序在运行阶段应该分配多大的内存空间

3.变量要求:变量中存储的具体的数据必须和变量的数据类型一致,当不一致的时候,编译报错

4.声明/定义变量的语法格式: 数据类型 变量名;

变量名:只要是合法的标识符就行,规范要求:首字母小写,后面每个单词首字母大写。

5.变量名声明之后怎么赋值?

语法格式: 变量名 = 字面值;

要求:字面值的数据类型必须和变量的数据类型一致。 ​ 等号是一个运算符。

6.声明和赋值可以放在一起完成。

7.变量赋值之后,可以重复赋值,变量的值可以变化;

int i = 10; System.out.println(i); //10

int i = 20; System.out.println(i); //20

8.有了变量的概念之后,内存空间得到了重复的使用:

9.通常访问一个变量包括两种访问形式:

第一种:读取变量中保存的具体数据 get/数据

第二章:修改变量中保存的具体数据 set/数据

10.变量在一行上可以声明多个: int a,b,c;

11.变量必须先声明再赋值才能访问

12.在方法体中的Java代码是遵守自上而下的顺序依次执行,逐行运行的

特点:第二行的代码必须完整的执行完之后第三行才正式执行

13.在同一个“作用域”当中,变量名不能重名,但是变量可以重新赋值。

14.关于作用域:

*什么是作用域?

描述的就是变量的有效范围,在什么范围之内是可以被访问的,只要出了这个范围该变量就无法访问了。

*变量的作用域只要记住一句话:

出了大括号就不认识了

如果需要使用具体的上下限取值,可以通过包装类中的常量进行使用

System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);

进制转换

int k = 123;
String ob = Integer.toBinaryString(k);  //将十进制转换成二进制的字符串
System.out.println(ob); //1111011
​
String oo = Integer.toOctalString(k);  //将十进制转换成8进制的字符串
System.out.println(oo); //173
​
String oh = Integer.toHexString(k);  //将十进制转换成16进制的字符串
System.out.println(oh); //7b

数据类型

1.数据类型的作用是什么? 程序当中有很多数据,每一个数据都是有相关类型的,不同类型的数据占用空间大小不同。数据类型的作用是指导JVM在运行程序的时候给该数据分配多大的内存空间。

2.Java中的数据类型包括两种:

基本数据类型和引用数据类型

3.关于基本数据类型:

基本数据类型包括四大类八小种:

第一类:整数型 byte short int long

第二类:浮点型 float double

第三类:布尔型 boolean

第四类:字符型 char

4.字符串不属于基本数据类型,属于引用数据类型,但是字符属于基本数据类型

5.八种数据类型各自占用空间大小是多少?

基本数据类型 占用空间大小「单位:字节」 ————————————————————————————

byte 1 ​ short 2 ​ int 4 ​ long 8 ​ float 4 ​ double 8 ​ boolean 1 ​ char 2

6.计算机在任何情况下都只能识别二进制 『现代的计算机底层采用交流电的方式,接通和断开两种状态』

7.什么是二进制? 一种数据的表示形式

8.字节(byte):

1 byte = 8 bit (1个字节=8个比特位) 1个比特位表示一个二进制位:1/0 ​ 1 KB = 1024 Byte ​ 1 MB = 1024 KB ​ 1 GB = 1024 MB ​ 1 TB = 1024 KB

9.整数型当中的byte类型占用1个字节,所以byte类型的数据占用8个比特位,那么byte类型的取值范围是什么?

*关于Java中的数字类型,数字都是有正负之分的,所以在数字的二进制当中有一个二进制位被称为“符号位”,并且这个“符号位”在所有二进制位的最左边,0表示正数,1表示负数。

*byte类型最大值:01111111~

---字符型 单引号括起来,里面只能有一个字符

一个中文占用2个字节,所以char可以存储一个中文字符

数据类型之转义字符 \(课本P19)

转义字符出现在特殊字符之前,会将特殊字符转移成普通字符

在控制台上输出一个普通的单引号字符

//char a = ' ' java中不允许这样编写程序,编译报错

char a = ' ' '; 报错 第一个单引号和第二个配对,第三个单引号找不到另一半

正确用法:

char a  = '\'';
System.out.println(a);

输出"HelloWorld!"

System.out.println("\"HelloWorld!\"");

//JDK中自带的native2ascii.exe命令,可以将文字转换成unicode编码形式

---怎么使用这个命令:

在命令行输入native2ascii,回车,然后输入文字之后回车即可得到Unicode编码

char n = '\u4e2d';   //中国的中对应的Unicode编码是  4e2d   \u转义。
System.out.println(n);

\u联合一串数字是某个文字的Unicode编码

关于Java语言中的整数型 byte short int long

1.java语言当中的”整数型字面值“被默认当作int类型来处理。要让这个”整数型字面值“被当作long类型来处理的话,需要在”整数型字面值“后面添加I/L,建议使用大写的L

2.java语言中的整数型字面值有三种表示方式:

1、十进制「是一种缺省默认的方式

2、八进制「在编写八进制整数型字面值的时候需要以0开始」

3、十六进制「在编写十六进制整数型字面值时需要以0x开始」零

数据类型强制类型转换(窄化操作)

大转小:

char a=(long) b;
//关于double
float f1 = 1234.56//语法报错,因为系统默认带小数点的数据类型为double类型
float f1 = (float)1234.56;
float f1 = 1234.56f;

运算符

普通运算符

浮点数无法精确存放

    public static void main(String[] args) {
        double d = 10.12;
        double res = d % 3;
        System.out.println(res);  //res = 1.1199999999999992
    }//返回值应该为1.12,由于浮点数不能精确存放,所以返回值是一个类似值

求余计算中,负数符号位不参与计算

10./3=3.333... 10/3=3

除法分母不能为0;

%求余运算

*单目运算符 ++ --

i++ i = i+1

i-- i = i-1

i++是先取值后加1 ++i是先加1后取值

int k = 1;
int res = k+++k+++k;
System.out.println(k);  // k=3
System.out.println(res);   //res = 6  1+2+3

需求:用户输入身高和体重,计算出BMI值

import java.util.Scanner;
public class test9 {
​
    public static void main(String[] args) {
     //键盘输入身高体重
        System.out.println("请输入身高(单位为米):");
        Scanner sc = new Scanner(System.in);
        double height = sc.nextDouble();
        System.out.println("请输入体重(单位为千克):");
        double weight = sc.nextDouble();
    //算法问题  
    double bmi = height / weight /weight;
    System.out.println("您的bmi的指数为:"+bmi);
    }
​
}
​

赋值运算符*

/*
 * 重要结论:扩展类的赋值运算符不改变运算结果类型,假设最初这个变量的类型是byte类型,无论怎么进行追加或者追减,最终
 * 该变量的数据类型还是byte类型
 * */
​
​
public class OperatorTest01{
    public static void main (String[] args) {
        //基本的赋值运算符
        int i = 5;
        //i= i + 5;
        i += 5;   //+=可以翻译为“追加、累加” 
        System.out.println(i);
        i -= 5;   //等同于i = i-5;
        System.out.println(i);
        i *= 5;  
        System.out.println(i);  //25
        i /= 5;  
        System.out.println(i);  //5
        i %= 5;
        System.out.println(i);  //0
        //----------------------------------
        //10没有超出byte取值范围,可以直接赋值
        byte b = 10;
        //编译错误   因为编译器只检查语法,不运行程序,编译器发现b+5的类型是int类型,而b是byte类型,
        //b = b + 5;
        //编译正确
        b = (byte)(b +  5);
        System.out.println(b);
        
        byte x = 10;
        x += 5;  //等同于 x = (byte)(x+5);    不等同于 x = x+5;
        System.out.println(x);
        
        byte y = 0;
        y += 128;   //等同于 y = (byte)(y + 128);
        System.out.println(y);  //-128  [精度损失]
    }
} 

注意:+= 符号可以自动转换类型

k++也可自动转换类型

k=k+1 不能自动转换

*字符串连接运算符

/*
 * 关于java中的+运算符:
 *   1.+运算符在Java语言当中有两个作用:
 *         *加法运算,求和
 *         *字符串的连接运算
 *    2.当“+”运算符两边的数据是数字的话,一定是进行加法运算。
 *    3.当“+”运算符两边的数据只要有一个数据是字符串,一定会进行字符串运算。并且,连接运算之后的结果还是一个字符串类型
 *    4。在一个表达式当中可以出现多个+,在没有添加小括号的前提下,遵循自左至右的顺序依次执行运算
 */

public class OperatorTest02{
	public static void main(String[] args) {
		int a  = 10;
		int b = 20;
		System.out.println(10 + 20 );//这里的加号是求和
		System.out.println(10+20+"30");   //输出3030
		//要求在控制台上输出“10+20=30”
		System.out.println("10 + 20 = " + a + b);  //输出1020
		System.out.println("10 + 20 = " + (a + b)); //输出30
		System.out.println(a + "+ b" + "=" + (a + b));  //输出 10 + b =30;
//		a = 1100;
//		b = 299;
		System.out.println(a + "+" + b + "=" + (a  + b));
	}
}

*关系运算符 > < >= <= == !=

用于判断两个数据之间的大小关系,计算结果为boolean,如果成立返回为true,不成立返回为false

注意:由于浮点数无法精确存放,所以判断k==1.0这个写法是错误的 正确写法: a - b 的绝对值小于1e-6

java提供了一个工具类Math,其中包含了一个abs()的方法实现求绝对值 Math.abs(d1-1)<1e-6 判断浮点类型的变量d1的值是否为1

*三目运算符/三元运算符 ? :

*(逻辑运''算符&& || ! ) 短路计算

&& 与 同真则真,其余为假

|| 或 同假则假

//左边为假,右边为假,结果为假,循环结束后,age自增1,所以结果为101
public static void main(String[] args) {
		int age  = 100;
		char cc='A';
		if(cc>='B' || age++<=200) {
			System.out.println("zzzzzz");
		}
		System.out.println(age);  //age=101
	}
//左边为假,右边为真,结果为假,短路,age++不执行
public static void main(String[] args){
	int age = 100;
	char cc = 'A';
	if(cc>='B' &&  age++<200 ){
	System.out.println("zzzzzz");
	}
	System.out.println(age);//输出100,因为短路的原因,导致age++没有得到执行
}

逻辑非: !布尔表达式 !true !false

关于& (按位与)

如果左右两边都是布尔类型,那么也相当于逻辑运算

和&&的区别:这里不能进行短路求值

位运算符:

&按位与 有0则0

|按位或 有1则1

^ 按位异或 对应位相同为0,不同为1

0和任何数异或,结果为任何数

按位取反 1变0,0变1

移位运算符

左移 <<

0000 1011  11
0000 1011 << 1   0001 0110  22   11*2^1   左移是右边补0
0000 1011 << 2   0010 1100  44   11*2^2

右移>>

0000 1011 >> 1  0000 0101  5  11/2^1  右移是补符号位  正数补0,负数补1
0000 1011 >> 2  0000 0010  2   11/2^2
>>> 无符号右移   统统左边补0
没有无符号左移这个概念

女朋友生气的时候不能讲道理,当女朋友不开心的时候她就是规则,你就要想想你女朋友为什么不开心。 例如:博哥媳妇生气的时候,博哥会在旁边傻笑,或者买点她喜欢吃的东西

如果是异地恋 ,最好的情况是当你在学习的时候,要让你的男朋友也在学习,这样大家都在忙的时候就不会有时间吵架,而是共同成长

常见数学计算:**

*Math.sqrt();计算平方根

int k = 16;
double res = Math.sqre(k);
System.out.println(res);

*Math.pow(a,b) 计算a的b次方

*Math.max(a,b)计算最大值

*Math.min(a,b)计算最小值

*Math. abs() 计算绝对值

*Math.ceil()天花板的意思,就是返回大的值;floor的地板的意思

四、结构化编程

任何复杂的问题都可以用三种基本算法结构来描述:顺序,选择,循环

条件分支:if switch

if:

package come2;
import java.util.Scanner;
public class test02 {
	public static void main(String[] args) {
	//要求输入成绩,如果>85,显示优秀,如果>70,显示良好,>60,及格,否则不及格
	Scanner sc = new Scanner(System.in);
	System.out.println("请输入成绩");
	int score=sc.nextInt();   //从键盘上获取数值
	if(score<0 || score>=100 ) {
		System.out.println("输入成绩不合法");
	}else {
		if(score>=85) {
			System.out.println("优秀");
		}else if( score>=70) {
			System.out.println("良好");
		}else if(score>=60) {
			System.out.println("及格");
		}else {
			System.out.println("不及格");
		}
	}
	}
}

开关分支语句switch:

语法:switch(表达式)

case 1: ​ case 2: ​ ...... ​ break;

default: ​ ...... ​ break;

package come2;
import java.util.Scanner;
public class test03 {

	public static void main(String[] args) {
		// 需求:输入年月份,显示对应本月的天数
		System.out.println("请输入年份:");
		Scanner sc = new Scanner(System.in);
		int year = sc.nextInt();
		System.out.println("请输入月份:");
		int month=sc.nextInt();
		int days = 0;
		if(month >=12 || month < 1) {
			System.out.println("您输入的月份错误!");
		}else{
			switch(month) {
			case 1:
			case 3:
			case 5:
			case 7:
			case 8:
			case 10:
			case 12:
				days = 31;
				break;
			case 4:
			case 6:
			case 9:
			case 11:
				days=30;
				break;
				default:
				if(year%4==0 && year%100 !=0 || year % 400 ==0) {
					days = 29;
				}else {
					days = 28;
					break;
				}
			}
				System.out.println(year + "年" + month + "月有" + days + "天");
			}
		sc.close();
	}
}

循环

while循环 (未知循环次数时使用)

语法: while(条件){循环体;} 当条件成立时反复执行循环体,每执行一次判断一次条件

*break是立即终止循环,进入循环结构的后续代码继续执行,循环体执行结束

*continue是终止本次循环,进入下次循环,循环并没有执行结束

必须让while的条件执行慢慢的逼近于假,否则循环永远执行

do/while循环(不知道循环多少次,但至少循环一次)

do{循环体;}while{条件;}

先执行循环体,然后进行条件判断,如果条件为true,则执行下次循环;如果条件为false则终止循环

*不同于while循环的点:do/while循环至少执行一次循环体,而while循环有可能一次都不执行。

必须让while的条件执行慢慢的逼近于假,否则循环永远执行

for循环 (已知循环次数时使用)

for(表达式1;表达式2;表达式3){循环体;}

int res = 0;
	for(int i=1;i<101;i=i+2){
	res+=i;
	}
	System.out.println("1+3+5+....+99="+res);

练习题

1.计算输入数据的阶乘值

System.out.println("请输入整数:");
Scanner sc = new Scanner(System.in);
int kk = sc.nextInt();
int res = 1;
for(int k = 1; k<kk;k++){
	res*=k;
}
System.out.println(kk+"!="+res);

2.互换两个数的值(不允许使用中间变量)

//使用了中间值
package come2;
import java.util.Scanner;
public class test05 {

	public static void main(String[] args) {
		System.out.println("请输入一个数:");
		Scanner sc = new Scanner(System.in);
		int num1 = sc.nextInt();
		System.out.println("请输入第二个数:");
		int num2 = sc.nextInt();
		System.out.println("您输入的数据为("+num1+","+num2+"),更改后为:");
		int tmp = num1;
		num1 = num2;
		num2 = tmp;
		System.out.println("("+num1+","+num2+")");
		sc.close();

	}

}

//第一个数为10,第二个数为6
num1 = num1+num2;  //num1 = 16   num2 = 6
num2 = num1 - num2;
num1 = num1 - num2;

位运算法

3.输出三个数中的最大值和最小值

public static void main(String[] args) {
    //1.输入数字
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个数:");
        int a = sc.nextInt();
        System.out.println("请输入第二个数:");
        int b = sc.nextInt();
        System.out.println("请输入第三个数:");
        int c = sc.nextInt();
        //2.算法思想
        int max = 0;
        int min = 0;
        if(a>b) {
            max = a;
    else {
            max = b;
            }
        if(c>max)
            max = c;
        min = a<b?a:b;
        min = c<min?c:min;
        System.out.println("最大的数为:"+max);
        System.out.println("最小的数为:"+min);
    }

4.输出1-100的奇数(每行输出6个)

5.1-100求和(for while以及do/while的写法)

6.1-100奇数求和

7.1~100可以被三整除的数

8.求100以内所有能被3整除但不能被5整除的数

9.打印出所有水仙花数

10.判断一个数是否是质数

11.编程求出自然数101-205中的所有质数

12.输入两个正整数m和n,求其中最大公约数和最小公倍数

13.100~50000之间有多少整数,其各位数字之和为5分别是哪些数(例如1324的各位数字之和为1+3+2+4等于10(不为5),并统计满足条件的整数有多少个

衡量一个代码的优劣

1.正确性

2.可读性

3.健壮性

4.高效率与低存储:时间复杂度、空间复杂度(衡量算法的好坏)

五、数组

一维数组的使用

1>一维数组的声明和初始化

数组的使命是用来存储一组相同类型数据的数据的

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

3>如何获取数组的长度

System.out.println(array.length);
public class Test {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        for (int i = 0; i < array.length ; i++) {
            System.out.println(array[i]);
        }
        System.out.println("=========");
        for(int z : array){  //for-each  for(数组中每个元素的类型和变量 : 数组名)
            System.out.println(z);
        }
        System.out.println("==========");
        Arrays.sort(array); //给数组排序
        String ret = Arrays.toString(array);  //如果只打印,可以用这个方法
        System.out.println(ret);
    }
    

4>如何遍历数组

5>数组元素的默认初始化值

6>数组的内存解析

package shuzu;

public class test01 {

	public static void main(String[] args) {
	//1.一维数组的声明和初始化
		int num=10;
		int id = 1001;
		//静态初始化:数组的初始化和数组元素的赋值操作同时进行
		int[] ids;//声明
		ids = new int[] {1001,1002,1003,1004};//初始化
		//动态初始化:数组的初始化和数组元素的赋值操作分开进行
		String[] names = new String[5];
		
		//错误的写法
		//1.int[] arr1 = new int[];
		//2.int[5] arr2 = new int[5];
		
		//总结:
		//数组一旦初始化完成,其长度就确定出来了
		
		//2.如何调用数组的指定位置的元素:通过角标的方式调用
		//Java中的角标(或索引)从0开始,到数组长度-1结束
		names[0] = "大桥";
		names[1]="孙策";
		names[2]="刘健";
		names[3]="张三";
		names[4]="李四"; //只给了五个空间,所以最多只能建立五个
		
		//3.如何获取数组的长度
		//属性:length
		System.out.println(names.length); //5
		System.out.println(ids.length);  //4
		
		//4.如何遍历数组元素
		/*System.out.println(names[0]);
		System.out.println(names[1]);
		System.out.println(names[2]);
		System.out.println(names[3]);
		System.out.println(names[4]);*/
		
		for(int i = 0;i < names.length; i++) {
			System.out.println(names[i]);
		}
		
		//5.数组元素的默认初始化值
		/*	>数组元素是整形:0
			>数组元素是浮点型:0.0
			>数组元素是char类型:0或‘\u0000’  
			>数组元素是boolean型:false
			
			>数组类型是引用数据类型:null*/
		int [] arr = new int[4];
		for(int a = 0;a < arr.length;a++) {
			System.out.println(arr[a]); //0 0 0 0
		}
			short [] arr1 = new short[4];
			for(int a = 0;a < arr.length;a++) {
				System.out.println(arr1[a]); //0 0 0 0 
			}
				char [] arr2 = new char[4];
				for(int a = 0;a < arr.length;a++) {
					System.out.println(arr2[a]); //四个空格    
		}
				String[] arr3 = new String[4];
				System.out.println(arr[0]);
				
				//6.数组的内存解析 
	}
}

数组是引用类型

计算机中有五块内存:

java虚拟机栈、本地方法栈、方法区、堆、程序计数器

局部变量 内存就是在栈上开辟的

引用变量 会new一个对象,所以内存在堆上开辟

数组初始化 null是一个零号地址,是受保护的范围,所以当给数组初始化为null之后再给数组赋值会出现空指针异常

int[] array = null;  //array这个引用不指向任何对象,所以在书写的时候直接给赋值null

空指针异常:

原因:使用了一个值为null的引用

根据提示检查引用,看一下为什么是null。

通过其中任何一个引用,修改这个对象的值,另一个引用去访问的时候,也是会被改变的

 public static void main(String[] args) {
        int[] array1 = {1,2,3,4};    //把array1赋值给array2,此时就没有人引用这个对象了,就会被系统回收
        int[] array2 = {11,22,33,44};
        array1 = array2;             //两个数组指的是同一块内存地址
        array1[0]  = 1888;
        System.out.println(array1);   //1888 22 33 44
        System.out.println(array2);   //1888 22 33 44
    }
 public static void main2(String[] args) {
        int[] array = new int[100];
        for (int i = 0; i < 100; i++) {   //遍历数组
            array[i] = i + 1;
        }
        System.out.println(Arrays.toString(array));
    }
​
    public static void main3(String[] args) {
        int[] array1 = {1,2,3,4};    //把array1赋值给array2,此时就没有人引用这个对象了,就会被系统回收
        int[] array2 = {11,22,33,44};
        array1 = array2;             //两个数组指的是同一块内存地址
        array1[0]  = 1888;
        System.out.println(array1);   //1888 22 33 44
        System.out.println(array2);   //1888 22 33 44
    }
​
    //数组的拷贝
    //法一:
    public static void main4(String[] args) {
        int[] array = {1,2,3,4};
        //可以当扩容来用
        int[] copy = new int[array.length];
​
        //src:从哪里拷贝  srcPos:从拷贝数组的哪个位置开始拷贝
        //dest:拷贝到哪里  destPos:从拷贝数组的拷贝到哪个位置
        //length:就是拷多长
        System.arraycopy(array,1,copy,0,array.length-1);   //支持部份拷贝,可以指定区间拷贝
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(copy));
    }
    //法二:
    public static void main5(String[] args) {
        int[] array = {1,2,3,4};
        int[] ret = Arrays.copyOf(array,array.length);
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(ret));
        ret[0] = 199;
        System.out.println(Arrays.toString(array));  //1 2 3 4
        System.out.println(Arrays.toString(ret));   //199 2 3 4
    }
​
    public static void main6(String[] args) {
        int[] array1 = {1,2,3,4};
        int[] array2 = {1,2,3,4};
        //System.out.println(array1 == array2);   //错误写法
        System.out.println(Arrays.equals(array1,array2));   //比较两个数组里面的值是否相等,输出结果为true或者false
    }
​
 //数组克隆 (产生副本)
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4};
        int[] array2 = array1.clone();
        System.out.println(Arrays.toString(array1));
        System.out.println(Arrays.toString(array2));
        array2[0] = 199;
        //改变array2[0]的值,不会影响到array1的值    此时叫做深拷贝
        System.out.println(Arrays.toString(array1));  //1,2,3,4
        System.out.println(Arrays.toString(array2));  //199,2,3,4
    }
    

四个常用方法:sort() toString() fill() copyOf()

 //数组逆序
    public static void swap(int[] array,int i,int j){
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
​
    public static void reverse(int[] array){
        int left = 0;
        int right = array.length-1;
        while(left < right){
            swap(array,left,right);
            left++;
            right--;
        }
    }
​
    public static void main(String[] args) {
        int[] array = {6,5,4,4,2};
        reverse(array);
        System.out.println(Arrays.toString(array));
    }

一维数组的练习题:

package shuzu;
import java.util.Scanner;
//数组练习题
/*
 * 从键盘读取学生成绩,找出最高分,并输出学生成绩等级
 * 成绩>=最高分-10  等级为‘A’
 * 成绩>=最高分-20  等级为‘B’
 * 成绩>=最高分-30  等级为‘C’
 * 其余                    等级为‘D’
 * 
 * 提示:先读入学生人数,根据人数创建int数组,存放学生成绩
 * */
public class test02 {

	public static void main(String[] args) {
		//1.使用Scanner,读取学生个数
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入学生个数:");
		int number = sc.nextInt();
		//2.创建数组,存储学生成绩,动态初始化
		int[] scores = new int[number];
		//3.遍历数组,给数组中的元素赋值
		System.out.println("请输入"+number+"个学生成绩:");
		for(int i = 0;i < scores.length;i++) {
			scores[i] = sc.nextInt();     //给数组元素赋值	
		}
		//4.获取数组中元素的最大值:最高分
		int maxScore=0;
		for(int i = 0;i < scores.length;i++) {  //这一步的遍历可以和上一步的遍历合二为一
			if(maxScore < scores[i]) {
				maxScore = scores[i];
			}
		}
		//5.根据每个学生成绩与最高分的差值,得到每个学生的等级,并输出等级和成绩
	for(int i = 0;i < scores.length;i++) {
		if(maxScore - scores[i] <=10) {
			System.out.println("student" + i +"score is:"+scores[i]+",grade is “A");
		}else if(maxScore - scores[i] <=20) {
			System.out.println("student" + i +"score is:"+scores[i]+",grade is “B");
		}else if(maxScore - scores[i] <=30) {
			System.out.println("student" + i +"score is:"+scores[i]+",grade is “C");
		}else {
			System.out.println("student" + i +"score is:"+scores[i]+",grade is “D");
		}
	}
	}
}

多维数组的使用

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

2.1>一维数组的声明和初始化

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

3>如何获取数组的长度

4>如何遍历数组

5>数组元素的默认初始化值

6.>数组的内存解析

package shuzu;

public class test03 {

	public static void main(String[] args) {
		//1. 二维数组的声明和初始化
		int[] arr = new int[] {1,2,3};  //一维数组
		
		//二维数组静态初始化
		int[][] arr1 = new int[][] {{1,2,3},{4,5,6},{7,8}};
		int[] arr4[] = new int[][] {{1,2,3},{4,5,6,10},{7,8}};
		int[] arr5[] ={{1,2,3},{4,5,6},{7,8}};
		
		//二维数组动态初始化(两种方式)
		String[][] arr2 = new String[3][2];
		String[][] arr3 = new String[3][];    //二维中的元素个数不同
		
		//2.如何调用数组的指定位置的元素
		System.out.println(arr1[0][1]); //2
		System.out.println(arr2[1][1]); //null 
		
		arr3[1] = new String[4];
		System.out.println(arr3[1][0]);  //如果不写上一行代码就会报错
		
		//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.println(arr4[i][j] + " ");
			}
			System.out.println();
		}
		//5.二维数组的使用
		/*
		 * 规定:二维数组分为外层数组元素,内层数组元素
		 * eg:  int[][] arr = new int[4][3];
		 * 外层元素: arr[0],arr[1]等
		 * 内层元素:arr[0][0],arr[1][2]等
		 * */
	}
}

数组中常见的算法

1.数组元素的赋值(杨辉三角、回形数等)

2.求数值型元素中的最大值、最小值、平均数、总和等

3.数组的复制、反转、查找(线性查找、二分法查找)

 

4.数组元素的排序算法

比特之二维数组

  //二维数组的定义
    public static void main(String[] args) {
        int[][] array= {{1,2,3,4},{5,6,7,8}};
        for (int i = 0; i < 3; i++) {
            for(int j = 0;j < 4; j++){
                System.out.println(array[i][j]+" ");
            }
            System.out.println();
        }
    }
   //法二:
    public static void main(String[] args) {
        int[][] array = {{1,2,3,4},{5,6,7,8}};
        for(int i = 0;i<array.length;i++){
            for(int j = 0;j < array[i].length;j++){
                System.out.println(array[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println("======================");
        for(int[] ret : array){
            for(int x : ret){
                System.out.println(x+" ");
            }
            System.out.println();
        }
        System.out.println("=======================");
        System.out.println(Arrays.deepToString(array));
    }

2

六、方法的使用

1.方法的定义以及使用

一个方法从写好到用起来

1.定义方法,决定这个方法返回值为什么类型?方法名叫啥?形参有几个?什么类型?什么顺序?

2.使用这个方法,调用这个方法。方法名()->看一下有几个参数,都是啥类型?都是啥顺序

3.方法有返回值吗?要不要接收?拿什么类型接收?接收了返回值,我用返回值干啥?

方法结束后,add方法开辟的栈帧,就被系统回收了

总结:1.方法的调用是在栈上的

2.当方法遇到return或者遇到右括号代表当前方法结束了。对应方法开辟的栈帧回收了

//方法定义:
修饰符 返回值类型  方法名称 ([参数类型 形参 ...]){
 方法体代码;
 [return 返回值];
}

在Java中方法的位置灵活,可在main函数的上方,也可在下方

方法名采用小驼峰命名

该返回值为int类型,方法体中必须含有return;

public class Test {
    //计算两个数之和
    public static  int add(int a,int b){
        int c = a+b;
        return c;
    }
    
public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int ret1 = add(2,3);   //接收形参的值
        System.out.println(ret1);
        
        int p = ret1*5;
        System.out.println(p);
        
        int ret2 = add(a,b);   //这里只调用了方法,参数为主函数中定义的
        System.out.println(ret2);
    }

若返回类型为void(无返回值类型),方法体中不用写return; 主函数中也不需要接收参数,直接调用即可

  public static  void add2(int a , int b){
        System.out.println((a+b)*5);
    }
    public static void main(String[] args) {
        add2(1,2);
    }

注意:1。类型和个数,顺序都要匹配

2.--匹配!!!!

 //main方法求阶乘
    public static void main3(String[] args) {
        int sum = 0;
        for (int j = 0; j <= 7 ; j++) {
        int ret = 1;
            for (int i = 1; i <= j; i++) {
                ret *= i;
            }
            sum += ret;
        }
        System.out.println(sum);
    }

    //调用方法求阶乘
    public static  int facNum(int num){
            int sum = 0;
            for (int j = 1; j <= num ; j++) {
                int ret = 1;
                for (int i = 1; i <= j; i++) {
                    ret *= i;
                }
                sum += ret;
            }
           // System.out.println(sum);
            return sum;
        }

    public static void main4(String[] args) {
        int ret = facNum(5);
        System.out.println(ret);
    }

    //调用方法改进
    public static  int facNum1(int num){
        int sum = 0;
        for (int j = 1; j <= num ; j++) {
            int ret = fac(j);
            sum += ret;
            //sum += fac(j);
        }
        // System.out.println(sum);
        return sum;
    }

    public static  int fac(int n){
        int ret = 1;
        for (int i = 1; i <= n; i++) {
            ret *= i;
        }
        return ret;
    }

    public static void main(String[] args) {
        System.out.println(facNum1(5));
    }
    

2.掌握方法传参

在Java中,实参的值永远都是拷贝到形参中,形参和实参的本质是两个实体

   //两个数交换  做不到
    public static  void swap(int a,int b){
        int temp = a;
        a = b;
        b = temp;
    }
    
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        swap(a,b);
        System.out.println(a);
        System.out.println(b);
    }

做不出来这道题,因为形参的值不会影响实参的值,学习了类和对象之后可以解决

3.掌握方法重载

1.方法的名称必须相同

2.方法的参数列表顺序不同(顺序,个数,类型)

3.和返回值没有关系

   public static int sum(int a,int b ){
        return a+b;
    }
    public static int sum(int a,int b,int c ){
        return a+b+c;
    }
    public static double sum(double a,double b ){
        return a+b;
    }

4.掌握递归

自身中包含了自身

把原问题分解为小问题,然后小问题的解决方式和大问题的解决方式是一样的

递归满足两个条件:

1.自己调用自己

2.有一个终止条件(起始条件)

最难的地方在于如何确定这个递推公式

public static  int fac(int n){
        if(n == 0){
            return 1;
        }
        int tmp = n * fac(n-1);
        return tmp;
    }

    public static void main(String[] args) {
        System.out.println(fac(5));
    }

所有的递归过程都在栈上

//练习一:按顺序打印一个数字的每一位(例如:1234打印出1 2 3 4) 
public  static  void func(int n){
        if(n <= 9){
            System.out.println(n % 10);
            return;    //终止条件,必须要有
        }
        func(n/10);
        System.out.println(n % 10);
    }

    public static void main(String[] args) {
        func(1234);
    }
    
  //计算某一个数的阶乘
    public static int sum(int n){
        if(n == 1){
            return 1;
        }
        return n + sum(n-1);
    }

    public static void main(String[] args) {
        System.out.println(sum(10));
    }

循环写和递归写有什么区别

递归的好处:代码少

坏处:不好理解,不好书写

循环的好处:容易理解

坏处:代码多

递归浪费的空间的比较多,可能会将栈溢出

   //写一个递归方法,输入一个非负整数,返回组成他的数字之和。
    //例如:输入1729,则应返回1+7+2+9,它的和是19
    public static int func1(int n){
        if(n <= 9){
            return n;
        }
        return n%10 +  func1(n/10);
    }

    public static void main(String[] args) {
        System.out.println(func1(123));
    }
//斐波那契数列
    public static  int fib(int n){
        if(n == 1 || n == 2){
            return 1;
        }
       int ret =  fib(n-1)+fib(n-2);
        return ret;
    }

    public static void main(String[] args) {
        System.out.println(fib(5));
    }
 

七、类和对象

方法的定义

calss 类名{
	属性、成员属性 成员变量
	行为、成员方法
}
    class phone{
        public String brand;  //品牌          属性
        public String type;  //型号
        public double weight;  //重量
        //。。。。。。。。。。。。

        public void movie(){
            System.out.println("看电影");          //  动作
        }
        public void call(){
            System.out.println("打电话");
        }

    }

一般来说,一个Java文件只写一个public类

类:可以说是用户自定义的一个类型

一个类可以实例化多个对象,通过关键字new

注意事项:

类名注意采用大驼峰定义

成员前写法统一为public,后面会详细解释

此处写法的方法不带static关键字,后面会详细解释

课堂练习:定义一个狗类

public class Main {
    public static void main(String[] args) {
         Dog dog = new Dog();
         dog.color="黄色";
         dog.name="圆圆";
        System.out.println(dog.color+" "+dog.name);
​
        dog.barks();
        dog.eat();
    }
}
​
class Dog{
    //属性
    public String name;
    public String color;
​
    //行为
    public void eat(){
        System.out.println("狗狗吃东西");
    }
    public void barks(){
        System.out.println("汪汪汪");
    }
}

注意事项:

new关键字用于创建一个对象的实例

使用.来访问对象中的属性和方法

同一个类可以创建多个实例

类在方法区,不占内存

public class Test2 {
    public static void main1(String[] args) {
        Person1 person1 = new Person1();
        System.out.println(person1.age);
        System.out.println(person1.name); //此时name和age并没有赋值,但是此时不会报错,因为他们属于成员变量,所以这里编译器会给他们默认值
        //如果是引用类型,那么为null;int float 对应的0值;boolean默认值flase;char默认值'\u0000'
        person1.show();
    }

    public static void main2(String[] args) {
        //引用指向null代表person1不指向任何对象
        Person1 person1 = null;
        person1.name = "张三";
        person1.age = 19;
        person1.show();//空指针异常
    }

    public static void main3(String[] args) {
        //引用可以指向引用吗?
        Person1 person1 = new Person1();
        Person1 person2 = new Person1();
        person1 = person2;
        //不能
        //这个代表person这个引用指向了person2这个引用的对象

    }

    public static void main(String[] args) {
        //一个引用能不能同时指向多个对象
        Person1 person1 = new Person1();
        person1 = new Person1();
        person1 = new Person1();
        person1 = new Person1();
        person1 = new Person1();  //最后person1只是指向了一个对象
        //不能!!!
    }
}

class Person1{
    public String name;
    public int age;

    public void eat(){
        System.out.println("吃东西!");
    }
    public void show(){
        System.out.println("姓名:"+name+"年龄:"+age);
    }
}

this引用

public class DateUtil {
    public int year;   //成员变量
    public int month;
    public int day;

    public void setDate(int y,int m,int d ){
        year = y;
        month = m;
        day = d;
    }

    public void show(){
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args) {
        DateUtil dateUtil = new DateUtil();
        dateUtil.setDate(2022,4,8);
        dateUtil.show();
    }
}

this引用调用的是成员方法的对象

this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型

this只能在“成员方法”中使用

在“成员方法”中,this只能引用当前对象,不能再引用其他对象

this还有另外两种用法:

this访问构造方法

this访问成员方法

对象的构造和初始化

什么是构造方法?

构造方法非常特殊,这个方法没有返回值,而且方法名和类名一致

(public) DateUtil(){
	 System.out.println("不带参数的构造方法,这个方法如果没有写,Java会提供一个这样的方法");
}

如果自己写了就会调用自己写的,自己没写就会自动调用默认的,不带参数的方法 只会被调用一次

方法的重载

 public DateUtil(int y,int m,int d){
        this.year = y;    //加上this来表示给当前的对象的属性赋值
        this.month = m;
        this.day = d;
        System.out.println("调用了带有3个参数的构造方法!");
    }
    public DateUtil(){
        System.out.println("不带参数的构造方法,这个方法如果没有写,Java会提供一个这样的方法");
    }

不可以加void,不然就不叫构造方法了

当我们调用完成构造方法,那么对象就生成了

构造方法的步骤:

1.为对象分配内存

2.调用合适的构造方法

this访问构造方法:

 public static void main(String[] args) {
        DateUtil dateUtil = new DateUtil();
        dateUtil.show();
    }
    
    public DateUtil(int y,int m,int d){
        this.year = y;    //加上this来表示给当前的对象的属性赋值
        this.month = m;
        this.day = d;
        System.out.println("调用了带有3个参数的构造方法!");
    }
    public DateUtil(){
        this(1999,4,8);  //this调用当前对象的其他构造方法
        //1.this只能放在第一行
        //2.只能在构造方法内部才能使用
        System.out.println("不带参数的构造方法,这个方法如果没有写,Java会提供一个这样的方法");
    }
      public void show(){
        System.out.println(year+"年"+month+"月"+day+"日");
    }
    
    //输出:调用了带有3个参数的构造方法!
不带参数的构造方法,这个方法如果没有写,Java会提供一个这样的方法
1999年4月8日

不能形成环:

public Date(){
this(1900,1,1);//1
}

public Date(int year,int month,int day){
this();   //2
}
//1和2只能写一个

this访问成员方法

   public DateUtil(int y,int m,int d){
        this.show();
        this.year = y;    //加上this来表示给当前的对象的属性赋值
        this.month = m;
        this.day = d;
        System.out.println("调用了带有3个参数的构造方法!");
    }

this.data:访问当前对象的成员变量(成员变量是指:在类的内部,方法的外部定义的变量)

this.func():访问当前对象的成员方法------>this不能调用静态的成员方法和属性

this():调用当前对象的其他构造方法

//整合代码
import java.util.Date;

public class DateUtil {
    public int year;
    public int month;
    public int day;

    public void setDate(DateUtil this,int y,int m,int d ){  //DateUtil this是隐式参数,每个成员方法第一个参数默认是this
        this.year = y;    //加上this来表示给当前的对象的属性赋值
        this.month = m;
        this.day = d;
    }

    public DateUtil(int y,int m,int d){
        this.show();
        this.year = y;    //加上this来表示给当前的对象的属性赋值
        this.month = m;
        this.day = d;
        System.out.println("调用了带有3个参数的构造方法!");
    }
    public DateUtil(){
        this(1999,4,8);  //this调用当前对象的其他构造方法
        //1.this只能放在第一行
        //2.只能在构造方法内部才能使用
        System.out.println("不带参数的构造方法,这个方法如果没有写,Java会提供一个这样的方法");
    }
    public void show(){
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args) {
        DateUtil dateUtil = new DateUtil();
        dateUtil.show();
    }
    public static void main2(String[] args) {
        DateUtil dateUtil = new DateUtil(2022,11,11);
        dateUtil.show();
    }

    public static void main1(String[] args) {
        DateUtil dateUtil = new DateUtil();    //实例化对象,此时会调用对象的构造方法
        dateUtil.setDate(2022,4,8);
        dateUtil.show();

        DateUtil dateUtil2 = new DateUtil();    //方法前面是哪个引用就调用哪个对象的方法
        dateUtil2.setDate(2022,5,8);
        dateUtil2.show();

        DateUtil dateUtil3 = new DateUtil();
        dateUtil3.setDate(2022,5,8);
        dateUtil3.show();

        //静态方法里面不能用this,会报错
       // System.out.println(this.day);
    }
}

成员变量的生命周期:对象创建出生,对象销毁就结束

局部变量的生命周期:进入方法创建,出方法结束

八、封装

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

什么是封装?封装有什么意义?

--- 封装是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互 ( 把类的实现细节进行了隐藏,对外只提供一些交互的接口就可以)

访问修饰限定符--->访问权限

目前已知的权限有:

private public protected 默认权限

每种权限 到底是什么样的权限?

private 被修饰之后,只能在当前类当中才能使用

class Student{
   // private String name;  //被修饰之后,只能在当前类当中才能使用
    public String name;
    public int age;
​
    public void show(){
        System.out.println("姓名:"+name);
    }
}
public class Test {
    
    public static void main(String[] args) {
    Student student = new Student();
    student.name="xtx";
    student.show();
    }
}

一般情况下,成员变量是private,成员方法是public 但这不是固定的

包的概念

包是对类、接口等的封装机制的体现,是一种对类或接口等的很好的组织方式

在一个工程中,允许存在相同名称的类,只要在不同的包中即可

import java.util.*;   *是通配符,如果有两个以上的类在util包里面,就可以简写
*不是把里面所有的导进来,而是用啥取啥
import java.util.*;
import java.sql.*;
​
 Date date = new Date();   //报错,因为util和sql里面都有Date类,这个时候编译器不知道用哪个里面的类
 java.util.Date date = new java.util.Date();  //正确写法。手动导包

自定义包

在src下右键选择new下的package

package com.bite.www;   //package声明当前类是在哪个包底下

public class TestDemo {

    public static void main(String[] args) {
        com.bite.www2.TestDemo testDemo = new com.bite.www2.TestDemo();
    }
}

包的访问权限:

private:同一个包的同一个类

default 同一个包的同一个类,同一个包的不同类

protected:同一个包的同一个类,同一个包的不同类,不同包中的子类

public:哪里都能访问

import java.util.*;
import java.sql.*;

class Student{
   // private String name;  //被修饰之后,只能在当前类当中才能使用
    public String name;
    public int age;

    public void show(){
        System.out.println("姓名:"+name);
    }
}
public class Test {

    public static void main(String[] args) {
    Student student = new Student();
    student.name="xtx";
    student.show();

        java.util.Date date = new java.util.Date();
    }
}

private

class Person{
    //name和eat这两个方法都已经被封装起来了,使用private关键字进行了封装,此时,name的权限就变小了。
    //它只能在当前类中被访问
    private String name;
    public int age;

    //构造方法:
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Person(String name){
        this.name = name;
    }
    public String getName(){    //提供公开的接口
        return this.name;
    }
    public void setName(String name){
        this.name=name;
    }
    public Person(){

    }


    private void eat(){
        System.out.println("吃饭!");
    }
    public void show(){
        System.out.println(name+" "+age);
    }
}
public class Test {

    public static void main(String[] args) {
        Person person = new Person("zhangsan",10);
        person.setName("xutianxin");
        person.show();
        System.out.println(person.getName());
//        person.name = "xtx";
//        person.eat();
    }
}

static静态成员变量

在Java中,被static修饰的成员,称之为静态成员,也可以称之为类成员,其不属于某个具体的对象,是所有对象所共享的

成员变量:

1.静态成员变量 /类变量或者类成员

2.非静态成员变量/普通成员变量

class Student{
    private String name;
    private int age;
    private static String classRoom = "107期";

    //构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void doClass(){
        System.out.println(name+" "+"在上课");
    }
}

public class Test2 {
    public static void main(String[] args) {
        Student student1 = new Student("xtx",18);
        Student student2 = new Student("xtx1",17);
        Student student3 = new Student("xtx2",16);
        student1.doClass();
    }
}
class Student{
    private String name;
    private int age;
    public static String classRoom = "107期";

    //构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void doClass(){
        System.out.println(name+" "+"在上课");
    }
}

public class Test2 {
    public static void main(String[] args) {
        System.out.println(Student.classRoom);   //静态的成员变量不属于对象,所以不用通过对象的引用来访问,直接可以通过类名访问
    }
}

static静态成员方法

成员方法:

静态成员方法/类方法

非静态成员方法

//在静态方法的内部不能使用非静态的数据成员
//只要是非静态的数据成员,都需要通过对象的引用去访问它
public static void func(){
System.out.println(this.name);  //静态方法里面不能使用this
System.out.println(name);
System.out.println("staticFunc()");
}//eror
public static void func(){
    Student student = new Student();
    System.out.println(student.name);
System.out.println("staticFunc()");
}

我们建议:获取静态的成员变量或者是设置静态的成员变量,此时最好的方法是静态的,否则要是非静态的,还得实例化对象。

对于静态成员变量初始化:

1.直接赋值

2.默认初始化

3.可以通过提供get和set方法来进行初始化

4.在构造对象的时候可以在构造方法中进行赋值

5.通过代码块进行赋值

代码块

普通代码块 定义在方法内部的代码块

构造块 类里面,方法的外面

静态块

同步代码块

带有两个参数的代码块:

{
    name = "caocao";
    System.out.println("非静态代码块/实例化代码块/构造代码块!-》初始化非静态的数据成员")
}//一般不写实例化代码块,一般初始化不这样写

public Student(String name,int age){
this.name = name;
this.age = age;
System.out.println("带有两个参数的构造方法!")
}

不带参数的构造方法:

{
    name = "caocao";
    System.out.println("非静态代码块/实例化代码块/构造代码块!-》初始化非静态的数据成员")
}

public Student(){
System.out.println("不带参数的构造方法!")
}

public String name = "wusuowei";  //一般初始化这样写

{
    name = "caocao";
    System.out.println("非静态代码块/实例化代码块/构造代码块!-》初始化非静态的数据成员")
}//输出caocao
----------------------------------------------------------------------------------------
{
    name = "caocao";
    System.out.println("非静态代码块/实例化代码块/构造代码块!-》初始化非静态的数据成员")
}
public String name = "wusuowei";  //输出wusuowei

总结:如果都是非静态的,那么看定义顺序。谁在后,最后就是哪个值

编译器编译好代码后,会把非静态代码块的东西放到构造方法里面的最前面

static{
	System.out.println("静态代码块->初始化静态的数据成员/提前准备一些数据")
}

不管static静态代码块放在哪里,一定是先执行的

只要类被加载,静态代码块一定会执行,并且只被执行一次(不管写多少次)

先执行静态的,再执行非静态的,最后执行构造方法

对于你想输出一个对象的引用的值的时候,如果你没有自己写一个toString方法,那么就会调用Object这个类的方法。。。。如果自己写了,就调用自己写的

//自己写toString
class Test{
	public String toString(){
	.......................
	}
}

继承和多态

继承:

对共性进行抽取,从而实现代码的复用

eg:Dog extends Animal

子类 父类

class Animal {
    public String name;
    public int age;

    public void eat(){
        System.out.println(name+"正在吃饭");
    }
}

class Dog extends Animal{
    public void wangwang(){  //子类特有的方法
        System.out.println(name +"汪汪叫");
    }
}

class Cat extends Animal{
    public void miaomiao(){
        System.out.println(name +"喵喵叫");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "坦克";
        dog.wangwang();

        Cat cat = new Cat();
        cat.name = "圆圆";
        cat.eat();
    }
}

子类中访问父类的成员变量

class Base{
    public int a;
    public int b;
    public int c = 10;
}

class Derived extends Base{
    public int c = 100;

    public void func(){
        //子类有  那么就拿子类的  子类没有 就拿父类的
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);  //如果子类和父类有同名的成员变量,优先访问子类自己的
        System.out.println(super.c); //super暂且理解为代表父类的引用  
        //正确的理解:super只是一个关键字。最大的作用是写代码或者读代码的时候,体现出更好的可读性!
    }
}

public class Test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        System.out.println(derived.a);
        derived.func();   //c = 100
    }
}

super:

1.super.data 在子类当中访问父类的成员变量

2.super.func() 在子类当中访问父类的成员方法

注意:

只能在非静态方法中使用

在子类方法中,访问父类的成员变量和方法

重载(Overloading)和重写的区别

重载:一个类里面有两个方法,方法的形参不同,返回值类型不同

重写:有继承关系的两个类,具有相同的方法名,返回值类型,形式参数列表,子类修改父类的属性或方法

子类构造方法

在继承关系上,当我们在构造子类的时候,一定要先帮助父类进行构造

class Cat extends Animal{
    int size;
    public Cat(String name, int age,int size){
        super(name,age);   //初始化父类,如果不初始化,就报错
        this.size = size;  //初始化自己
    }

super在子类构造方法内,调用父类构造方法的时候,一定要放在第一行

     public Cat(String name, int age){
        super(name,age);   
        this("1",2,3)   //报错  super和this不能同时出现
    }

protect类

同一个包的同一个类,同一个包的不同类,不同包的子类

多态

  1. 向上转型(upcasting)(子转为父)

  2. 向下转型(downcasting)(父转为子),需要加强制类型转换符

  3. 注意:向下转型和向上转型的前提条件是两种类型之间必须存在继承关系,否则编译器报错

  4. 多态指的是父类型引用指向子类型对象,编译运行两种状态

    • 编译阶段:静态绑定父类的方法

    • 运行阶段:动态绑定子类型对象的方法

    • 多种形态:编译时候一种形态,运行时候另一种形态

  5. 必须向下转型的情况:访问的是子类对象中“特有”的方法。此时必须进行向下转型

编译阶段

对于编译器来说,编译器只知道一个变量的类型,只知道这个类型有哪些方法。编译器检查语法的时候,发现变量对应的类型有那个方法,所以编译通过。这个过程称为静态绑定方法

运行阶段

堆内存当中创建的Java对象是类的子类对象,所以调用方法的时候,真正执行的是子类对象的方法,所以运行阶段会动态执行子类对象的方法,这个过程属于运行阶段绑定。运行阶段绑定属于动态绑定

满足的条件:

1.必须在继承体系下

2.子类必须对父类方法进行重写

3.通过父类的引用调用重写的方法

向上转型

把子类对象给父类

eg.Animal animal = dog;

理论上来说,符号两边的数据类型必须一致,否则赋值会出错

当发生转型之后,此时通过父类的引用只能访问父类自己的成员。不能访问到子类特有的成员。

父类引用指向子类对象

动态绑定:

在编译时,不能确定方法的行为,需要等到程序运行时,才能具体确定调用哪个类

静态绑定:

在编译时,根据用户传递实参类型就确定了具体调用了哪个方法,典型代表:函数重载

当父类引用 引用的对象不一样的时候表现出的行为是不一样的

重写需要注意的点:

1.private修饰的方法不能被重写

2.static修饰的方法不能被重写

3.子类的访问修饰限定权限要大于等于父类的权限private < 默认的 <protect<public

4.被final修饰的方法不能被重写,此时这个方法被称作密封方法

优点:让代码实现更简单灵活

缺点:不能调用到子类特有的方法

向下转型

Dog dog = (Dog)animal1;
dog.wangwang();

父类的类型给到子类,但是向下转型非常的不安全

if(animal1 instanceof Bird){  //改正
Bird bird = (Bird)animal1;
bird.fly();
}

instanceof:判断animal1的引用是否指向Bird对象,如果指向了才能转型。

public class Test4 {
    public static void main(String[] args) {
        Animal1 animal1 = new Bird1();
        Animal1 animal2 = new Dog1();

        if (animal1 instanceof Dog1){
            Dog1 dog1 = (Dog1)animal1;
            dog1.eat();
        }
        if (animal1 instanceof Bird1){
            Bird1 bird1 = (Bird1)animal1;
            bird1.fly();
        }
    }
}
class Animal1{

}
class Bird1 extends Animal1{
    public void fly(){
        System.out.println("鸟儿在飞");
    }
}
class Dog1 extends Animal1{
    public void eat(){
        System.out.println("狗狗在吃饭");
    }
}
class Shape{
    public  void draw(){
        System.out.println("画圆形!");
    }
}

class Rect extends Shape{
    public void draw(){
        System.out.println("画矩形!");
    }
}

class Cycle extends Shape{
    public void draw(){
        System.out.println("画圆形!");
    }
}

class Flower extends Shape{
    public void draw(){
        System.out.println("❀");
    }
}
public class Test3 {
    public static void drawMap(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();

        drawMap(rect);
        drawMap(cycle);
        drawMap(new Flower());
    }
}
public class Test3 {
    public static void drawMap(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Flower flower = new Flower();

        Shape[] shapes = {cycle,rect};

        for (Shape shape: shapes) {
            shape.draw();
        }
    }
}

避免在构造方法中调用重写的方法

注意:

当在父类的构造方法当中去调用父类和子类重写的方法的时候,会调用子类的。

抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象,如果一个类没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

1.抽象类使用abstract来定义类

2.抽象类不能实例化对象

3.此时在抽象类当中,可以有抽象方法,或者非抽象方法

4.什么是抽象方法,一个方法被abstract修饰,没有具体的实现。只要包含抽象方法,那么这个类必须是抽象类

5.当一个普通类继承了这个抽象类,必须重写抽象类中的抽象方法

6.抽象类存在的最大意义就是为了被继承

7.抽象方法不能被private,final,static修饰,所以一定要满足重写的规则

8.当一个子类没有重写抽象的父类的方法,可以把当前子类变为abstract修饰

9.抽象类当中不一定包含抽象方法

抽象类和普通类有什么区别?

1.抽象类不能被实例化

2.抽象类当中,可以包含非抽象方法和抽象方法,但是普通类只能包含非抽象方法!

abstract class Shape{
    public void draw(){
        System.out.println("画圆形!");
    }

    public abstract void func();
}

抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

抽象类的作用:

抽象类本身不能被实例化,想要使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法

九、接口

什么是接口?

1.使用interface关键字来定义接口

2.接口不能被实例化

3.接口中的成员变量默认都是public static final的

4.接口当中的方法不写也是默认为 public abstract的

5.接口当中的方法不能有具体的实现,但是从jdk8开始可以写一个default修饰的方法

6.接口当中不能有构造方法

7.接口需要被类实现使用关键字implements

8.接口当中可以有static修饰的方法!

interface IShape{
    public abstract void draw();
    //void draw();   //二者等价
}
​
class Rect implements IShape{
    public void draw(){
        System.out.println("矩形!");
    }
}
​
class Flower implements IShape{
    public void draw(){
        System.out.println("❀");
    }
}
​
public class Test {
    public static void drawMap(IShape shape){
        shape.draw();
    }
​
    public static void main(String[] args) {
   // IShape shape = new Ishape();   //接口不能实例化
     IShape shape = new Rect();  //向上转型
        IShape shape1 = new Flower();
​
        drawMap(shape);
        drawMap(shape1);
    }
}

接口使用:

接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口中的所有抽象方法

public class 类名称 implements 接口名称{
​
}

类可以继承一个普通类 类可以继承一个抽象类 类可以继承一个普通类/抽象类 同时实现多个接口 implements A,B

接口可以通过extends 拓展多个接口的功能

接口的使用案例

给对象数组排序 Comparable

package demo2;

import java.util.Arrays;

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public int score;

    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        if(this.age > o.age){
            return 1;
        }else if(this.age < o.age){
            return -1;
        }else
            return 0;

        //如果是姓名比较
//        if(this.name.compareTo(o.name) > 0){
//            return 1;
//        }else if(this.name.compareTo(o.name) < 0){
//            return -1;
//        }else
//            return 0;
    }
}
public class Test {
    public static void sort(Comparable[] array){
        for (int i = 0; i < array.length-1; i++) {
            for (int j = 0; j < array.length-1-i; j++) {
//                if(array[j] > array[j+1]){
//                    交换
//                }
                if(array[j].compareTo(array[j+1]) > 0){
                    Comparable tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
    }

    public static void main(String[] args) {
        Student[] student = new Student[3];
        student[0] = new Student("zhangsan",19,59);
        student[1] = new Student("lisi",20,69);
        student[2] = new Student("wangwu",4,83);

        //System.out.println(student[0].compareTo(student[1]));
        Arrays.sort(student);
        System.out.println(Arrays.toString(student));
    }
    public static void main1(String[] args) {
        int[] array = {1,5,3,8,10,4,3};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

接口间的继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口和接口之间可以多继承。即:用接口可以达到多继承的目的

Clonable接口和深拷贝

Object 类中存在一个clone方法,调用这个方法可以创建一个对象的拷贝,但是要想合法调用clone方法,必须要先实现Clonable接口,否则就会抛出CloneNotSupportedException异常

Clonable接口是一个空接口,它的作用就是表示当前对象是可以被克隆的。

1.对自定义类型进行拷贝

class Student implements Cloneable{
    public String name;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student();
        student1.name = "xtx";
        Student student2 = (Student)student1.clone();
        System.out.println(student1);
        System.out.println(student2);
    }
}

浅拷贝和深拷贝

浅拷贝:

深拷贝:克隆了一个文件,修改另一个文件不会影响另一个,这种就叫深拷贝

Object类

hashCode()

暂时理解为算了一个具体对象的位置

结论:1.Object类默认的hashcode()方法用来确定对象在内存中存储的位置是否相同。如果子类将hashCode方法重写,那么hashCode方法的返回值就是子类实例变量转成int类型整数的结果

2.事实上hashcode()在散列表中才能用,在其他情况下没用。在散列表中hashcode()的作用是捕获对象的散列码,进而确定该对象在散列表中的位置

十、内部类

  1. 实例内部类

  2. 静态内部类

  3. 局部内部类

  4. 匿名内部类(重点)

实例内部类

public class InnerClassTest {
    int a;
    int b;
    InnerClassTest(int a, int b){
        this.a = a;
        this.b = b;
    }
​
    private class InnerClass{
        int i1 = 1;
        int i2 = 2;
        int i3 = a;
        int i4 = b;
    }
​
    public static void main(String[] args) {
        InnerClass innerClass = new InnerClassTest(100, 200).new InnerClass();
        System.out.println(innerClass.i1);
        System.out.println(innerClass.i2);
        System.out.println(innerClass.i3);
        System.out.println(innerClass.i4);
    }
}
​
  • 第一:内部类可以使用private和protected修饰,外部类不可以

  • 第二:实例内部类要使用,必须先实例化外部类对象

  • 第三:实例内部类不能有静态声明(jdk1.8)

静态内部类

public class InnerClassTest {
    static int a = 100;
    int b = 200;
​
    static class InnerClass{
//        可以定义实例变量
        int i1 = 1;
        int i2 = 2;
//        可以定义静态变量
        static int i3 = 300;
//        可以引用外部类的静态变量
        static int i4 = a;
//        报错,不能直接引用外部类的实例变量
//        int i5 = b;
//        可以通过外部类的引用访问外部类的实例变量
        int i5 = new InnerClassTest().b;
    }
​
    public static void main(String[] args) {
        System.out.println(new InnerClassTest.InnerClass().i1);
        System.out.println(new InnerClassTest.InnerClass().i2);
        System.out.println(InnerClassTest.InnerClass.i3);
        System.out.println(InnerClassTest.InnerClass.i4);
        System.out.println(new InnerClassTest.InnerClass().i5);
    }
}
  • 第一:静态内部类对象创建时可以不用创建外部类对象

  • 第二:静态内部类可以直接访问外部类的静态变量

  • 第三:静态内部类如果要访问外部类的实例变量就需要实例化外部类,通过外部类对象的引用来访问

  • 第四:静态内部类可以声明实例成员也可以声明静态成员

局部内部类

public class InnerClassTest {
    private int a = 200;
    
//    局部变量在内部类中使用必须加final关键字修饰
    public void method(final int temp){
        class InnerClass{
            int i1 = 100;
//            可以访问外部类的成员变量
            int i2 = a;
            int i3 = temp;
        }
//        只能在局部内部类所在的方法体内使用内部类
        InnerClass innerClass = new InnerClass();
        System.out.println(innerClass.i1);
        System.out.println(innerClass.i2);
        System.out.println(innerClass.i3);
    }
​
    public static void main(String[] args) {
        InnerClassTest innerClassTest = new InnerClassTest();
        innerClassTest.method(300);
    }
}
​
  • 第一:局部内部类使用外部类的局部变量时,外部类的局部变量需要使用final修饰

  • 第二:在局部内部类中可以访问外部类的成员变量

  • 第三:局部内部类只在当前方法有效,方法结束后局部内部类就消失了

匿名内部类

public class InnerClassTest {
    public static void main(String[] args) {
//        没有用匿名内部类
        MyInterface m1 = new MyInterfaceImpl();
        m1.add();
//        用了匿名内部类
        MyInterface m2 = new MyInterface() {
            @Override
            public void add() {
                System.out.println("add...");
            }
        };
        m2.add();
    }
}
interface MyInterface{
    void add();
}
class MyInterfaceImpl implements MyInterface{
​
    @Override
    public void add() {
        System.out.println("add...");
    }
}
​
  • 第一:匿名内部类可以直接new接口,在大括号里写具体实现

  • 第二:上面这种写法可以使用lambda表达式替代

    MyInterface m2 = () -> System.out.println("add...");
    m2.add();

十一、String类

String类提供的构造方法有很多,例如以下三种:

public static void main(String[] args) {
        //1.使用常量串构造
        String s1 = "Hello bit";
        System.out.println(s1);
​
        //2.直接newString对象
        String s2 = new String("Hello bit");
        System.out.println(s2);
​
        //3.使用字符数组进行构造
        char[] array = {'H','e','l','l','o','b','i','t'};
        String s3 = new String (array);
        System.out.println(s3);
    }

String中有两个成员变量

value和hash

value中存的一个数组

方法:

1.求字符串的长度: str1.length()

2.判空方法:str2.isEmpty()

String str2 = " "; //这个引用指str2对象指向的地址里面为空

String str3 = null; //指这个引用不指向任何对象

String类的比较

两种场景:1.相不相同 2.大小

   public static void main(String[] args) {
        String str1 = new String("Hello");
        String str2 = new String("Hello");

        System.out.println(str1 == str2);   //false
       //比较两个引用所指向的对象当中的内容是否相同
        System.out.println(str1.equals(str2));   //true  
    }

按照字母顺序比较大小

str1 > str2 返回正数

str1 < str2 返回负数

str1 = str2 返回0

  public static void main(String[] args) {
        String str1 = new String("Hello");
        String str2 = new String("Healo");

        System.out.println(str1.compareTo(str2));   //11
      
     //忽略大小写进行比较
      System.out.println(str1.compareToIgnoreCase(str2));  
    }

十一、异常

有继承关系

都继承了Throwable这个类

Throwable 下有两个类:Exception,RuntimeException

编译时期/受查 运行时期/非受查

异常的捕获:

异常处理的五个关键字:

try,catch,finally,throw,throws

注意事项:

异常一旦抛出,其后的代码不会执行

finally

一般我们不建议在finally中写return(被编译器当作一个警告)

面试题:

1.throw和throws的区别?

throw是抛出一个异常 throws是声明一个异常

2.finally中的语句一定会执行吗?

答:一定会执行

异常处理

  1. Exception的直接子类是受控异常

  2. Exception的子类RuntimeException的子类是非受控异常

  3. 异常对象的getMessage()方法输出结果是创建异常对象时传入的参数

  4. 异常对象的printStackTrace()方法输出异常堆栈信息,帮助定位发生异常的位置排查错误

  5. throw关键字是抛出异常对象

  6. throws关键字是写在方法定义位置上,向调用者抛出

  7. try...catch语句块中,try发生异常,后续代码不执行,直接进入对应的catch代码块中

  8. 如果没有catch异常,发生异常时候后续代码将不会执行

  9. finally代码块一定会执行,并且在return之前执行

  10. 如果有多个catch将自上而下匹配,所以写的时候异常从小到大写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值