Java学习笔记

Java学习笔记
LiSir第一章 java基础
标识符的命名规则
1 | / /T0D0标识符得命名规则 |
数据存储单位
1.比特(bit位):数据运算的最小存储单位。
2.字节(byte):数据的最小存储单位。
字节和比特之间可以互相转换的,关系如下
1byte = 8bit
数据类型
基础数据类型
一共4类8种,分别如下
整数数据类型:byte,short,int,long分别是8位,16位,32位,64位,整型常量类型默认为
int类型浮点类型:含有小数点的数据类型;float(单精度浮点类型),double(双精度浮点类型)。,浮点型常量默认为
double类型注意:如果float定义时,没有加
f则会被认为在定义double类型,从而出现类型转换的错误。1
2
3//单精度浮点类型,我们需要用F(f)来结尾如
float f = 2.0f;
double d = 12.3;字符类型:char类型代表一个单一的字符,进行运算时会被转为ACSII码进行运算,用单引号定义。
1
char c = '@';
布尔类型:boolean类型:判断条件是否成立,成立取值true,不成立取值false,该类型不能做运算,可以与字符串类型进行拼接。
1
boolean bln = true;
引用数据类型
引用数据类型:类 数组 接口 枚举 注解
就比如最常用的String类型就是引用类型。String可以用来声明包含0个,1个,等多个字符的字符串;
自动类型提升
规则:当容量小的变量与容量大的变量做运算时,结果自动转换为容量大的数据类型。(这里的容量指表示数据的范围。)
下面这条线,从左到右是自动类型提升,从右往左是强制类型转换。
byte、short、char ——> int ——-> long ——> float ——->double
特别的:byte、short、char类型的变量直接做运算,结果为int类型。
强制数据类型转换
在java中范围小的数据可以直接转换为范围大的数据,但是范围大的数据不能转换成范围小的数据,如果非要大类型转小类型,那么我们应该使用小括号进行强制类型转换
1 | byte b =10; |
运算符
就是参与数据运算的符号,Java定义,无法自行定义
算数运算符
二元运算符:两个元素参与运算的运算符 1 + 2
算术表达式 = 元素1 二元运算符 元素2 (表达式是有结果的,就需要有类型,这里的类型是元素种类类型最大的那一种,最小使用的类型为int类型)
1
2
3
4
5
6System.out.println(1 + 2) //3
System.out.println(2 - 2) //0
System.out.println(1 * 2) //2
System.out.println(4 / 2) //2 (int,int) ==>int结果类型取决于类型大的那个
System.out.println(1.0 / 2) //0.5 (double,int) ==>double,结果类型取决于类型大的那个
System.out.println(5 % 2) //1(取余数,模运算)一元运算符:一个元素参与的运算符(
++,--)++:本质是加1操作的简化版本,也成为自增运算符,注意,++i,表示i先自加1随后在进入下面的运算,而i++表示i先参加运算后再加1--:本质是减1操作的简化版本,也成为自减运算符,注意,–i,表示i先自减少1随后在进入下面的运算,而i–表示i先参加运算后再减1
赋值运算符
1 | public class OperatorDemo3 { |
逻辑运算符和位运算符
逻辑运算符用于组合条件表达式,并基于这些条件表达式的真假值来决定最终的结果。
&& (逻辑与) - “且”的关系
描述: 当两个操作数都为
true时,结果才为true。该运算符表达”且”的关系、特点:
- 短路行为: 如果第一个操作数为
false,则不会评估第二个操作数。
- 短路行为: 如果第一个操作数为
示例:
1
2
3boolean a = true;
boolean b = false;
boolean result = a && b; // 结果为 false
&(按位与) - “且”的关系
描述:按位与 (
&) 用于整数类型(如byte、short、int和long),它按位进行操作。对于两个整数,按位与运算符会比较它们每一位上的二进制位,如果两个位都为1,那么结果位也为1;否则结果位为0。特点
- 当两个对应位都是
1时,结果位为1;否则结果位为0。 - 没有短路行为。
- 当两个对应位都是
示例:
1
2
3int a = 5; // 二进制表示: 0101
int b = 3; // 二进制表示: 0011
int result = a & b; // 结果为 1 (二进制: 0001)
|| (逻辑或) - “或”的关系
描述: 逻辑或 (
||) 用于连接两个布尔表达式,并且只要这两个表达式中有任何一个为true,整个表达式的结果就为true。特点:
||具有短路行为,这意味着如果第一个表达式为true,那么第二个表达式将不会被计算。示例:
1
2
3boolean a = true;
boolean b = false;
boolean result = a || b; // 结果为 true
|(按位或) - “或”的关系
描述:按位或 (
|) 用于整数类型(如byte、short、int和long),它按位进行操作。对于两个整数,按位或运算符会比较它们每一位上的二进制位,如果两个位中至少有一个为1,那么结果位也为1;否则结果位为0。特点:
- 当两个对应位中至少有一个为
1时,结果位为1;否则结果位为0。 - 没有短路行为。
- 当两个对应位中至少有一个为
示例
1
2
3int a = 5; // 二进制表示: 0101
int b = 3; // 二进制表示: 0011
int result = a | b; // 结果为 7 (二进制: 0111)
! (逻辑非)
描述: 对操作数的布尔值取反。
示例:
1
2boolean a = true;
boolean result = !a; // 结果为 false
位运算符
位运算符在Java中用于按位操作整数类型的数据(如 byte、short、int 和 long)。位运算符允许你直接对这些数据类型的二进制位进行操作。
1. & (按位与)
- 当两个对应位都是
1时,结果位才是1;否则结果位为0。
2. | (按位或)
- 当两个对应位中至少有一个是
1时,结果位为1;否则结果位为0。
3. ^ (按位异或)
- 当两个对应位不同时,结果位为
1;相同则结果位为0。
4. ~ (按位取反)
- 对每个位进行取反操作,
1变为0,0变为1。
5. << (左移)
- 将二进制位向左移动指定的位置数,高位丢失,低位补零。
6. >> (带符号右移)
- 将二进制位向右移动指定的位置数,高位补符号位(如果是负数,则补
1;如果是正数,则补0)。
7. >>> (无符号右移)
- 将二进制位向右移动指定的位置数,高位补
0。
三元运算符 - 条件运算符
语法:
变量 = (条件表达式)?(任意表达式1):(任意表达式2)
1 | public class OperatorDemo6 { |
比较运算符和比较方法
==是一个比较运算符- ==:既可以判断基本数据类型,也可以判断引用数据类型。
- ==:如果判断基本数据类型,比较的是值是否相等,如果判断的是引用数据类型,则判断的是地址值是否相等
equals是Object类中的一个方法- 只能判断引用类型
- 默认判断的是地址是否相等,但是使用中通常会在子类中重写该方法,用来判断内容是否相等
流程控制
顺序执行
- 按照代码顺序从前往后执行
分支执行
- if……else……
- if……elif……….elif……..else……..
- switch(变量){ case1: ……. ; case2: ……… ;default: ………..;},switch分支判断也叫等值分支判断。
其中在switch分支中如果想要跳出某个case并结束分支执行,就在那个case块的内容最后加上break用来跳出
重复执行(循环)
在Java中,循环结构允许你重复执行一段代码直到满足某个特定条件。Java提供了几种不同类型的循环结构,包括for循环、while循环和do-while循环。下面是每种循环的基本语法和示例。
for 循环
for循环是最常用的循环结构之一,特别适合已知循环次数的情况。
语法
1 | for (初始化; 终止条件; 更新表达式) { |
例子:
1 | for (int i = 1; i <= 5; i++) { |
这段代码将会输出数字1到5。
while 循环
while循环会在每次迭代前检查条件是否为真。只有当条件为真时,循环体才会被执行。
语法
1 | while (条件) { |
例子:
1 | int i = 1; |
这段代码同样会输出数字1到5。
do-while 循环
do-while循环至少会执行一次循环体,然后在每次迭代后检查条件是否为真。
语法
1 | do { |
例子:
1 | int i = 1; |
这段代码同样会输出数字1到5。
控制语句
在循环内部,你可以使用控制语句来改变循环的执行流程:
break: 用于立即退出循环。continue: 用于跳过当前迭代,直接进入下一次迭代。return: 用于从方法中返回并退出循环。
例子:
1 | for (int i = 1; i <= 10; i++) { |
这段代码将输出1到4和6到7。
嵌套循环
循环还可以嵌套在一起,即在一个循环内包含另一个循环。
示例:
1 | for (int i = 1; i <= 3; i++) { |
这段代码会输出一个3x3的矩阵,每个元素都表示两个循环变量的值。
小练习:九层妖塔
请在控制台输出打印如下
代码如下:
1 | for(int i = 0;i<9;i++){ |
switch-case
在Java中,switch 语句是一种选择结构,用于基于不同的条件来执行不同的代码块。它提供了一个更简洁的方式来编写多分支的 if-else 结构。
基本语法
1 | switch (expression) { |
注意:
- 表达式 (
expression) 必须是一个整数、字符、枚举或字符串。 - 常量 (
constant) 必须是字面量或者常量表达式(不能是变量)。 - break 语句用来结束当前的
case并跳出整个switch语句。如果省略break,程序会继续执行下一个case,这就是所谓的“穿透”行为。 - default 子句是可选的,当
expression的值与所有case常量都不匹配时,会执行default中的代码。default可以放在switch语句的任何位置,但是通常放在最后。
示例
假设我们有一个变量 day,表示一周中的某一天,我们可以使用 switch 语句来输出这一天的名称:
1 | int day = 4; // 假设今天是星期三 |
Java 7 和以后版本的新特性
从Java 7开始,switch 表达式可以接受 String 类型。此外,从Java 12开始,switch 语句支持新的箭头语法,使得代码更加简洁。
Java 12+ 的增强版 switch 表达式
1 | String fruit = "apple"; |
这种形式允许你将 switch 语句当作一个表达式来使用,并且不需要 break 语句。
转义字符(常用,复习)
在Java中,转义字符是一种特殊的字符序列,用于表示一些不可见或者特殊含义的字符。这些字符通常以反斜杠(\)开始。下面是一些常用的Java转义字符及其含义:
\n- 新行(换行符)\t- 水平制表符(Tab键)\b- 退格(Backspace)\f- 换页符\r- 回车(将光标移至行首)\'- 单引号\"- 双引号\\- 反斜杠\xxx- 八进制转义序列,其中xxx是1到3位的八进制数,代表对应的ASCII字符。\uxxxx- Unicode转义序列,其中xxxx是四位的十六进制数,代表对应的Unicode字符。
例如,你可以使用这些转义字符来构造字符串:
1 | String example = "Hello,\n\tWorld!"; // Hello,后面是一个新行,然后是制表符,接着是World! |
在这个例子中,\n 代表一个新行,\t 代表一个水平制表符。
你还可以使用这些转义字符来创建包含特殊字符的字符串,比如:
1 | String withQuote = "He said, \"Hello, World!\""; // 字符串中含有双引号 |
在这个例子中,\" 用来表示字符串中的双引号。
键盘输入
目的:为了拿到用户输入的内容
步骤:
- 导入该类所在的包,java.util.*
- 创建该类的对象
- 利用对象调用里面的功能
1 | package KeyInput; |
常用方法如下:
Stringnext()查找并返回此扫描仪的下一个完整标记。Stringnext(Pattern pattern)返回下一个标记,如果它与指定的模式相匹配。Stringnext(String pattern)返回下一个标记,如果它与从指定字符串构造的模式相匹配。BigDecimalnextBigDecimal()扫描输入下一个标记为BigDecimal。BigIntegernextBigInteger()扫描输入下一个标记为BigInteger。BigIntegernextBigInteger(int radix)扫描输入下一个标记为BigInteger。booleannextBoolean()扫描输入的下一个令牌到一个布尔值,并返回该值。bytenextByte()扫描输入下一个标记为byte。bytenextByte(int radix)扫描输入下一个标记为byte。doublenextDouble()扫描输入下一个标记为double。floatnextFloat()扫描输入下一个标记为float。intnextInt()扫描输入下一个标记为int。intnextInt(int radix)扫描输入下一个标记为int。StringnextLine()将此扫描仪扫描到当前行,并返回跳过的输入。
进制转换
当我们讨论“四种进制”时,通常指的是二进制、八进制、十进制和十六进制。这些进制在计算机科学和数字电子学中非常常见。下面是一些关于这些进制之间相互转换的基本知识点和技巧。
1. 二进制(Binary)
- 定义:二进制是一种基于2的数制系统,只包含两个数字:0 和 1。
- 转换到十进制:每个二进制位乘以其位置对应的2的幂次,然后求和。例如,二进制数
1011转换成十进制为1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = 8 + 0 + 2 + 1 = 11。
2. 十进制(Decimal)
- 定义:十进制是我们日常生活中最常用的数制,它包含了0到9这十个数字。
- 转换到二进制:通过不断地用2去除十进制数,并记录每次的余数,直到商为0,然后将所有余数倒序排列即得二进制数。例如,十进制数
11转换成二进制的过程为11 / 2 = 5 ... 1 -> 5 / 2 = 2 ... 1 -> 2 / 2 = 1 ... 0 -> 1 / 2 = 0 ... 1,因此得到二进制数1011。
3. 八进制(Octal)
- 定义:八进制是一种基于8的数制系统,使用0到7这八个数字。
- 转换到二进制:每位八进制数可以转换为三个二进制位。例如,八进制数
127转换成二进制为001 010 111。 - 转换到十进制:每位八进制数乘以其位置对应的8的幂次,然后求和。例如,八进制数
127转换成十进制为1*8^2 + 2*8^1 + 7*8^0 = 64 + 16 + 7 = 87。
4. 十六进制(Hexadecimal)
- 定义:十六进制是一种基于16的数制系统,使用0到9这十个数字和A到F这六个字母(代表10到15)。
- 转换到二进制:每位十六进制数可以转换为四个二进制位。例如,十六进制数
FA转换成二进制为1111 1010。 - 转换到十进制:每位十六进制数乘以其位置对应的16的幂次,然后求和。例如,十六进制数
FA转换成十进制为15*16^1 + 10*16^0 = 240 + 10 = 250。
进制之间的直接转换
- 二进制与八进制:二进制数每三位一组转换为八进制数的一位,反之亦然。
- 二进制与十六进制:二进制数每四位一组转换为十六进制数的一位,反之亦然。
原码、补码、反码
- 二进制的最高位是符号位:0表示正数,1表示负数
- 正数的原码补码反码都一样,简称正数三码合一
- 负数的反码 = 符号位不变,其他位取反
- 负数的补码 = 反码 + 1,负数的反码 = 负数的补码 - 1
- 0的反码,补码都是0
- java没有无符号数,换言之,java中的数都是有符号的
- 在计算机运算时,都是以补码的方式来运算的
- 当我们看运算结果的时候,要看他的原码
第二章 面向对象
什么是面向对象?
所谓面向对象,其实就是分析问题时,以问题所涉及的是或者物为中心的分析方式
类和对象
类 and 对象(先有类,再有对象)
类表示归纳和整理;对象表示具体的事务。
在Java中我们用class表示类,通常类名首写字母大写。
基本语法结构:
1 | class 类名{ |
创建对象的语法结构
对象:类的实例化(具象化)
1 | new 类名();//小括号不能丢 |
对象是将内存地址赋值给了变量,所以变量其实引用了内存中的对象,所以称之为引用变量,而变量的类型称之为引用数据类型。
特殊的对象:空对象(null),没有引用的对象,称之为空对象,关键字对象,所有引用对象的类型默认取值为null
属性
所谓属性,就是类的对象的相同特征
声明:属性类型 属性名称 = 属性值
如果在声明属性的同时进行了初始化赋值,那么所有对象的属性就完全相同。如果希望每个对象的属性不一致,那么我们可以只声明即可,那么属性会在构造对象的时候默认初始化,而默认初始化的值取决于属性的类型
byte,short,int,long ==> 0
float,double ==> 0.0
boolean flg ==> false
char ==>空字符
引用数据类型 ==> null
1 | //初始化并赋值zhansan |
对象在内存中存放的形式
对象的内存分配机制
类和对象的内存分配
静态(static)
针对于具体对象的属性称之为对象属性,成员属性,实例属性
针对于具体对象的方法称之为对象方法,成员方法,实例方法
把和对象无关,只和类相关的称之为静态,
和类相关的方法称为静态方法
和类相关的属性称为静态属性
静态语法:就是在属性和方法前面增加static关键字
在Java语法中:
成员方法可以访问静态属性和静态方法
静态方法不可以访问成员属性和静态方法
在访问静态类的属性时,可以用静态类名.属性名直接访问。
静态代码块
当类的信息加载完成后,会自动调用静态代码块,可以完成静态属性的初始化功能
1 | static{ |
在我们构建对象时,代码块会进行初始化,但不是静态的
1 | { |
方法
方法的声明
1 | 访问修饰符 【方法返回值类型】/【void】 方法名(参数){功能代码} |
方法中void是空的意思,意味着没有结果。如果在方法中的结果需要返回,则确定返回值的类型利用return关键字来返回结果。没有返回值就用void
注意:
- 一个方法可以最多有一个返回值,可以通过返回数组来获得多个返回值(获取多个返回值)
- 方法的返回值可以是任意类型,包含基本数据类型或引用数据类型(数组,对象)
- 如果方法要求有返回数据类型,则方法中最后执行的语句必须为return 值;
- 如果方法使用了void修饰,则可以无返回值,也可以只写return
- 返回值的类型存在自动类型提升[精度大的返回值类型可以返回一个小精度的,反之则不行],也就是说如果某个方法的返回值是double类型,那么返回int类型是符合语法的,反之则不行
1 | //返回的结果是布尔类型的所以用Boolean |
方法传参(方法参数)
1.语法:方法名(参数类型 参数名称)
方法名(参数类型1 参数名称1,参数类型2 参数名称2,参数类型3 参数名称3) { }进行多个参数的传递。
2.传参注意事项:
参数个数要匹配
参数类型要匹配或兼容,精度小的可以传递给精度大的,反之则不行。
参数顺序要匹配
当参数个数不确定时,我们可以使用特殊语法来声明:可变参数
1
2//参数类型... 参数名称
方法名(String ... name){ }当参数列表中还有别的参数时,我们需要将可变参数声明在参数列表最后。也就是说,可变参数是函数中最后一个参数
基本数据类型:传数值,也叫值拷贝,不会影响主方法中的变量。
引用数据类型(对象,数组):传引用地址,这就会影响主方法中的变量。
1 | //main方法中 |
方法调用细节
- 同一个类中的方法调用:直接调用即可。
1 | package method; |
- 跨类中的方法调用:A类调用B类中的方法:需要通过对象名调用。
1 | package method; |
方法的调用机制
[方法调用机制]: https://www.processon.com/diagraming/671795ce61fdee7d75e9306f “方法调用机制”
小插曲
Java中的三块内存区域
栈:存放的是(方法,变量)
堆:存放(对象)
元空间:存放类的信息,也称之为方法区
包
包中容纳类
语法:package 包完整路径;
包完整路径用·隔开
包名一般全是小写字母
包的导入:
我们用import语法来导入包。
import使用在package后class前
1 | package xxx.xxx.Xxxx; |
面向对象——构造方法
基本介绍
构造方法又叫**构造器(constructor)**,是类的一种特殊方法,它的主要作用是完成对新对象的初始化。它有几个特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动调用该类的构造器完成对象初始化
构造方法:专门用于构建对象
基本语法如下
1 | [修饰符] 方法名(类名) (形参列表){ |
注意点
- 构造方法也是方法,无返回值但不能使用void关键字
- 方法名和类名完全相同
- 一个类可以有多个构造方法,也可以进行重载(重载的概念:方法名相同,参数的类型,顺序,个数不同,且与返回值类型无关。)
- 如果类中没有任何构造方法,那么JVM(java虚拟机)会自动添加一个公共的无参的构造方法。可以通过javap来进行反编译查看。
- 如果类中有构造方法===>JVM不会提供默认构造方法
- 一旦定义了构造器,那么就会覆盖默认的无参构造方法,除非显式的声明一下。
- 构造方法也是方法,可以传递参数,但是一般传递参数的目的是用于对象属性的赋值
this关键字(重点)
什么是this?
java虚拟机会给每个对象分配this,代表当前对象。坦白的讲,要明白this不是一件容易事。
1 | class Dog { |
总结:那个对象调用,this就代表那个对象。
this在JVM内存中的理解
简单理解:this在堆内存中指向了自己。
this的使用细节
this关键字可以用来访问本类的属性、方法、构造器
this用于区分当前类的属性和局部变量
访问成员方法的语法:this.方法名(参数列表)
访问构造器语法:this(参数列表);注意,只能在构造器使用且该条语句必须位于第一条语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class User{
//构造器1
User(){
//使用this关键字传入参数调用下面构造方法
this("zhansan");
}
//构造器2
User(String name){
//接收name并用this关键字调用下面方法
this("zhangsan","男");
}
//构造器3
User(String name, String sex){
System.out.println("名字是:"+name+","+sex);
}
}this不能再类定义的外部使用,只能在类定义的方法中使用
supper关键字(重点)
super代表父类的引用,用于访问父类的属性、方法、构造器
基本介绍:
- super可以访问父类的属性,但不能访问父类中被private修饰的属性和方法
- 语法super.属性名/方法名
- 访问父类的构造器
- 语法super(参数列表)且只能放在构造器中并且位于构造器中的第一条语句位置。
面向对象——封装
封装指的是把抽象出来的属性和对属性的操作(方法)封装在一起,数据(属性)被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作。
想想一个电视,它只对外部暴露了按钮,但是按钮具体内部时怎么操作的,我们不得而知
面向对象——继承
关键字:Extends
基本语法:
1 | class 子类 extends 父类 { } |
类存在父子关系:子类可以直接获取到父类的成员属性和成员方法
类的继承只能是单继承,一个类只能有一个父类,不能存在多个父类
在继承中,如果父类和子类含有相同的属性,那么可以采用特殊的关键字进行区分:super & this.
super:表示上一级
this:表示当前
继承中的细节
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的那个构造器默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类中使用super指明使用父类的那个构造器对父类进行初始化,否则编译不通过
- 如果希望指定调用父类某个构造器,就去使用super(参数列表)去指定调用
- super在使用时,需要放在构造器的第一行位置。
- super和this都需要在构造器第一行使用,因此这两个关键字不能存在同一个构造器中
- java中所有的类都是Object的子类
继承中的方法问题
构造方法(完成内存控件属性的初始化操作):
父类对象是在子类对象前创建完成的,创建子类对象前,会先创建父类对象
如果父类提供了有参构造方法,那么JVM就不会提供默认的构造方法,那么子类应该调用super方法(带入参数)构建父类对象。
继承在JVM中的分布
面向对象——多态(难点)
所谓多态,其实就是一个对象在不同场景下表现出来的不同状态和形态。
多态语法其实就是对对象的使用场景进行了约束。
以父类声明的子类对象,只能使用父类中的方法,如果子类对象想使用子类方法,需要用子类来声明子类对象
多态的基本介绍
方法或对象具有多种形态,它是建立在封装和继承基础之上的。
多态的体现(重难点10.28日复习)
- 方法的多态 - 方法的重载和方法的重写
- 对象的多态(核心)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变。
- 运行类型可以变化
- 编译类型看定义时
=号 的左边,运行类型看=号的右边。
比如Master master = new Dog(); 在这条语句中,他的编译类型是Master,而运行类型是Dog
- 方法参数的多态
方法多态的向上转型(难点)
!!!!针对的是成员方法!
本质:父类的引用指向了子类的对象,比如:Animal animal = new Dog()
语法:父类类型 引用名 = new 子类类型()
特点:编译类型看左边,运行类型看右边。
①可以调用父类中的所有成员(注意访问权限)
②不能调用子类中特有成员,因为在编译阶段,决定能调用那些成员方法是由编译类型决定的。
③在运行阶段,最终运行效果看子类的具体实现(也就是看运行类型),会先在子类中查找是否存在某个方法,并且权限可以访问到,如果有,则使用子类中的方法,如果没有,则在父类中去寻找。
方法多态的向下转型(难点)
类似于强制转换的规则:大的可以转换成小的,但小类型不能变为大类型。
语法:子类类型 引用 = (子类类型) 父类引用;
注意点:
①只能强制转换父类的引用,不能强制转换父类的对象
②要求父类的引用必须指向的是当前目标类型的对象
③当完成向下转型后,可以调用子类类型中所有的成员
属性
属性没有重写之说,属性的值只看编译类型,也就是等号左边。
属性也不存在动态绑定机制。
动态绑定机制(重点)
- 当调用对象方法时,该方法会和对象的运行类型(
也就是等右边的类型/内存地址)绑定。 - 当调用对象属性时,不存在动态绑定机制,哪里声明就在哪里使用。
参数多态
方法定义的形参类型为父类类型,实参允许为子类类型。
面向对象——方法重载(overload)
相同的方法:在一个类中,相同方法是指方法名,参数列表相同,和返回值类型无关的方法。
方法重载: 在Java中,如果方法名称相同,但是参数列表(个数,顺序,类型)不相同,会认为是不同的方法,只是名称一样,我们称之为方法的重载。
成员方法在被调用时,同一个方法名是通过参数的类型,个数进行区分的
在构造方法中,想要调用其他构造方法,那么需要使用关键字:this
1 | package com.itheima.面相对象综合实例; |
打印如下:
1 | 名字是:zhangsan,男 |
结论
基本数据类型:
基本数据类型匹配方法时,可以在数值不变的情况下扩大数据的精度。
byte数据类型无法和char类型做转换,char没有负数,而byte存在着负数
引用数据类型:
引用数据类型无法扩大数据精度,但是引用数据类型匹配方法时,会往object类去找。
比如A类是B类的父类,那么当调用的方法在B类中不符合时,就去A类中寻找,如果A类中没有就去A类的父类object中寻找
面向对象——方法重写(override)
定义:在父类中的方法其实主要体现通用性(可以被多个子类继承,可以被多个子类使用自身方法),无法在某一个特定情况去使用这个方法。如果子类对象需要再某个特定业务逻辑中使用该父类的方法,那么就需要对父类中的方法进行重写,这个操作成为重写。
请注意:
子类中的方法不会覆盖父类的方法,只是父类当前方法不适用这个场景
如果想在当前类调用父类方法可以使用super关键字进行调用
当子类的方法中的返回值类型,参数列表,方法名称都和父类相同时才可以进行重写
1 | package com.itheima.面相对象综合实例; |
通过以上例子,总结四点:
- 一个对象能使用什么方法,取决于引用变量的类型
- 一个对象能使用什么属性,取决于引用变量的类型
- 一个对象的方法具体使用(这个方法怎么去执行,逻辑是什么,直接使用还是间接使用),需要看具体对象
- 一个对象的属性具体的使用是不需要看具体的对象的,属性在哪里声明就在哪里使用
方法重写的注意点
- 子类方法的参数,方法名称,要和父类方法的参数,方法名称一致。
- 子类方法的返回类型和父类方法返回类型一样,或者子类方法的返回值的类型是父类返回值类型的子类
- 比如父类
public Object getInfo(){},子类中public String getInfo(){}由于String是Object的子类,所以这也是方法的重写(这句话非常非常非常重要)
- 比如父类
- 子类方法不能缩小父类方法的访问权限。访问权限排序:
public > protected > 默认 > private
面向对象——递归
定义:方法调用自身,成为递归方法
递归最经典的案例——求n以内的奇数和
1 | package com.itheima.面相对象综合实例; |
递归经典问题—阶乘
一个大于1的数的阶乘就是这个数乘以这个数减一的阶乘
1 | package com.itheima.面相对象综合实例; |
注意:
- 递归应该有跳出的逻辑
- 调用自身时,传入的参数应该是有规律的
方法递归的执行过程图解
面向对象——访问权限
- public:公共的,访问权限修饰符
在Java中,公共类只有一个,且名称和源码文件相同
main方法:main方法是有JVM调用的,JVM可以任意调用,可以忽略权限问题
Java中的访问权限有四种:
- private:私有的,同一个类中可以使用,别的类,其他地方无法使用
- (default):默认权限,当不设定任何权限时,JVM会默认提供权限,本类,同包、可以访问,不同包下不可以访问,子类不可以访问
- protected:受保护的权限,本类,同包、子类可以访问,不同包不可以访问。
- public:公共的,任意使用。
Object类
equals(Object obj)
- 判断指定的对象是否与此对象“相等”。
- 默认实现比较的是两个对象的引用是否相同(即是否指向同一个对象/引用地址是否相同),但在很多情况下需要重写此方法来实现基于内容的比较。
hashCode()方法
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向了同一个对象,则哈希值肯定是一样的。
- 两个引用,如果指向的是不同的对象,则哈希值是不一样的。
- 哈希值主要是根据地址号来的,但是不能把哈希值等价于地址
- 可以进行重写
toString()方法
- 返回值:全类名 + @ + 哈希值的十六进制
- 一般我们都会重写toString方法,用于返回对象的属性信息。
- 当我们输出一个对象时,toString方法会被默认调用。
1 | package Object.object_toString; |
面向对象高级—类变量和类方法
类变量(静态变量)
类变量也被成为静态变量,用static关键字修饰,是该类所有对象共享的变量,访问时遵循访问修饰规则。
- static变量是同一个类所有对象共享。
- static变量是在类加载的时候就生成了。
定义:访问修饰符 static 数据类型 变量名
访问类变量:
- 类名.静态变量
- 对象名.类变量名
使用场景:
- 某个变量需共享时,可以设置为静态变量
与实例变量的区别
类变量是类的所有对象共享的,而实例变量是每个对象独享的。
类变量细节
- 类变量可以通过
类名.类变量名来访问;实例变量不能通过类名.变量名访问 - 类变量在类加载的时候就完成初始化了,即使没有创建对象,也可以访问类变量
- 类变量的生命周期是随着类加载开始,类卸载结束。
1 | package OOP_Advanced.static_; |
类方法(静态方法)
静态方法的基本概念
- 定义:
- 静态方法是属于类的,而不是类的某个特定对象。
- 使用
static关键字修饰的方法就是静态方法。
- 声明和调用:
- 静态方法可以在类的外部通过类名直接调用,无需创建类的实例。
- 例如,如果有类
MyClass包含一个静态方法myMethod,可以这样调用:MyClass.myMethod();
静态方法的特点
- 访问限制:
- 静态方法只能直接访问其他静态成员(包括变量和方法)。换句话说,静态方法无法访问实例成员
- 如果需要访问非静态成员,则必须通过类的实例来访问。
- 生命周期:
- 静态方法随类的加载而加载,并且只加载一次。
- 这意味着静态方法在程序启动时就已经存在了。
- 内存使用:
- 静态方法和静态变量存储在方法区中,所有对象共享这部分内存。
- 因此,对于多个对象来说,静态方法占用的内存是共享的,这有助于节省内存。
静态方法的应用场景
- 工具方法:
- 常用于实现一些工具性质的方法,如数学计算、字符串处理等。
- 例如,Java标准库中的
Math类就提供了很多静态方法,如Math.abs()、Math.sqrt()等。
- 工厂方法:
- 可以用来创建类的对象,特别是当对象的创建过程较为复杂时。
- 例如,使用静态方法来返回一个配置好的对象实例,而不是让每个客户端都进行复杂的初始化工作。
- 单例模式:
- 在实现单例模式时,通常会提供一个静态方法来获取单例对象。
- 如:
Singleton.getInstance()
类方法的注意事项
- 类方法中无this
- 类方法可以通过类名.类方法 / 对象名.类方法
- 普通方法与对象有关,必须通过对象名调用,不能通过类名调用
面相对象高级—main方法
1 | public static void main(String[] args){ |
解释:
java虚拟机需要调用类的main()方法,所以访问权限必须是public
java虚拟机在执行main()方法时不必创建对象,所以方法必须是static
该方法接收String类型的数组参数,该数组中保存执行java命令时所传递给所有运行类的参数
args这个字符串数组接收的参数就是
java 程序名 参数1 参数2 参数3,就指的是由参数1,2,3构成的数组。解释:java程序也可以通过
javac 程序名 编译 ====> java 程序名来运行一个java程序(在命令行中)。
面向对象高级—代码块
什么是代码块
- 概念:
代码块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
和方法不同的是,没有方法名,返回值,参数,只有方法体,而且不通过对象或类显示的调用,而是加载类时或创建对象时隐式调用。
- 基本语法
1 | [修饰符] { |
- 注意点
- 修饰符可写可不写,如果写只能写static
- 代码块分为两类,使用static被称为静态代码块,没有static的被称为普通代码块,
- 逻辑语句可以为任何逻辑语句
- 结尾
;可写可不写 - 代码块调用的顺序优先于构造器。
代码块使用细节(重点,11.8复习)
静态代码块和代码块
静态代码块会随着类的加载而执行,但静态代码块只执行一次;而普通代码块只有在进行对象创建的操作时(new 操作),才会被调用
类什么时候被加载?
①创建对象实例 —->new 操作
②创建子类对象实例 —->原因是创建子类对象实例时会先加载父类
③使用类的静态成员时(静态属性,静态方法)
普通的代码块,在创建对象实例时,会被隐式调用。每new一次,就会调用一次。
但如果只是使用类的静态成员时,普通代码块并不会执行
创建一个对象时,在一个类中初始化的顺序是:先静态成员 —–> 普通成员 ——-> 构造方法
在调用构造器时,除了我们说的,构造器一开始会调用父类的无参构造器(这步是隐藏的,除非父类的构造器需要是重写过的带参构造器),此时,还会调用普通代码块。
解释:在继承关系中,要注意,在初始化子类时,会先初始化父类,那么初始化父类在调用构造器时也会先super再普通代码块。
在继承关系中,当我们创建了子类对象时,个各类中的静态代码块/属性初始化;普通代码块/属性初始化;构造方法的调用顺序是什么样的呢?(面试题)
我有继承关系: B 继承 A
当我创建B类的对象时,执行顺序如下:
类加载时:
父类的静态代码块/属性 ——> 子类的静态代码块/属性 ——–>
创建对象时:
父类的普通代码块和/属性 ———> 父类的构造器 ——–> 子类的普通代码块/属性 ——-> 子类的构造器
面向对象高级—final关键字
final的用法
final可以修饰变量:变量的值一旦初始化不允许被修改
final可以修饰属性:但是JVM不会对自动对属性进行初始化,需要手动初始化,并且属性值不可以修改
final可以修饰方法:final修饰的方法不可以被子类重写
final可以修饰类:当final修饰一个类时,被修饰的类不可以有子类
final不可以修饰构造方法。
final可以修饰方法参数:一旦修饰,该参数无法修改。
一般将final修饰的变量称之为常量,或者叫不可变量。
final的使用场景
- 当某个类不希望被继承时,可以用final修饰这个类。
- 禁止重写父类中的某个方法,可以使用final来修饰这个方法。
- 禁止修改类中的某个属性,可以使用final来修饰这个属性,类似与前端中使用const创建的变量。是一个常量。
- 禁止修改某个局部变量,可以使用final修饰。
final细节
final修饰的被称为常量,一般用xxx_xxx_xxx来命名
final修饰的属性在定义时,必须赋初值,且以后不能修改,可以在下面的位置进行初始化
定义时:public final double TAX_RATE = 0.08;
在构造器中
在代码块中
如果final修饰的属性是静态的,则初始化的位置只能是:
定义时/静态代码块中
final类不能继承,可以创建对象
final不能修饰构造器(即构造方法)
final往往和static搭配使用
- 包装类(Interger,Double,Float,Boolean等都是final类),String类等都是final类
面向对象高级—抽象类(Abstract)
抽象类的定义
用abstract修饰的类被称为抽象类,用abstract修饰的方法被称为抽象方法
抽象类:不完整的类就是抽象类,抽象类不能够构造对象
1
2
3
4//语法
abstract class 类名{
}抽象方法:只有声明方法,而不去写方法里面的逻辑。
1
2//语法
abstract 返回值类型 方法名(参数);在一个类中,有抽象方法就是抽象类,反之,如果有抽象类,他的方法不一定是抽象方法。
抽象类无法直接构造对象,但是可以通过子类间接构造对象。
如果抽象类中含有抽象方法,那么子类继承抽象类,需要重写抽象方法,将方法补充完整。
abstract关键字不能和final关键字一起使用
抽象类的细节
- 抽象类不能被实例化,也就是说抽象类不能创建对象
- 抽象类不一定要包含抽象方法
- 一旦类包含了抽象方法,那么这个类必须被声明为抽象类
- abstract只能修饰类和方法,不能修饰属性或其他内容
- 抽象类可以有任何成员,和普通类唯一的区别就是抽象类不能使用new关键字新建一个实例
- 抽象方法不能有函数体
- 如果一个类继承了抽象类,那么他必须实现(重写)抽象类中的所有抽象方法,除非自己也是抽象类。
- 抽象方法不能使用private、final、static来修饰,因为被这些关键字修饰的方法不能重写。
面向对象高级—接口(interface)(11.9复习)
接口的定义
接口:可以简单理解为规则。类比USB接口,接口它规定了一系列规则。接口就是给出一些没有实现的语法,封装到一起,到某个类要使用的时候在根据具体情况把这些方法重写。
基本语法:
1 | //接口语法 |
- 接口其实是抽象的 —–> 可以省略abstract关键字
- 接口中的方法可以是:
- 抽象方法(不包含方法体)
- 默认实现方法和静态方法(JDK8以及以后才有的特性)
接口的一段演示代码
1 | //接口 - 类似于USB |
接口的使用细节
- 接口不能被实例化,接口不能new操作
- 接口中的所有方法是public,接口中的抽象方法可以不用abstract修饰
- 一个普通类实现某个接口,就必须把接口中的方法都实现了,不可以少写或不写
- 抽象类实现接口可以不用实现接口的方法
- 一个类可以实现多个接口
- 接口中的属性,只能是final,也就是说,在接口中属性的修饰符只能是public static final 修饰符,
- 比如在接口中定义了一个变量:int n = 1 <==========>相当于这样写: public static final int n = 1;
- 接口访问属性的语法是:接口名.属性名(因为接口中的属性是静态属性)
- 接口不能继承其他类,但是接口可以继承接口。
- 接口的修饰符只能是public和默认(不写),这一点和类是一样的。
继承和实现的区别
- 继承只能是单一继承,而接口可以一次性实现多个接口,所以实现接口更像是对继承单一机制进行的补充。
- 接口比继承更灵活,继承得满足猫(is - a) 动物,而接口只需要满足猫(like - a)兔子一样可以跑。
接口的多态特性(11.10复习)
接口的引用可以指向实现了接口的类的对象实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package OOP_Advanced.interface_;
/*
接口的多态体现
1.接口的引用可以执行实现了接口的类的对象。
*/
public class InterFace_poly {
Interface_poly interfacePoly_1 = new Test_B();
Interface_poly interfacePoly_2 = new Test_C();
}
interface Interface_poly {
}
class Test_B implements Interface_poly {}
class Test_C implements Interface_poly {}在数组上多态的体现,这一点和上面一样
1
2
3
4
5
6
7Interface_poly[] interfacePolyArray = new Interface_poly[2];
interfacePolyArray[0] = new Test_B();
interfacePolyArray[1] = new Test_C();
interface Interface_poly {}
class Test_B implements Interface_poly {}
class Test_C implements Interface_poly {}
接口的多态传递
接口A1和A2和类Teacher
- A1被Teacher类实现,那么
A1 a1 = new Teacher()一定是成立的, - 但是如果这时写出了
A2 a2 = new Teacher()就会报错,因为A2类并没有实现Teacher类 - 但如果A2继承了A1接口
A2 extends A1,那么错误就会消除,这个过程可以看做多态特性发生了传递。
面向对象高级—内部类(难点、重点)
内部类的定义
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),被嵌套的类称为(outer class)外部类
类的五大成员:属性、方法、构造器、内部类、代码块
1 | package OOP_Advanced.innerclass; |
内部类的分类
共有4种内部类
定义在 - 外部类局部 - 位置上 - 比如方法内:
- 局部内部类(有类名)
- 定义在方法或者作用域块中。
- 可以直接访问外部类的所有成员,包括私有成员(private修饰)
- 不能添加访问修饰符,可以被final修饰,一旦被final修饰就不能被继承了
- 作用域:仅仅在定义它的方法或代码块中。
- 想访问局部内部类,可在外部类方法中创建内部 类的对象,即可访问。
- 如果外部类变量和局部内部类变量重名,由于就近原则,如果此时想访问外部类的同名变量的话,语法是
外部类名.this.变量名,而实际上外部类名.this就是外部类的对象,可以参考this的指向(谁调用this指向谁)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package OOP_Advanced.innerclass;
public class LocalInnerClass {
public static void main(String[] args) {
TestA testA = new TestA();
testA.m2();
}
}
class TestA {
private int n1 = 4;
private void m1(){
System.out.println("我是TestA类中的方法m1");
}
public void m2(){
//局部内部类 - 位置通常在方法中或者代码块中
//这里定义的类被称为局部内部类
class LInnerClass {
private void InnerClass_m1(){
System.out.println("TestA类中的变量n1 = " + n1);
m1();
}
}
//可以在这里使用局部内部类
LInnerClass l = new LInnerClass();
l.InnerClass_m1();
}
}- 局部内部类(有类名)
定义在 - 外部类成员 - 位置上:
- 成员内部类(无static修饰)
- 静态内部类(使用了static修饰)
面向对象高级—枚举(Enum)
定义:枚举是一个特殊的类,其中包含了一组特定的对象,一般使用大写的的字母,这些对象不会发生改变。
1 | enum City{ |
- 枚举使用enum关键字使用。
- 枚举类的对象一般放在前面,如果还要写入别的语法那么应该用分号与后面的语法隔开。
- 枚举类不能创建对象,只能在内部使用
举个例子
1 | public class EnumTest { |
面向对象——匿名类
在某些场合下,类的名字不重要了,我们只是想使用类中的方法或功能,那么这个时候就需要用到特殊的语法:匿名类。
定义:没有名字的类,被称为匿名类
举个例子
1 | public class NoNameClass { |
接口也可以
1 | //只关心你是怎么飞的,并没有考虑你是什么类,所以这里用匿名类。 |
面向对象——Bean规范
- 类要求必须含有无参的,公共的构造方法
- 属性必须私有化,然后提供公共的get,set方法
例子
1 | public class Movie { |
面向对象——作用域
例子
1 | package com.itheima.面相对象综合实例; |
第三章 常见类和对象
常见类和对象——Object
Object类的包路径:java.lang.Object
Object对象的常用方法:
1 | package com.itheima.常见类和对象; |
常见类和对象——数组
语法
声明一个数组:数据类型[] 数组名;
初始化一个数组:
静态初始化:静态初始化:数组变量的声明和数组元素的赋值同时进行
1
2
3
4//声明一个数组 : 数据类型[] 数组名;
int[] nums;
//也可以下面这种方式,也表示数组的长度为5 - 静态初始化:数组变量的声明和数组元素的赋值同时进行
nums = new int[]{1,2,3,4,5};动态初始化:动态初始化:数组变量的声明和数组元素的赋值分开进行
1
2
3int[] num1;
//初始化一个数组:表示创建一个长度为5的数组 - 动态初始化:数组变量的声明和数组元素的赋值分开进行
num1 = new int[5];
数组的访问:通过数组名[下标(索引)]来访问
数组的修改:数组名[索引] = 新值
数组的特点
数组的特点:(学习时注意与JS中的数组进行区分。)
- 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组是引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型。
- 数组一旦初始化完成,其长度就确定了,并且长度不可更改(重点)
- 创建数组对象会在内存中开辟一块”连续的空间”。占据空间的大小,取决于数组的长度和数组中元素的类型。
数组的默认值
在 Java 中,当你创建一个数组但没有给每个元素赋初值时,Java 会自动为数组的每个元素分配一个默认值。这个默认值取决于数组元素的数据类型。下面是各种基本数据类型数组的默认值:
- 整型 (
int[],short[],byte[]): 默认值为0. - 长整型 (
long[]): 默认值为0L. - 浮点型 (
float[]): 默认值为0.0f. - 双精度浮点型 (
double[]): 默认值为0.0d. - 字符型 (
char[]): 默认值为\u0000(即 ASCII 的 NUL 字符).或0 - 布尔型 (
boolean[]): 默认值为false.
例如:
1 | int[] intArray = new int[10]; // 每个元素都是 0 |
引用数据类型的数组,默认值是 null。例如:
1 | String[] stringArray = new String[5]; // 每个元素都是 null |
请注意,这些默认值只适用于使用 new 关键字创建的数组。如果你声明一个数组但没有用 new 显式地分配内存,则不能访问这些默认值,因为这样的数组实际上并未创建。
一维数组内存解析
该图传达的意思有:
- 数组在内存中时,栈中存放的是数组的首元素的地址。
- new出来的内容是在堆中的,通过赋值操作把响应的地址赋值给变量。
- 通过new关键字出来的会新开辟一块内存空间(重要)
第二张图:
这张图传达的意思:
- 如果我们直接进行赋值,那么就是将数组的内存地址赋值给新的变量,进而两个变量指向同一个数组。
- 在该图中我们没有使用new关键字进行定义新变量,所以没有开辟新的内存空间
常见类和对象——二维数组
二维数组的定义
二维数组可以理解为一个矩阵。
1 | //二维数组(可以简单理解为3X3的表格) |
数组——应用
冒泡排序
1 | package com.itheima.常见类和对象; |
上面这个只是找出了我们的最大的数放到了数组最后面,那么我们的冒泡排序的结果应该1,2,3,4,5所以稍加改动如下:
解析:那么在上面的基础上,我们每次把最大数放在后面不就可以了吗?也就是说第一次比较5个数,那么5最大放在后面,第二次比较4个数一定是4最大,我们把四放在四个数的最后面。依次类推,不就完成了吗?
1 | //代码逻辑(冒泡排序) |
思路图解冒泡排序
选择排序
1 | //选择排序 |
二分查找法
- 前提:数据有序。
解释:二分查找法,又称折半查找(Binary Search),是一种在有序数组中高效查找特定元素的算法。其核心思想是通过将待查找区间反复减半来逼近目标值,直至找到目标或者确定目标不存在于数组中。以下是二分查找的基本步骤:
初始化: 确定查找区间的初始边界,通常是整个数组的开始索引(设为
left)和结束索引(设为right),或者说是数组的第一个元素和最后一个元素的索引。计算中间索引: 在每一轮查找中,计算当前查找区间中间元素的索引
mid = (left + right) / 2(实际应用中为防止整数溢出,通常写作mid = left + (right - left) / 2)。
比较与分支:
- 如果数组中间元素
array[mid]正好等于目标值,查找成功,返回中间元素的索引mid。 - 如果
array[mid]小于目标值,则说明目标值位于中间元素右侧的子区间,因此更新查找区间为left = mid + 1。 - 如果
array[mid]大于目标值,则目标值位于中间元素左侧的子区间,更新查找区间为right = mid - 1。
- 重复步骤2和3,直到
left > right,此时说明目标值不在数组中,查找失败。
二分查找的时间复杂度为O(log n),其中n是数组中的元素数量。这是因为每次操作都将查找范围减半,因此大大减少了需要检查的元素数量,尤其是在处理大数据集时,这种算法的效率优势更为明显。但需要注意的是,二分查找的前提是数组必须是有序的,且通常适用于静态数据结构,因为对数组的插入和删除操作可能会破坏其有序性,从而增加维护成本。
1 | package com.itheima.常见类和对象; |
常见类和对象——包装类
在Java中,基本数据类型如 int, char, boolean 等都有对应的引用类型,也称为包装类。这些包装类提供了对基本数据类型的一些额外功能,比如自动装箱/拆箱、比较、序列化等。以下是Java中所有基本数据类型的包装类:
| 基本数据类型 | 包装类 |
|---|---|
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
char |
Character |
boolean |
Boolean |
包装类的特点
- 实例化:包装类是对象,因此需要通过
new关键字实例化。 - 不可变性:所有的包装类都是不可变的(immutable),这意味着一旦创建了包装类的对象,就不能更改其值。
- 方法:包装类提供了许多有用的方法,例如转换、比较、解析等。
- 装箱与拆箱:从Java 5开始,Java支持自动装箱(将基本类型自动转换为包装类)和自动拆箱(将包装类自动转换为基本类型)。
示例
下面是一些使用包装类的例子:
创建包装类对象
1 | Integer myInt = new Integer(10); // 明确地创建对象 |
使用包装类的方法
1 | // 解析字符串为整数 |
使用包装类的静态方法
1 | // 比较两个整数 |
特殊情况
对于 Integer 类,Java 为了节省内存,对于 -128 到 127 之间的整数,会缓存这些值的对象。这意味着在这个范围内使用自动装箱时,实际上使用的是同一个对象。
1 | Integer a = 127; |
但是,超出这个范围时,对象不再是同一个:
1 | Integer a = 128; |
将基本数据类型转换为包装类
1 | package com.itheima.常见类和对象; |
5)常见类和对象——日期和日历
日期类:
1 | package com.itheima.常见类和对象; |
日历类:
1 | //拿到日历的对象 |
6)常见类和对象——工具类
- 工具类不应该创建对象才能使用,也就意味着,可以直接使用类中的属性和方法,一般都声明为静态的。
- 工具类对外提供的属性和方法都应该是公共的。
- 为了使开发者方便,应该尽量提供丰富的方法和属性。
第四章 异常
Java中的异常分为两大类:
可以通过代码恢复正常逻辑执行的异常,称之为运行期异常:RuntimeException
不可以通过代码恢复正常执行逻辑的异常,称之为编译异常:
Exception
异常处理的语法:
try:尝试(放的是可能会出现问题的代码)
catch:捕捉,捕捉多个异常时,优先选择小的异常,然后将其抛出
finally:最终
1 | try{ |
异常的常见类型:
1 | // TODO 异常 |
如果方法中可能会出现问题,那么需要提前声明,告诉其他人,我的方法可能会出现问题。这时,我们需要使用throws关键字
1 | public static void main(String args[]) throws ArithmeticException{ |
那么在运行期的异常,他不会抱任何问题,因为只要你的逻辑对了,代码就能继续运行,那么我们该如何让他出现提示,需要解决异常呢?下面这种方法,我们手动抛出异常对象,但是使用的是throw关键字,然后new出异常对象
1 | public static void main(String args[]) throws Exception{ |
(1)自定义异常
看代码:
1 | package com.itheima.Exception; |
ArrayList详解
1.怎么学?
arrayList会提供创建容器对象的方式
会提供相应方法对容器进行操作
增 删 改 查
创建对象 ====> 添加增删改查方法 ====> 了解集合特点
2.ArrayList常用方法
构造器
ArrayList
是用的最多、最常见的一种集合。
1 | //创建一个空的集合对象 |
常用方法名
1 | //将指定的元素添加到此集合的末尾 |
3.从集合中遍历元素,并筛选出元素删除它,应该如何操作才能不出bug?方式一:每次删除一个数据后,索引-1。
方式二:从集合后面遍历然后删除,可以避免漏掉元素。
集合中常用的接口
1.Collection接口(面对单一的数据处理)
常用子接口:List,Set,Queue
List:按照 插入顺序保存数据,数据可以重复
具体实现类:ArrayList,LinkedList
Set:集,无序保存,数据不能重复
具体实现类:HashSet(面对K-V键值对的数据处理)
Queue:队列
具体实现类:ArrayBlockingQueue
1.Map接口
具体实现类:hashMap,HashTable
什么是ArrayList?
动态数组:底层由数组实现,但可自动扩容(默认初始容量为10)
泛型支持:只能存储对象(如
Integer,String等),需用包装类代替基本类型属于
java.util包:使用前需要导入:import java.util.ArrayList;1
2
3
4
5
6
7
8
9
10
11
12
13
14
## ArrayList的操作
- **创建ArrayList**
```java
// 创建一个存储 String 的 ArrayList
ArrayList<String> names = new ArrayList<>(); // 空列表,初始容量10
// 创建指定初始容量的 ArrayList
ArrayList<Integer> numbers = new ArrayList<>(20); // 初始容量20
// 通过已有集合快速初始化
ArrayList<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana"));
ArrayList常用方法
增:以上面的names为例
1
2
3names.add('xiaoli')
//按索引位置增加
names.add(1,'xiaohua')删
1
2
3
4
5
6//按索引删除,返回被删除的元素
names.remove(1)
//删除第一个出现的内容,返回是否删除成功
names.remove('xiaoli')
//删除所有元素
names.clear()改
1
2//参数两个:索引,替换的值
names.set(1, "C++"); // 将索引1的元素替换为"C++"查
1
2String first = names.get(0); // 获取索引0的元素(若越界会抛 IndexOutOfBoundsException)调用.get()方法 0 表示第一个元素
int size = list.size(); // 获取当前元素数量(不是容量!)判断存在性
1
2boolean isEmpty = list.isEmpty(); // 是否为空
boolean exists = list.contains("Go"); // 是否包含"Go"
遍历 ArrayList
方法有三:普通for循环
1 | for (int i = 0; i < list.size(); i++) { |
增强for循环
1 | for (String item : list) { |
迭代器
1 | Iterator<String> iterator = list.iterator(); |
⚠️ 注意事项
1. 自动装箱与性能
存储基本类型需用包装类(如
ArrayList<Integer>),频繁操作可能影响性能示例:
1
2
3ArrayList<Integer> nums = new ArrayList<>();
nums.add(5); // 自动装箱:int → Integer
int num = nums.get(0); // 自动拆箱:Integer → int
2. 线程不安全
ArrayList非线程安全,多线程环境下需手动同步或改用Vector/CopyOnWriteArrayList
3. 扩容代价
- 添加元素时若容量不足,会创建新数组并复制元素(时间复杂度 O(n))
- 优化:预分配足够容量(如
new ArrayList<>(1000))
💡 进阶技巧
1. 转换为数组
1 | String[] arr = list.toArray(new String[0]); // 转换为 String[] |
2. 排序
1 | Collections.sort(list); // 自然顺序排序(如字符串按字母顺序) |
3. 过滤元素(Java 8+)
1 | list.removeIf(item -> item.length() > 5); // 删除长度超过5的字符串 |
LinkedList详解
LinkedList = Linked(链接) + List
泛型
1 | package com.itheima.List; |
比较器
1 | package com.itheima.List; |
HashSet
1 | package com.itheima.List; |
HashSet处理重复数据
重写equals和hashcode方法
1 | package com.itheima.List; |
Queue接口
1 | package com.itheima.Queue; |
HashMap(处理键值对数据(K,V))
数据存储无序
1 | package com.itheima.HashMap; |
HashTable
1 | package com.itheima.HashTable; |
迭代器
1 | package com.itheima.HashTable; |
1、如何拿到用户的输入
有时候的一些业务是需要用户和系统互动的,这时,我们就需要来接收用户的互动,在java中,通常这么做
1 | //新建一个扫描器对象sc |
举个例子,比如我下面这段代码
1 | Scanner sc = new Scanner(System.in); |
很好理解,接收用户输入的数字id然后把他传给operator.searchMovieById(id);方法完成代码逻辑。
字符串类型
- 方式一:
1 | String name = "小黑" |
- 方式二:调用String类的构造器初始化字符串对象
1 | public String() //创建一个空白字符串对象,不含有任何内容 |
(2)有关字符串的一些方法
1 | //查询字符串长度(字符个数) |
(3)字符串进行比较有哪些需要注意的?
1、字符串的比较使用==比较好吗?为什么?什么时候使用==?
- 不好,对于字符串对象的比较,==比较的是地址,容易出业务bug
- 基本数据类型的变量或者值应该使用==比较。
2、开发中比较字符串推荐使用什么方式比较?
使用String提供的equals方法,它只关心字符串内容一样就返回true,
1 | 方法名 |
(4)随机产生验证码(使用字符串方式)
1 | package com.itheima.StringController; |
(5)两种方式的区别
双引号给出的字符串对象,存在于堆内存中的常量池中,相同内容只会存储一份。new 字符串对象,每new一次都会在堆内存中产生一个字符串对象。
字符串大小写转换
1 | String s ="Hello,World" |
StringBuilder方法
java提供的一种对字符串进行拼接,反转的快捷类
使用实例:
1 | StringBuilder s = new StringBuilder(); |
Java中遇到的各种问题
(1)Mybatis与java
在Mybatis和java联合编写xml文件时,有关Mapper.xml(编写SQL的)文件中,增删改查标签中,使用parameterType属性指定传递参数类,resultType属性指定返回结果类型,resultMap标签完成当数据库表字段和Java实体类属性名或类型不一致时的手动映射,另外,SQL文中,where条件中只有一个参数时,推荐使用基本类型(int,short,char,byte,float,double,long,boolean)+String,当有多个参数时,使用Java实体类,接下来,我们来讲讲具体的用法。
来看示例:
1 |
|
1.parameterType
定义在增删改查标签中的属性,指定SQL文中where条件的参数类型,可以是基本数据类型(8个)或String或者对象类型
例如,根据id查询name,如果数据库中定义的字段id是int类型,就可以指定parameterType的属性值是int,可以参看下边的代码。
1 | < select id="queryPersonNameById" parameterType="int" resultType="String"></select> |
2.resultType
也是定义在增删改查标签中的属性,用于指定查询结果或增删改执行结果的类型,同样可以是基本数据类型+String+对象类型,这里需要注意的是,当查询结果是多条时,无需写List,MyBatis底层会自动为我们生成List<数据类型>,我们只需指定类型即可。
同样是上边的例子,根据id查询name,name在数据库中是varchar类型,resultType就指定成String类型,类型不区分大小写,尽量保持和Java一致,使用大写。
1 | < select id="queryPersonNameById" parameterType="int" resultType="String"></select> |
3.resultMap
当实体类的属性名和数据库表字段名或类型不一致时使用
例如,Person表中,定义的字段是id和name
Java实体类中,定义的是pid和pname,这种情况下,使用resultMap手动完成数据库和Java实体类属性的映射。
它是单独的< resultMap>标签,定义id(该属性是下面要用到的resultMap标签对应的值),type(该属性指定了结果应该映射到的Java类的完全限定名或别名。)通常使用在查询标签中
例如下边的代码
id标签完成数据库主键的映射
result标签完成数据库非主键字段的映射
property属性指定java实体类中的属性名,column是数据库字段名
1 |
|
(2)增强for循环
1 | for (元素类型 参数 : 集合名称) { |
通常适配类型:它通常用于遍历数组或实现了
Iterable接口的集合类(如ArrayList、HashSet等)。增强for循环的优点:
使用增强型
for循环的优点简洁易读: 代码更简洁,不需要显式地处理索引。
减少错误: 避免了手动操作索引可能带来的错误,例如越界访问。
使用增强型
for循环的限制只读访问: 增强型
for循环适用于只读访问集合中的元素,如果需要修改元素或需要索引值,则需要使用传统的for循环。无法跳跃访问: 不能实现按特定步长跳跃访问元素,只能顺序遍历。












