认识和理解Java
平台
- J2SE(Standard Edition) 面向桌面应用开发
- J2EE(Enterprise Edition) 面向企业级开发
- J2ME(Micro Edition) 面向手机等移动端开发
发行版本
- 1.2
- JavaSE(corejava)
- JavaEE(企业级开发)->JaKartaEE
- JavaME(手机端)
- 1.5
- 新功能:自动装箱、泛型、注解、枚举、jmm、并发
- 1.8
- 主流公司 1.8的版本
- 线程调度策略 JDK1.2 -> JDK1.8
- 高版本的功能更多 ->低版本的代码跑不通
- JVM 做了更替
术语
- SDK 软件包开发,主要包含函数库或者工具等等
- API 应用程序编程入口(连接SDK)
- API documentation API说明文档,描述API中的类,方法等使用的方法
- JDK Java程序开发工具包,面向Java程序的开发者
- JRE Java程序运行环境,面向Java程序的使用者
优点
- 更纯粹的面向对象编程,加速开发的过程
- 多线程
- 开源及强大的生态环境,社区活跃,第三方类库选择丰富
- 跨平台 JVM
- JVM是Java Virtual Machine(Java虚拟机)的缩写,它是一个虚构出来的计算机规范结构,是通过在实际的计算机上仿真的模拟各种计算机功能来实现的
- JVM就是Java代码和计算机之间的一个桥梁
- 各个操作系统中的JVM运行后都会产生相同的字节码文件(.class),在虚拟机中都可以识别同样的字节码文件,然后运行,实现跨平台
- 无需内存管理 gc(垃圾回收器)
- JVM中,由垃圾回收器(Garbage Collection GC)释放内存
- 安全----字节码验证
- 检查当前class文件的版本和JVM的版本是否兼容
- 检查当前代码是否会破坏系统的完整性
- 检查当前代码是否有栈溢出的情况
- 检查当前代码中的参数类型是否正确
- 检查当前代码中的类型转换操作是否正确
常用的命令
- javac 编译命令 javac Hello.java
- java 运行命令 java Hello
- javap 反解析命令,可以解析出class字节码文件的内容 javap -v Hello.class
- jar 打包命令 jar -cvf hello.jar -C bin
- javadoc 生成API文档命令 javadoc -d api -version -charset UTF-8 src/*.java
常用的包
- java.lang 最常用的一个包,里面的类可以在我们的程序中直接使用,不需要import导入
- java.awt 、javax.swing、java.awt.event
- 这三个包属于同一种类型,它们包下面的类都是用来做图形化界面的(GUI)
- 注意:javax开头的包,是sun公司后面又扩展进来的包,刚开始是没有的
- java.io 这个包下的类主要用于输入输出流操作,也就是读数据/写数据
- java.net 这个包下的类主要用于网络编程,例如把计算机A和计算机B之间建立网络连接,然后进行信息传输
- java.util 这个包下的类都是一些常用的工具类,可以帮我们在代码中实现一些辅助的功能,例如表示日期、使用集合存储数据、使用工具类操作数组等等
包
作用:包是用来区分类的
**导包:**import
- java.lang包是不需要手动导入包的
- System
- java.util包
- import java.util.ArrayList;
- import java.util.Date;
建包:
- 公司域名 倒写形式
package com.briup.day1;
public class Hello{
}
类加载
- 启动类加载器bootstrapClassLoader,非java语言实现, jre/lib 例如rt.jar
- 扩展类加载器ExtClassLoader,java语言实现,是ClassLoader类型的对象,指定路径ClassPath
双亲委任
1、Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
2、ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
3、AppClassLoader:主要负责加载应用程序的主函数类。
- 现在要加载hello.class文件中的类
- 首先加载任务就交给了AppClassLoader(检查是否加载过,如果有那就无需再加载)
- 然后AppClassLoader把这个任务委托给自己的父加载器,也就是ExtClassLoader(检查是否加载过,如果有那就无需再加载了)
- 然后ExtClassLoader把这个任务委托给自己的父加载器,也就是bootstrapClassLoader(检查是否加载过,如果有那就无需再加载)
- 然后bootstrapClassLoader就尝试去加载hello这个类,但是在指定路径下并没有找到
- 然后任务又交回给了ExtClassLoader,ExtClassLoader尝试加载这个hello类,但是在指定路径没招到
- 然后任务又给回了AppClassLoader
- 最后AppClassLoader从ClassPath中制定的路径里面找到并加载了这个hello类,完成类加载的过程
标识符、关键字、变量
注释
- // 单行注释
- /* */ 多行注释
- /** */ 文档注释
相关注解
- @author:作者
- @version:版本
- @deprecated:不推荐使用的方法,过时的方法
- @param:方法的参数类型
- @return:方法的返回类型
- @see:用于指定参考的内容
- @exception:抛出的异常
- @throws:抛出的异常,和exception同义
- @since:从什么开始
生成文档:
javadoc -d api -author -version -charset UTF-8 src/*.java
标识符
概念
- bit 计算机最小的传输单位
- byte 计算机最小的存储单位
- 1byte == 8bit
- 1kb == 1024byte
- 1mb == 1024kb
- 1g == 1024mb
- 1t == 1024g
命名规则
- 标识符可以由字母,数字,下划线,美元符号组成
- 标识符开头不能是数字
- 标识符的字符大小写敏感
- 标识符的长度没有限制
- 标识符不能使用java中的关键字或保留字
推荐规则
- 类和接口,首字母大写,如果是两个单词,第二个单词的首字母大写Account{},AccountBase{}
- 方法和变量,首字母小写,如果是两个单词,第二个单词的首字母大写getStudentName(),personNum()
- 常量,全部字母大写,如果是两个单词,使用下划线分隔int ,AX_NUM
- 尽量使用有意义的名字,尽量做到见名知意,numOfStudent
基本数据类型
字节
- 数据输出大多是以“位”(bit,比特)为单位
- 每8位(bit)组成一个字节
- 一个字节可以由两个十六进制位表示(二进制转换成16进制)
- 右边是运算表达式 整个类型的上限由右边最高类型精度决定
- 前提:数字默认是int,如果该int类型的范围在左边类型的范围内,会自动类型转换
数据类型
布尔型:boolean
- 布尔类型占用1个字节(8位)
- 必须是true或者false
- JVM中会转换为1(true)或者0(False)
字符型:char
- char类型占用2个字节(16位) 0~65535
- char类型是无符号类型,没有正负号符号位,两个字节也就是16位,2的16次方为65536,范围就是0-65535
- 一个String是由0~n个char组成
- Java语言对文本字符采用Unicode编码
整型:
- byte 8位,1字节,范围:-27~27-1
- short 16位,2字节,范围:-215~215-1
- int 32位 ,4字节,范围:-231~231-1
- long 64位,8字节,范围:-263~263-1
浮点型:
- float 的精度为7位左右有效数字
- double 的精度为16位左右的有效数字
常见编码:
- **ASCLL:**它只用一个字节的7位,一共表示128个字符
- **GB2312:**包括对简体中文字符的编码,一共收录了7445个字符(6763个汉字和682个其他字符),它与ASCLL字符编码兼容
- GBK: 对GB2312字符编码的扩展,收录了21886个字符(21003个字符和其他字符),它与ASCLL字符编码兼容
- **Unicode:**由国际Unicode协会编制,收录了全世界所有语音文字中的字符,是一种跨平台的字符编码(全国统一)
- 用2个字节(16位)编码。被称之为UCS-2,Java语言采用
- 用4个字节(32位)编码,被称之为UCS-4;
- **UTF-8:**UTF-8,使用1至4个字节为每个字符编码,其中大部分汉字采用三个字节编码,少量不常用汉字用四个字节编码,因为UTF-8是可变长度的编码方式,相对于Unicode编码可以减少存储占用的空间,所有被广泛使用
- ISO-8859-1:
- 又称为Latin-1,是国际化标准组织(ISO)为西欧语言中的字符制定的编码
- 用1个字节(8位(来为字符编码,与ASCLL字符编码兼容
转义字符:
- \n 换行符,将光标定位到下一行的开头
- \r 回车,将光标移动到行首打字机(和环境有关,会有不同功能)
- \t 垂直制表符,将光标移到下一个制表符的位置
- \\ 反斜杠字符
- \` 单引号字符
- \“ 双引号字符
变量
- 局部变量
- 定义在方法中的变量,超过这个作用范围,局部变量就不产生作用了
- 没有默认值
- 局部变量被直接包裹的大括号中,从这变量声明开始,一直到这个大括号结束
- 方法的参数,也是局部变量,作用范围在整个方法中
- 存放在栈区中
- 实例变量
- 实例变量是有默认值的,即使声明完不赋值,它也是有一个默认的值
- byte\short\int\long ----默认值为0
- boolean ----默认值为false
- char ----默认值为空字符 ‘\u0000’
- float\double ----默认值为0.0
- 实例变量的作用范围是当前类中所有的非静态方法中,都可以访问
- 存放在堆区中
- 实例变量是有默认值的,即使声明完不赋值,它也是有一个默认的值
声明–>赋值–>使用
基本类型变量
-
byte,short,int,long,float,double,boolean,char
-
基本数据类型的变量直接包含了单个值,这个值的长度和格式符合变量所属数据类型的要求
-
Java语言中基本类型变量声明时,系统会直接给该变量分配空间,因此程序中可以直接操作
- 在内存中存储的是一个基本类型值
- 可以在栈中直接分配内存
- 例如 int a = 1; 变量a 的值就是int值1
引用类型变量
-
类(class)、接口(interface)、数组【】//除了基本数据类型之外,就是引用类型
-
引用型变量的值是直接指向内存空间的地址,所指向的内存中保存变量所表示的一个值或者一组值,引用在其他语言中被称为指针或者内存地址,Java语言不支持显示的使用内存地址,而必须通过变量名对某个内存地址进行访问
-
引用类型变量声明时,只是给该变量分配引用空间,数据空间未分配,因此引用类型变量声明后不能直接引用
-
引用类型变量在声明后必须通过实例化开辟数据空间,才能对变量所指向的对象进行访问
类和对象
- 类是一套模板,根据模板创建具体的对象
- 学生(类) 张三(对象)
- 类(模板)
- 静态 java:属性
- 姓名 身高 爱好
- 动态 java:方法(函数)
- 打篮球 rap
- 静态 java:属性
MyDate today;
today = new Date();
//第一条语句的执行,将给today变量分配一个保存引用的空间,第二条语句分两个步骤执行,首先执行new Date(),给today变量开辟数据空间,然后再执行第二条语句中的赋值操作
- 对应内存所存储的值是一个引用(地址),是指向对象的存储地址
- 对象的引用在栈中,对象实际存放在堆中
- 例如 Circle circle = new Circle(9) (参数表示半径);变量circle的值保存的是一个引用,它指明这个Circle对象的内容存储在内存的什么位置
操作符
赋值运算符
操作符 | 作用 | 例子 |
---|---|---|
= | 最基础的赋值操作符,=号右边的值,赋值给=左边变量 | int a = 1;int x = 0; |
*= | 一个变量和另一个数据相乘,并把结果再赋值给这个变量 | int a = 1;a*=2;//a=a*2 |
/= | 一个变量和另一个数据相除,并把结果再赋值给这个变量 | int a =2;a/=2;//a=a/2; |
%= | 一个变量和另一个数据相余,并把结果再赋值给这个变量 | int a = 5;a%=2;//a=a%2 |
+= | 一个变量和另一个数据相加,并把结果再赋值给这个变量 | int a =5;a+=2;//a=a+2 |
-= | 一个变量和另一个数据相减,并把结果再赋值给这个变量 | int a = 5;a-=2;//a=a-2; |
a++ 表示自增1的操作 ,同等于a+=1
a– 表示a变量自减1操作,同等于a-=1
a++和++a的区别:
- a++表示先使用a的值进行操作或者运算,然后再让a完成自增
int a = 1;
int b = a++;
System.out.println(a);//输出2
System.out.println(b);//输出1
- ++a表示先让a完成自增,然后再使用a的值进行操作或者运算
int a = 1;
int b = ++a;
System.out.println(a);//输出2
System.out.println(b);//输出2
package com.briup.day2.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/19-07-19-14:56
* @Description:赋值运算符
*/
public class Demo1 {
public static void main(String[] args) {
// 基本赋值运算符 格式代码 ctrl+shift+f
int a = 1;
//a = a*2; // 2
// 衍生赋值运算符
a*=2; // a=a*2
System.out.println(a);
a-=1;
System.out.println("a = " + a);
a+=10;
System.out.println("a = " + a);
a/=5; // 11/5 只保留整数
System.out.println("a = " + a);
a%=2;
System.out.println("a = " + a);
}
}
特殊例子 自增\自减
// 特殊: a+=1 === a++ ++a (自增)
// a-=1 === a-- --a
// 情况 ++在前面 先计算x的值,再赋值
// int x = 1;
// x++; // 2
// ++x; // 3
// System.out.println(x);
// int x = 1;
// int y = ++x;
// System.out.println(y); //2
// 情况 ++在后面 先赋值 再计算
// int x = 1;
// int y = x++; //1
// System.out.println(y); // 1
// System.out.println(x); // 2
int x = 1;
x++; // 2
System.out.println(x++); // 2 x=3
System.out.println(++x); // 4
比较运算符
操作符 | 作用 | 例子 |
---|---|---|
> | 比较是否大于 | 1>0 |
>= | 比较是否大于等于 | 1<=0 |
< | 比较是否小于 | 1<2 |
<= | 比较是否小于等于 | 1<=2 |
instanceof | 判断对象是否属于指定类型 | stu instanceof Student |
instanceof,判断一个指定的对象,是否属于另一个指定的类型
Person o = new Person();
//表示引用o所指向的对象,是不是属于Person类型
System.out.println(o instanceof Person);
package com.briup.day2.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/19-07-19-15:14
* @Description:比较运算符
*/
public class Demo2 {
public static void main(String[] args) {
int a = 1;
int b = 2;
// 结果是布尔值
boolean flag = a <= b; // false
System.out.println(flag);
}
}
等值操作符
操作符 | 作用 | 例子 |
---|---|---|
== | 比较两边的数据是否相等,相等返回true,不相等返回false | 1==2,o1=o2 |
!= | 比较两边的数据是否不相等,相等返回false,不相等返回true | 1!=2,o1!=o2 |
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-9:25
* @Description:等值操作符
*/
public class Demo1 {
// == != 比较
public static void main(String[] args) {
// 基本数据类型(比较值)
System.out.println(1 == 2); // false
System.out.println(1 != 2); // true
// 引用类型(地址)
// 左边是类模板 右边是类的具体对象
Hello hello = new Hello();
Hello hello2 = new Hello();
// 比较两个变量指向的地址
System.out.println(hello == hello2);
System.out.println(hello.toString());
System.out.println(hello2);
// 补充 基本类型只比较值 不管类型
System.out.println(1 == 1.0); // true
System.out.println(97 == 'a');// true
}
}
// 一个java文件可以写多个类 只能有一个public
// public修饰的类的名称 必须是文件名字
class Hello{
}
- 算数
操作符 | 作用 | 例子 |
---|---|---|
+ | 两个值相加 | int a =1+1; |
- | 两个数字相减 | int a = 1-1; |
* | 两个数字相乘 | int a = 1*1; |
/ | 两个数字相除 | int a = 1/1; |
% | 两个数字取余 | int a = 5%2; |
**注意:**使用+号,也可以连接两个字符串,数值也可以和字符串使用+号连接,连接之后的结果还是字符串,负数求余,最终结果符号取决于第一个数
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-9:40
* @Description:算术运算符
*/
public class Demo2 {
public static void main(String[] args) {
// 基本运算
int a = 10;
int b = 4;
System.out.println(a + b); //14
System.out.println(a - b); // 6
System.out.println(a * b); // 40
System.out.println(a / b); // 2 舍小数
System.out.println(a % b); // 2
// 特殊: + 和可以做拼接 比如和字符串使用
// 字符串 + 任何内容 组成新的字符串
String s = "hello";
// "hellocom.briup.operation.Demo1@4554617c"
System.out.println(s + new Demo1());
System.out.println(s+a); //"hello10"
System.out.println(s + "我的值是" + a + "!");
System.out.println(s+5+5); //hello55
System.out.println(s+(5+5)); // hello10
// 注意%对 负数求余 最终结果符号取决于第一个数
System.out.println(10%-4); // 2
System.out.println(-10%4); // -2
}
}
移位运算符
操作符 | 作用 | 例子 |
---|---|---|
>> | 算术右位移运算,也叫做带符号的右移运算 | 8>>1 |
<< | 左位移运算 | 8<<1 |
>>> | 逻辑右位移运算,也叫作不带符号的右移运算 | 8>>>1 |
>> 算术右移位运算
注意,这个操作的本质就是除以2n,这个n就是我们右移的位数。
注意,除以2n之后,只保留整数部分
注意,正数右移之后,最左边空出的位置,都要补0
注意,负数右移之后,最左边空出的位置,都要补1
例如,16 >> 3 结果是2 ,相当于 16 / 2 = 2
<< 左移位运算
注意,这个操作的本质就是乘以2n,这个n就是我们左移的位数
注意,无论正数负数左移之后,最右边空出的位置,都要补0
注意,当左移之后,得到的数字已经超出当前类型所能表示的最大值的时候,这个值最终会被限定
到这个当前类型中,所以最终显示的值会和我们逻辑上算出的值有所不同。
例如:直接使用2进制表示数字
int a = 0b01000000000000000000000000000000;
int result = a<<2; //其实这个结果已经超出了int能表示的最大值
System.out.println(result); //结果是0
特殊情况:
int a = 0b00000000000000000000000000000001;
System.out.println(a<<32); //结果是1 相当于1<<0
System.out.println(a<<33); //结果是2 相当于1<<1
System.out.println(a<<34); //结果是4 相当于1<<2
原因:
如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模/取余。如果对int
型移动33位,实际上只移动了33%32=1位。如果对int型移动32位,实际上只移动了32%32=0位
>>> 逻辑右移位运算,也叫做【不带】符号的右移运算
注意,这个操作的本质就是除以2n,这个n就是我们右移的位数
注意,除以2n之后,只保留整数部分
注意,正数和负数右移之后,最左边空出的位置,都要补0
例如:
12>>>1 结果是 6
-12>>>1 结果是 2147483642
注意:在操作的时候,
java操作的都是计算机中的补码
正数的原码、反码、补码都是一样的
例如:数字1的原码0000 0001,反码0000 0001,补码0000 0001
负数的原码、反码、补码有所不同
例如:数字-1
原码:1000 0001
反码:1111 1110 除了符号位之外,其他按位取反
补码:1111 1111 反码基础上加1
package com.briup.operation;
import java.util.Arrays;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-9:54
* @Description:移位运算符 >> << >>>
*/
public class Demo3 {
public static void main(String[] args) {
// 4 8/2
// 24个0 00001000 正数 原码 反码 补码一样
// 00000000 00000000 00000000 00001000
// 000000000 00000000 00000000 0000100 0
// 有符号右移 正数补0 负数补1
System.out.println(8>>1); // 2
// 10000000 00000000 00000000 00001000
// 11111111 11111111 11111111 11110111 反码+1
// 111111111 11111111 11111111 1111100 0 补码
// 111111111 11111111 11111111 1111011 反码
// 100000000 00000000 00000000 0000100 原码
System.out.println(-8>>1); // -4
// 16 8*2
// 00000000 00000000 00000000 00001000
// 0 0000000 00000000 00000000 000010000
System.out.println(8<<1); // 左位
// 12 >>>1 无符号右移动 一率补0 情况 12>>1
// -12 >>>1 左边补0
// 10000000 00000000 00000000 00001100
// 11111111 11111111 11111111 11110011 反码
// 11111111 11111111 11111111 11110100 补码
// 011111111 11111111 11111111 1111010 0
// 2^31-5 === 2147483647-5
// 特殊情况
int a = 0b00000000000000000000000000000001;
// 00000000000000000000000000000001
// 00000000000000000000000000000001
// 32%32 33%32
System.out.println(a<<32); //结果是1 相当于1<<0
System.out.println(a<<33); //结果是2 相当于1<<1
System.out.println(a<<34); //结果是4 相当于1<<2
}
}
/* private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}*/
位运算符
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-10:19
* @Description:位运算
*/
public class Demo4 {
public static void main(String[] args) {
// & : 两个二进制每一位运算,都为1 结果1
// 24个0 00000001
// 24个0 00000001
// 24个0 00000001
System.out.println(1 & 1); //1
// 24个0 00000001
// 24个0 00000000
// 32个0
System.out.println(1 & 0); //0
// 24个0 00000001
// 24个0 00000011
// 24个0 00100001
System.out.println(1 & 3); // 1
// | : 两个二进制每一位运算,有1个为1 结果1
// 24个0 00000001
// 24个0 00000011
// 24个0 00000011
System.out.println(1 | 3); // 3
// ^ : 两个二进制每一位运算,相同为0 不同1
// 24个0 00000001
// 24个0 00000011
// 24个0 00000010
System.out.println(1 ^ 3); // 2
// ~: 取反
// 0000000000 0000000000 0000000000 0000000001
// 1111111111 1111111111 1111111111 1111111110 补码
// 1111111111 1111111111 1111111111 1111111101
// 1000000000 0000000000 0000000000 0000000010
System.out.println(~1); // -2
}
}
案例
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-10:56
* @Description:交换两个变量的值
*/
public class Demo5 {
public static void main(String[] args) {
// int a = 10;
// int b = 5;
b = a;
a = b;
// // 方式1: 借助中间变量
// int temp;
// temp = a; // 保存a的10
// a = b; // a=5
// b = temp; // b=10;
// System.out.println(a); // 5
// System.out.println(b); // 10
// 第二种方式: 不能使用中间变量
// int a = 10;
// int b = 5;
// // 隐式条件 a和b的结果
// a = a + b; // 15
// b = a - b; // 15-5 = 10
// a = a - b; // 15-10 = 5
// System.out.println(a); // 5
// System.out.println(b); // 10
// 第三种方式: 使用异或 ^ 交换数值
int a = 10;
int b = 5;
a = a^b; // 中间结果
b = a^b;
a = a^b;
System.out.println(a);// 5
System.out.println(b);// 10
}
}
逻辑运算符
连接多个条件
- &&
- 同时为true 才为true
- 短路: 如果前面为false 后面的表达式不会计算
- ||
- 有一个为true 就为true
- 短路: 如果前面为true,后面的表达式不会计算
- & 位运算符
- 同为1 才为1
- | 位运算符
- 有一个为1 就为
计算机会把true识别为1 false为0
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-11:18
* @Description:逻辑运算符
*/
public class Demo6 {
public static void main(String[] args) {
//位运算符 & (与运算)
// && 连接两个布尔表达 拼接多个判断条件(同时成立)
int a = 1;
int b = 5;
// true && true == true
System.out.println(a>0 && b>1);
// false && true == false
System.out.println(a>1 && b>1);
System.out.println(a>1 & b>1);
//位运算符| (或运算)
// || 拼接多个判断条件(有一个为true成立)
// false || true
System.out.println(a>0 || b>1); // true
System.out.println(a>1 || b>1); // true
System.out.println(a>1 | b>1);
// 注意: 短路效果
// a=1 b=5
// false && 后面的表达式不计算
// System.out.println(a++>1 && ++b>0);
// System.out.println(a); // 2
// System.out.println(b); // 5
// true ||
//System.out.println(++a>1 || ++b>0);
System.out.println(++a>1 | ++b>0); // 不短路
System.out.println(a); //2
System.out.println(b); //5
}
}
三目运算符
package com.briup.operation;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-11:38
* @Description:条件运算符
*/
public class Demo7 {
public static void main(String[] args) {
// int num = 0;
// char gender;
// // 0 男 1 女
// if(num == 0){
// gender = '男';
// }else{
// gender = '女';
// }
// 三目运算符
// 表达式?第一种结果:第二种结果
// num==0?'男':'女"
int num = 1;
char gender = num == 0?'男':'女';
System.out.println(gender);
}
}
类型转换
-
浮点数强转为整数时,会把小数部分去掉,与四舍五入没关系
-
手动类型转换
-
高精度–>低精度
-
int e = 128; byte f = (byte)e; //-128 //0000000000000000 10000000 //(补码)10000000(-127-1) //补码转原码时,需要-1 //强转时会把前面的二进制数砍掉,结果会变化,影响精度
-
-
自动类型转换
-
当低精度–>高精度
-
byte c = 1; int d = c; //00000001 //000000000000000000 00000001 //二进制中,低精度转高精度只是在前面添0,并不影响最后结果,可以自动转换类型
-
long l = 0xfffffffffff;
0x表示这个数是16进制数,0表示8进制。
编译器报错,因为右边默认是int,但其超出了范围(没超出int范围的话
编译器会隐式将int转为long),故报错(同样的错误也会出现在float)short s = 123;
(这个123也是int类型,这里,= 操作编译器能隐式转换)
s = s + 123;编译器报错,那是因为s+1是int类型(编译器先将s转化为int,再+1),
这里,+ 操作编译器不能隐式转换(会提示失真,即精度可能会受损),正确的做法:
s = (short)(s + 123)注意,不是(short)s + 123。
流程控制
程序中需要执行的代码,其结构主要分为:
- 顺序结构
- 分支结构
- 循环结构
流程
顺序
顺序结构就是最基本的流程控制,只要将diamante从上到下,按照顺序依次编写即可
分支
- if else
语法1:
if(关系表达式){
语句体
}
//其他代码
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true,就执行语句体
- 如果关系表达式的值为false,则不执行语句体
- 据需执行if代码块后面的其他代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKwv4Y85-1678333215265)(…/1658229931733.png)]
int a = 10 ;
if(a%2 == 0){
System.out.println("变量a的值为偶数");
}
//其他代码
语法2:
if(关系表达式){
语句体1;
}else{
语句体2;
}
//其他代码
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true,就执行语句体1
- 如果关系表达式的值为false,就执行语句体2
- 继续执行if代码块后面的其他代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SKJpsEOO-1678333215266)(…/1658230129471.png)]
int a = 10 ;
if(a%2 ==0){
System.out.println("变量a的值为偶数");
}else{
System.out.println("变量a的值为奇数");
}
if和else形成了一个组合,特点就是如果if中的代码执行了,那么else的代码就不执行,如果if中的代
码没执行,那么else中的代码就会执行。也就是if和else这俩个语句中的代码,【一定】是有唯一的
一个执行,而另一个不执行。
但是,如果有俩个if,那么它们俩个是相互独立切互不影响的俩个结构
int a = 10;
if(a%2==0){
System.out.println("变量a的值为偶数");
}if(a%2==1){
System.out.println("变量a的值为奇数");
}
注意,第一个if条件无论是true还是false,第二个if条都会继续判断,这个逻辑和if-else是不同的
语法3:
if (关系表达式1) {
语句体1;
}else if (关系表达式2) {
语句体2;
}
…else {
//else代码;
}//其他代码
执行流程:
-
首先计算 关系表达式****1 的值
-
如果表达式1的值为true,就执行语句体1;如果值为false就计算关系表达式****2的值
-
如果表达式2的值为true,就执行语句体2;如果值为false就计算关系表达式****3的值
-
…
-
如果没有任何关系表达式为true,就执行else代码
-
如果中间有任何一个关系表达式为true,那么执行完对应的代码语句之后,整个if-elseif-else退出
int a = 10;
if(a>90){
System.out.println("优秀");
}else if(a>80){
System.out.println("良好");
}else if(a>70){
System.out.println("中等");
}else if(a>60){
System.out.println("及格");
}else{
System.out.println("不及格");
}
从上到下依次判断,有一个判断为true执行了代码,那么后续判断都不再执行,如果判断都为false,则执行else语句代码
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-14:31
* @Description:例如:实现一个方法,方法需要传一个参数,表示年份,
* 方法调用完后会返回一个boolean值,表示这个年份是不是闰年。
*/
public class DemoIf1 {
/**
* 整除4并且不能整除100 或者 整除400
* @param year 给定年份 2022
* @return true/false 是否闰年
*/
public static boolean isRenYear(int year){
// if((year % 4 == 0 && year % 100 !=0) || year % 400 == 0 ){
// return true;
// }else{
// return false;
// }
return (year % 4 == 0 && year % 100 !=0) || year % 400 == 0;
}
public static void main(String[] args) {
boolean flag = isRenYear(2022);
if (flag){
System.out.println("闰年");
}else{
System.out.println("平年");
}
}
}
- switch case
switch语句和if很类似,都是用来判断值是否相等,但是switch默认只支持byte、short、int、char这四种类型的比较,JDK8中也允许String类型的变量做对比
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/20-07-20-15:10
* @Description:com.briup.loop
*/
//例如,实现一个方法,方法需要俩个int类型参数,
// 一个表示年份,一个表示月份,
// 方法的返回值也是int,
// 返回值的含义是指定年份指定月份一共有多少天
public class TestSwitch3 {
/**
* @param year 2022 2020
* @param month 2/3 2/3
* @return 具体该年该月的天数
* 2月(闰年 29/平年 28)
* 1 3 5 7 8 10 12 : 31
* 4 6 9 10 : 30
*/
public int getDay(int year,int month){
// 默认 1个月31天
int result = 31;
switch (month){
case 4:
case 6:
case 9:
case 10:
result = 30;
break;
case 2:
// 闰年/平年 29/28 alt+shift+m
result = isRunYear(year)?29:28;
break;
}
return result;
}
// 判断是平年还是闰年
public boolean isRunYear(int year) {
return (year % 4 == 0 && year % 100 !=0) || year % 400 == 0;
}
public static void main(String[] args) {
TestSwitch3 testSwitch3 = new TestSwitch3();
System.out.println(testSwitch3.getDay(2022, 2));
System.out.println(testSwitch3.getDay(2020,2));
System.out.println(testSwitch3.getDay(2022,7));
System.out.println(testSwitch3.getDay(2020,7));
}
}
循环
- for
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-9:19
* @Description:测试for循环
*/
public class TestFor1 {
public static void main(String[] args) {
// test1();
// test2();
// test3();
test4();
}
// 在控制台输出1-5和5-1
public static void test1() {
// 初始化 1
// 终止条件 5
// 循环体 sout(从1-5)
// 控制条件 从1到5 ++
for (int i = 1; i <= 5; i++) {
// 循环体
System.out.print(i + "\t"); // 不换行
}
// i = 6
System.out.println(); // 空行
// fori 5.for 5.forr
for (int i = 5; i >= 1; i--) {
System.out.print(i + "\t"); // 5 4 3
}
// i = 0
}
// 例如,求1-5之间的数据和,并把求和结果在控制台输出
public static void test2(){
// 定义一个变量 sum 1+2+3+4+5
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum = sum + i; // 1+0 1 sum+i
}
System.out.println(sum);
}
// 例如,求1-100之间的偶数和
public static void test3(){
int sum1 = 0;
int sum2 = 0;
for (int i = 1; i <= 100; i++) {
// sum = sum + i;
// 偶数
if (i % 2 == 0){
sum1 += i;
}else {
sum2 += i;
}
}
// System.out.println(i);
System.out.println(sum1);
System.out.println(sum2);
}
// 死循环
public static void test4(){
// 情况1: 反接近于终止条件
// for (int i = 1; i < 10; i--) {
// System.out.println(i);
// }
// 情况2: 主动 无限循环 i
// for(int i = 1;;i++){
// System.out.println(i);
// }
// 情况3:
for(;;){
System.out.println("hello");
}
}
}
- while
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-10:07
* @Description:测试while循环
*/
public class TestWhile1 {
public static void main(String[] args) {
// test1();
// test2();
test3();
}
// 死循环
private static void test3() {
boolean flag = true;
while(flag){
System.out.println("hello");
}
}
// 例如,
// 世界最高山峰是珠穆朗玛峰(8844.43米=8844430毫米),(中止条件)
// 假如有一张足够大的纸,
// 它的厚度是0.1毫米。(初始条件)
// 那么折叠(*2 逼近终止条件)多少次,可以折成珠穆朗玛峰的高度
private static void test2() {
// 1. 折叠次数 ?
double i = 0.1; // 初始条件
int count = 0; // 折叠此时 未知
while(i <= 8844430){
i *= 2; // 折叠 逼近终止条件
count++;
}
// 最终次数
System.out.println("折叠次数" + count);
}
//例如,循环不断的生成[0,9]随机数,
// 直到生成随机数为5的时候,那么就停止这个循环。
private static void test1() {
// 随机数怎么写???
// Math.random() [0,1) 小数
// [0,9] Math.random() * 10 [0,10) 9.6
// double random = Math.random();
// double newR = random * 10; // 9.6
// int newR2 = (int) newR; // double -> int 9
// System.out.println(newR2);
// 一次 [0-9]
//int n = (int) (Math.random() * 10);
int i = -1; //随机数 -> 终止条件
while (i != 5) {
// 注意: 将完整的随机数 强转
i = (int) (Math.random() * 10); // == 5
System.out.println(i);
}
// i == 5
}
}
- do while
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-10:51
* @Description:do while
*/
public class TestDoWhile {
public static void main(String[] args) {
// test1();
test2();
}
private static void test2() {
int i = -1;
do{
// 此时虽然i不符合循环条件,但是会先输出一次
System.out.println(i); // i
}while (i > 0);
}
// 例如,循环不断的生成[0,9]随机数,
// 直到生成随机数为5的时候,那么就停止这个循环。
private static void test1() {
int i = -1; // 初始化
do{
i = (int)(Math.random()*10); // 5 4
System.out.println(i);
}while(i != 5);
// i == 5;
}
}
嵌套循环
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-11:02
* @Description:测试嵌套循环
*/
public class TestForInFor {
public static void main(String[] args) {
// test1();
// test2();
// test3();
// test4();
test5();
}
/**
* *
* ***
* *****
* *******
*/
private static void test5() {
int n = 5;
// 外循环
for (int i = 1; i <= n; i++) {
// 空格列
for (int j = 1; j <= n - i; j++) {
System.out.print(" "); // j和i的关系 j = n-i
}
// *列
// 内循环 j和i的关系
// 1 1 : 2 3 : 3 5 j=2i-1
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
}
/**
* *
* **
* ***
* ****
* *****
* // 二位 嵌套循环
*/
private static void test4() {
int n = 5;
// 外循环
for (int i = 0; i < n; i++) {
// 内循环 多行 多少列 5 5 j和i有关系
for (int j = 0; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
// 例如,输出5行,每行10个五角星
// 嵌套循环 5行10列
// 外循环 5行
// 内循环 10列
private static void test3() {
// 外循环 行数
for (int i = 1; i <= 5; i++) {
// 内循环 列数 10
for (int j = 1; j <= 10; j++) {
System.out.print("★");
}
// 第1行的10列结束
System.out.println();
}
}
// 例如,在同一行,输出10个五角星 (循环)
private static void test2() {
//System.out.print("★");
for (int i = 1; i <= 10; i++) {
System.out.print("★");
}
}
// 例如,输出5个空行
private static void test1() {
// 注意: 如果不使用i 最终5次 0 1 2 3 4
for (int i = 0; i < 4; i++) {
System.out.println(); // 空行
}
}
}
控制
break
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-11:33
* @Description:循环控制 - break 结束for/while的当前循环
*/
public class TestBreak {
public static void main(String[] args) {
// test1();
test2();
}
private static void test2() {
boolean flag = true;
int i = 0;
while (flag){
i++;
System.out.print(i + "\t");
if(i == 5){
// break;
flag = false;
}
}
}
// 从0-10的循环,如果5,提前退出循环
private static void test1() {
for (int i = 1; i <= 10; i++) {
// i == 5 提前退出循环
System.out.print(i + "\t"); // 1 2 3 4 5
if (i == 5){
break;
}
}
// 正常退出循环
}
}
continue
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-11:40
* @Description:continue: 结束本次循环,继续下一次循环
*/
public class TestContinue {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
// System.out.print(i + "\t"); // 5不输出
if(i == 5){
continue; // 5跳过 继续6
}
System.out.print(i + "\t"); // 5不输出
}
}
}
label
package com.briup.loop;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-11:45
* @Description:label 解决嵌套循环
*/
public class TestLabel {
public static void main(String[] args) {
// test1();
// test2();
test3();
}
// break + label 可以跳出任意循环
private static void test3() {
loop:for (int i = 1; i <= 3; i++) {
loop2:for (int j = 1; j <= 4; j++) {
System.out.println("i= " + i + ", j= " + j);
if(j == 2){
// 只能退出当前循环 内循环
break loop;
}
}
}
}
private static void test2() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++) {
if(j == 2){
continue;
}
System.out.println("i= " + i + ", j= " + j);
}
}
}
// break的问题 : 无法退出外循环 只能退出当前循环
private static void test1() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++) {
System.out.println("i= " + i + ", j= " + j);
if(j == 2){
// 只能退出当前循环 内循环
break;
}
}
}
}
}
return
private static void test4() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++) {
System.out.println("i= " + i + ", j= " + j);
if(j == 2){
// 中断整个方法
return;
}
}
}
// 如果使用了return ,后面的代码无法执行
}
案例
1.程序运行后,用户可多次查询星期对应的减肥计划,直到输入0,程序结束
switch的break 退不出while循环
package com.briup.loop;
import java.util.Scanner;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-14:16
* @Description:com.briup.loop
*/
public class Demo1 {
public static void main(String[] args) {
test1();
}
// 例如,程序运行后,用户可多次查询星期对应的减肥计划,直到输入0,程序结束
// 分支结构 1- 2- 3- 4- 0-结束 switch case
// 循环结构 多次 while(true)
// 输入: 控制台输入 Scanner
private static void test1() {
// 从标准输入流 读文本 (键盘) 模板类 = 对象
Scanner sc = new Scanner(System.in);
// 从键盘读的值
// 分支: 根据数字 -> 减肥计划
boolean flag = true;
int i = -1; // 变量放循环外边
loop:while (flag){
System.out.println("请输入: 1-篮球,2-rap,3-唱,4-跳,0-退出");
i = sc.nextInt(); // 阻塞流(等待输入)
switch (i){
case 1:
System.out.println("篮球");
break;
case 2:
System.out.println("rap");
break; // switch的break 退不出while循环
case 3:
System.out.println("唱");
break;
case 4:
System.out.println("跳");
break;
case 0:
// 退出循环
flag = false;
// 2.break loop;
// return; 不推荐使用
default:
System.out.println("我也不会了");
break;
}
}
System.out.println("拜拜!!!!");
}
}
2.完成猜数字游戏
程序自动生成一个1-100之间的数字,使用程序实现猜出这个数字是多少
当猜错的时候根据不同情况给出相应的提示
- 如果猜的数字比真实数字大,提示你猜的数据大了
- 如果猜的数字比真实数字小,提示你猜的数据小了
- 如果猜的数字与真实数字相等,提示恭喜你猜中了
此时使用了基类 Scanner和Random
package com.briup.loop;
import java.util.Random;
import java.util.Scanner;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/21-07-21-14:43
* @Description:Random对象 随机数
*/
public class TestRandom {
public static void main(String[] args) {
// test1();
test2();
}
private static void test2() {
// 随机数 1-100
// 三种情况 分支
// 循环
System.out.println("系统生成了随机数[1-100],请猜测");
// 系统生成的值
Random random = new Random();
int r = random.nextInt(100)+1; // [1,101)
System.out.println("隐藏值: " + r);
boolean flag = true;
// 一直猜数
Scanner sc = new Scanner(System.in); // 猜值
while (flag){
// 玩家输入的值 n
int n = sc.nextInt(); // 输入的值
if (n > r){
System.out.println("太大了");
}else if(n < r){
System.out.println("太小了");
}else{
System.out.println("猜中了");
flag = false; // 退出循环 break;
}
}
}
private static void test1() {
// 随机数对象 3-7 8-20
Random random = new Random();
// 随机数 [0,10) -> 1 [1+10]
for (int i = 0; i < 50; i++) {
int n = random.nextInt(10)+1;
System.out.print(n + "\t");
}
}
}
其他
- Random输出[0,9)
- Scanner
数组
概述
一块连续的内存空间,存储相同的数据数据,里面的每个数据称为"元素"
数组没有自带方法,都是继承自Object父类
定义
任意类型 + [] === 数组类型
- 基本类型
- 引用类型
变量
int[] arr; String[] ss;
int arr2[];
对象
package com.briup.arr;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/22-07-22-9:08
* @Description:数组对象
*/
public class TestArr1 {
public static void main(String[] args) {
// int类型数组
int[] arr; // 声明
// new 关键字创建类的模板对象
// 其他情况1: System.out 方法内创建对象
// 其他情况2: String s = ""; = new String("");
arr = new int[5]; // new对象时需要指定数组容量
// 对象中的属性和方法(来自父类)
int len = arr.length; // 唯一属性: 数组的长度
System.out.println(len);
// 引用类型
System.out.println(arr); // [I@4554617c
}
}
特性
- 长度 []的值 固定
- 下标 0 — length-1
- 默认值 和类型有关
package com.briup.arr;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/22-07-22-9:32
* @Description:数组的特性
*/
public class Test2 {
public static void main(String[] args) {
// 1.长度
// 创建时指定(可以为0 不能为负数) 无法修改
// int[] arr = new int[-1]; // 编译不报错 (运行时异常)
int[] arr = new int[5];
System.out.println(arr);
// 2.下标范围 0 --- 数组.length-1
int a1 = arr[0];
int a2 = arr[1];
int a3 = arr[2];
int a4 = arr[3];
int a5 = arr[4];
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println(a4);
System.out.println(a5);
//ArrayIndexOutOfBoundsException 此处没有5的索引
// int a6 = arr[5];
// int a6 = arr[-1];
// 3.默认值
float[] arr2 = new float[3]; // 默认值是0.0
System.out.println(arr2[0]);
boolean[] arr4 = new boolean[3]; // 默认值false
System.out.println(arr4[0]);
String[] arr3 = new String[3]; // 默认值是null
System.out.println(arr3[0]);
}
}
创建方式
package com.briup.arr;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/22-07-22-9:47
* @Description:数组的创建方式 (创建时 指定值)
*/
public class Test3 {
public static void main(String[] args) {
// 1.方式1 先声明 后赋值 默认值
int[] arr;
arr = new int[5];
// 2. 声明并赋值 默认值
int[] arr2 = new int[5];
// 3. 声明并赋值 并给定元素值 此时中括号不能写长度
//{2,5,7,4} 隐藏条件: 长度为4
int[] arr3 = new int[]{2,5,7,4};
System.out.println(arr3[0]);
System.out.println(arr3[1]);
System.out.println(arr3[2]);
System.out.println(arr3[3]);
// 4.简写
int[] arr4 = {5,3,7,1};
// 遍历数组 [0-4) 次数 4
for (int i = 0; i < arr4.length; i++) {
System.out.print(arr4[i] + "\t");
}
}
}
排序
冒泡排序
package com.briup.arr;
import java.util.Arrays;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/22-07-22-10:22
* @Description:排序
*/
public class Sort {
public static void main(String[] args) {
int[] arr = {2, 1, 4, 9, 8, 5, 3};
// 排序后的结果 1 2 3 4 5 8 9
// 冒泡排序
bubberSort(arr);
// 选择排序
// 插入排序
}
/**
* 冒泡排序 2,1,4,9,8,5,3 顺序
* 每次相邻的两个数 21 14 49
* 如果前面的数比后面的大 交换顺序(大值在后面)
* <p>
* 每轮 n次 最终 只能确定一个最大值 放最后
*/
private static void bubberSort(int[] arr) {
// 2,1,4,9,8,5,3 6轮 arr.length-1轮
// 外循环 轮数 每轮只确定最大的数 放后面
// [0,6)
for (int i = 0; i < arr.length - 1; i++) {
// 内循环 次数 该轮多少次 比较出最大值
// 16 25 34 数组需要从0
// 06 15 24 33 42 51 j和i的关系j=arr.length-1-i
for (int j = 0; j < arr.length - 1 - i; j++) {
// 每一轮的相邻2个值作比较 前>后 交换
if (arr[j] > arr[j + 1]) {
// 交换顺序
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
// 每次的情况
System.out.println("第" + i + "轮 第" + j + "次的结果 " + Arrays.toString(arr));
}
// 每轮结束后的情况 Arrays. 重写规则 一行显示数组
System.out.println("第" + i + "的结果" + Arrays.toString(arr));
System.out.println();
}
}
}
结果
第0轮 第0次的结果 [1, 2, 4, 9, 8, 5, 3]
第0轮 第1次的结果 [1, 2, 4, 9, 8, 5, 3]
第0轮 第2次的结果 [1, 2, 4, 9, 8, 5, 3]
第0轮 第3次的结果 [1, 2, 4, 8, 9, 5, 3]
第0轮 第4次的结果 [1, 2, 4, 8, 5, 9, 3]
第0轮 第5次的结果 [1, 2, 4, 8, 5, 3, 9]
第0的结果[1, 2, 4, 8, 5, 3, 9]
第1轮 第0次的结果 [1, 2, 4, 8, 5, 3, 9]
第1轮 第1次的结果 [1, 2, 4, 8, 5, 3, 9]
第1轮 第2次的结果 [1, 2, 4, 8, 5, 3, 9]
第1轮 第3次的结果 [1, 2, 4, 5, 8, 3, 9]
第1轮 第4次的结果 [1, 2, 4, 5, 3, 8, 9]
第1的结果[1, 2, 4, 5, 3, 8, 9]
第2轮 第0次的结果 [1, 2, 4, 5, 3, 8, 9]
第2轮 第1次的结果 [1, 2, 4, 5, 3, 8, 9]
第2轮 第2次的结果 [1, 2, 4, 5, 3, 8, 9]
第2轮 第3次的结果 [1, 2, 4, 3, 5, 8, 9]
第2的结果[1, 2, 4, 3, 5, 8, 9]
第3轮 第0次的结果 [1, 2, 4, 3, 5, 8, 9]
第3轮 第1次的结果 [1, 2, 4, 3, 5, 8, 9]
第3轮 第2次的结果 [1, 2, 3, 4, 5, 8, 9]
第3的结果[1, 2, 3, 4, 5, 8, 9]
第4轮 第0次的结果 [1, 2, 3, 4, 5, 8, 9]
第4轮 第1次的结果 [1, 2, 3, 4, 5, 8, 9]
第4的结果[1, 2, 3, 4, 5, 8, 9]
第5轮 第0次的结果 [1, 2, 3, 4, 5, 8, 9]
第5的结果[1, 2, 3, 4, 5, 8, 9]
选择排序
/**
* 选择排序
* 每轮 选出最小的值 放在当前轮数位置 第几轮的最小值 就放第一个位置
* 轮数 arr.length-1轮
* 每次 从未排序列找出最小值
* 16 25 34 43 52 j = arr.length-1
* 06 15 24 33 42 j = arr.length-1-i
*/
private static void selectSort(int[] arr) {
// 1 2 3 4 5 8 9
// 6轮
for (int i = 0; i < arr.length - 1; i++) {
// 每轮假设第一个值 是最小值 记录的是下标
// int min = arr[i];
int min = i;
// 1 2 4 9 8 5 3 1-7) 1 2 3 4 5 6
for (int j = i + 1; j < arr.length; j++) {
// 内循环的目的是找出未排序的最小值(下标)
// 第一个值 和 后面的所有值比
// arr[j] > arr[j+1]
if (arr[min] > arr[j]) {
min = j; // min = 1 不能这么写
}
}
// 找出了未排序的最小值 和第一个交换
// 交换 arr[min] 真实最小值 arr[i] 每轮假设第一个最小值
if(min != i){
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
System.out.println("第" + i + "轮的结果 " + Arrays.toString(arr) );
}
System.out.println(Arrays.toString(arr));
}
第0轮的结果 [1, 2, 4, 9, 8, 5, 3]
第1轮的结果 [1, 2, 4, 9, 8, 5, 3]
第2轮的结果 [1, 2, 3, 9, 8, 5, 4]
第3轮的结果 [1, 2, 3, 4, 8, 5, 9]
第4轮的结果 [1, 2, 3, 4, 5, 8, 9]
第5轮的结果 [1, 2, 3, 4, 5, 8, 9]
[1, 2, 3, 4, 5, 8, 9]
插入排序
/*
* 规则:
* 将数组划分成2组 左边一组是已排序 右边是未排
* 默认第一个值是已排
*
* 新值,从未排的当前值与已排所有值比较,找到合适的位置插入
* 默认当前索引是最合适的位置
* 合适的位置: 当前值比左边的所有值小,直到更大停止
*
* 外层循环6 比较此时
*
* 内层循环
* 用当前值和已排所有值比较
* 21 32 43 54
*/
private static void insertSort(int[] arr) {
// [1,7) i==5 0 1 1 1 1 1 1
for (int i = 1; i < arr.length; i++) {
int shouldIndex = i; // 默认当前是应该的位置
int cunrrentV = arr[i]; // 临时值 记录当时的值 情况
// i=1 j=1-1=0
for (int j = i-1; j >= 0 ; j--) {
// 从未排的当前值与已排所有值比较,找到合适的位置插入
// 注意: 此处要换备份值
if(cunrrentV < arr[j]){
// 将j对应的值往后移动 将i的值放入j的位置
arr[j+1] = arr[j]; //arr[5] // 注意: arr[i]值变了
// 2 2 4 8 9 5 3 arr[5] = arr[6]
// 1 2 4 * 8 9 5 3 [6]
shouldIndex = j; // 合适的位置
}else{
break; // 比到4就不比了
}
}
// 将值放入合适的位置 此处要换备份值
if(shouldIndex != i){
arr[shouldIndex] = cunrrentV; // 5
}
}
System.out.println(Arrays.toString(arr));
}
Arrays
操作数组的工具类
package com.briup.arr;
import java.util.Arrays;
import java.util.List;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/25-07-25-14:12
* @Description:数组工具类 : 静态方法(static) 类.静态方法
*/
public class TestArrays {
public static void main(String[] args) {
int[] arr = {1,7,5,3};
// 1.toString: 将数组变成规则的字符串
// String: [元素,元素]
System.out.println(Arrays.toString(arr));
// arr换了地址指向
// 2.copyOf 复制原数组的值到新数组中
// arr = Arrays.copyOf(arr, arr.length * 2);
arr = Arrays.copyOf(arr, arr.length << 1);
System.out.println(Arrays.toString(arr));
// 3.排序方法
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 4. equals 比较两个数组中的元素相等
int[] a = {1,2,3};
int[] b = {1,2,3};
// == (引用类型)默认比较的是地址
System.out.println(a == b);
boolean flag = Arrays.equals(a, b);
System.out.println(flag);
// 5. 查找5在arr中索引位置 前提: 排序
System.out.println(Arrays.binarySearch(arr, 5));
// 6.将10 填充到数组的每个元素
Arrays.fill(arr,10);
System.out.println(Arrays.toString(arr));
// 7.数组 -> 集合
// int ... a === int[] a
// 语法糖
List<Integer> list = Arrays.asList(1,2,3);
System.out.println(list);
}
}
可变参
package com.briup.arr;
import java.util.Arrays;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/25-07-25-14:48
* @Description:可变参
*/
public class TestVariableParam {
public static void main(String... args) {
int[] arr = {1,7,4};
// sort(arr);
// sort(1,7,4,5); // 长度不固定
sort(); // new int[0];
sort2("hello",1,2,5);
}
// 特殊情况 多参不同类型,可变参放后面 1,2,"a"
public static void sort2(String s,int...arr){
}
public static void sort(int ... arr){
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
// public static void sort(int[] arr){
//
// }
}
类和对象
成员变量
变量和属性
- name
- setName
package com.briup.obj;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-14:07
* @Description:com.briup.obj
*/
public class Student {
String name; // 成员变量
public Student(){} // 默认构造器
// 成员方法
public void hello(){
System.out.println("name = " + name);
}
public static void main(String[] args) {
// 调用无参构造器 创建对象 并用变量接收
Student s = new Student();
// 对象调用成员
s.name = "tom";
s.hello();
}
}
成员方法
public int hello(int a) throws IOException{
return 0;
}
参数传递
基本类型传递 情况1
基本类型 情况2 拷贝的是副本给形参
引用类型 情况3
引用类型 情况4
引用类型: 情况5
该例子能证明 java总是按值传递
注意: 字符串是较为特殊的引用
递归
- 1.方法调用自己
- 2.终止条件
- 3.变量无限接近终止条件
package com.briup.method;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-15:59
* @Description:递归
*/
/*
1.方法调用自己
2.终止条件
3.变量无限接近终止条件
*/
public class Method6 {
int a = 0;
public void test(){
a++;
System.out.println(a);
// 递归调用
if(a == 10){
return; // 退出方法
}
test();
}
public static void main(String[] args) {
new Method6().test();
}
}
package com.briup.method;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-16:07
* @Description:com.briup.method
*/
public class Method7 {
// 例如,使用递归,完成从1累加到100
// 100
// y=f(x) 递归100 = 100 + test(99)
// 递归99 = 99 + test(98)
// 递归98 = 98 + test(97)
// 递归2 = 2 + test(1)
// 递归1 = 1;
public int test(int n){
int result = 0;
if(n == 1){
// 终止条件 n-1 = 0
return 1;
}
result = n + test(n-1);
return result;
}
public static void main(String[] args) {
System.out.println(new Method7().test(5));
}
}
重载
- 重载一般在同类中
- 重载只跟参数有关
- 个数
- 类型
- 顺序
- 重载跟修饰符和返回值无关
package com.briup.method;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-16:26
* @Description:方法重载
*/
public class Method8 {
// 重载只和参数有关系
// 参数个数不同
// 参数类型不同
// 参数顺序不同
public void test(){}
public void test(int a){}
public void test(String b){}
public void test(int a,String b){}
public void test(String b , int a){}
// 注意: 同类型不行
// public void test(int a,int b){}
// public void test(int b,int a){}
// 返回值不构成重载
// public int test(){
// return 0;
// }
// 修饰符不构成重载
// int test(){
// return 0;
// }
public static void main(String[] args) {
// 区分调用的哪个test
new Method8().test();
new Method8().test(1);
}
}
注意事项
package com.briup.method;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-16:26
* @Description:方法重载
*/
public class Method8 {
int a = 8;
// 重载只和参数有关系
// 参数个数不同
// 参数类型不同
// 参数顺序不同
public void test(){}
// 如果传的是byte 就近原则
public void test(short a){
// 局部 -> 成员
// short -> int
System.out.println(a);
System.out.println("Method8.test short");
}
public void test(int a){
System.out.println("Method8.test int ");
}
public void test(String b){}
public void test(int a,String b){}
public void test(String b , int a){}
// 注意: 同类型不行
// public void test(int a,int b){}
// public void test(int b,int a){}
// 返回值不构成重载
// public int test(){
// return 0;
// }
// 修饰符不构成重载
// int test(){
// return 0;
// }
// 注意: 类型可兼容
public void test(int a , long b){ }
public void test(long a , int b){ }
// (4 + 4) >> 1
public static void main(String[] args) {
// 区分调用的哪个test
Method8 method8 = new Method8();
// method8.test();
// method8.test(1);
// method8.test(1,1L); // test(int a , long b)
// method8.test(1L,1);
// System.out.println(2);
method8.test((byte)1);
}
}
构造器
特点:
- 方法名称和类名一致
- 没有返回类型,也不能
作用:
- 创建对象(new+构造方法)
- 可以初始化(new+有参构造)
重载:
构造器也叫构造方法,如果没有自定义有参构造器,系统默认提供无参构造器,重载后的参数不同
创建和初始化对象的过程:Student s = new Student()
- 对Student进行类加载,同时初始化类中静态的属性赋默认值,给静态方法分配内存空间
- 堆中分配对象的内存空间,同时初始化对象中的非静态的属性赋默认值
- 调用父类构造器
- int age = 10 ----对Student中的属性进行显示赋值
- 执行匿名代码块
- 执行构造器代码块
- = 赋值操作,把对象的内存地址赋给变量s
package com.briup.constructor;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-16:47
* @Description:构造器
*/
public class Constructor1 {
String name;
// 系统默认提供无参构造器,如果自定义了构造器,系统不再提供
public Constructor1(){}
// 自定义构造器
public Constructor1(String n){
// 左边的name是成员变量
// 右边的n是局部变量
name = n;
}
public static void main(String[] args) {
// 作用: 创建对象并初始化
Constructor1 constructor = new Constructor1();
Constructor1 constructor1 = new Constructor1("briup");
// System.out.println(constructor1.name);
}
}
构造中间的传递: 借助this关键字
String name;
int age;
// 系统默认提供无参构造器,如果自定义了构造器,系统不再提供
public Constructor1(){
// 无参构造器中调用了有参构造器
this("vanse"); // this代表当前对象的引用
}
// 自定义构造器
public Constructor1(String n){
// 左边的name是成员变量
// 右边的n是局部变量
//name = n;
this(n,10);
}
public Constructor1(String n , int a){
name = n;
age = a;
}
💁♂
注意: 构造器之间调用使用this,只能放第一行
this
在类中的非静态方法中,可以使用this关键字,来表示当前类将来的一个对象。
非静态方法
类加载:
- field(字段)
- private (getter/setter) -> JavaBean
- protected(父类)
- method(方法)
- public
- private(本类方法)
- constructor(构造器)
- public ->才能调用构造器,创建对象
- private ->无法调用构造器 创建对象(单例模式)
- class
- 外部类(相对的外层是包)
- public ->任何情况都可以创建对象
- default(默认) ->本包中才能创建对象
- 内部类
- private(常见)
- public(常见)
- protected
- default
- 外部类(相对的外层是包)
区分局部变量
package com.briup.testthis;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-17:14
* @Description:this
*/
public class TestThis1 {
String name = "tom";
// 用法1: 区分成员变量和局部变量
public TestThis1(){}
public TestThis1(String name){
// this代表当前对象的引用
// this.name = name;
System.out.println(name); // 默认就近原则 局部变量 briup
System.out.println(this.name); //tom
}
public static void main(String[] args) {
TestThis1 testThis1 = new TestThis1("briup");
//System.out.println(testThis1.name);
}
}
调用其他方法
package com.briup.testthis;
/**
* @Auther: vanse(lc))
* @Date: 2022/7/26-07-26-17:14
* @Description:this
*/
public class TestThis1 {
String name = "tom";
// 用法1: 区分成员变量和局部变量
public TestThis1(){}
public TestThis1(String name){
// this代表当前对象的引用
// this.name = name;
System.out.println(name); // 默认就近原则 局部变量 briup
System.out.println(this.name); //tom
}
// 作用2: 在非静态方法中调用另一个非静态方法
public void test1(){
System.out.println("TestThis1.test1");
// 还想调用test2();
// TestThis1 testThis1 = new TestThis1("briup");
// testThis1.test2();
this.test2(); // this 可以省略不写
}
public void test2(){
System.out.println("TestThis1.test2");
}
public static void main(String[] args) {
TestThis1 testThis1 = new TestThis1("briup");
//System.out.println(testThis1.name);
testThis1.test1();
}
}
调用构造器
属性
方法
Java方法的参数,分为形参和实参
形参:
- 形式上的参数
public void test(int a){}
其中,参数a就是test方法形式上的参数,它的作用就是接收外部传过来的实际参数的值
实参:
- 实际上的参数
public class Test{
public void test(int a){}
}
public static void main (String[] args){
Test t = new Test();
t.test(1);
int x = 10;
t.test(x);
}
其中,调用方法的时候,所传的参数1和x,都是test方法实际调用时候所传的参数,简称实参
值传递:Java中的参数传递都为值传递
方法的参数是基本类型,调用方法并传参,这时候进行的是值传递
public class Test{
//该方法中,改变参数当前的值
public static void changeNum(int a){
a= 10;
}
public static void main(String[] args){
int a = 1 ;
System.out.println("before:a = "+a);//传参之前,变量a的值
changeNum(a);
System.out.println("after:a = "+a)//传参之后,变量a的值
}
}
值传递,实参把自己存储的值(基本类型都是简单的数字)赋值给形参,之后形参如何操作,对实参一点影响都没有
引用传递:
方法的参数类型是引用类型,调用方法并传参,这时候进行的是引用传递
这时候之所以称之为引用传递,是因为参数和形参都是引用类型变量,其中保存都是对象在堆区中的内存地址
public class Test{
//该方法中,改变引用s所指向对象的name属性值
public static void changeName(Student s){
s.name = "tom";
}
public static void main (String[] args){
Student s = new Student();
System.out.println("before:name="+s.name);
//传参之前,引用s所指向对象的name属性值
changeName(s);
System.out.println("after:name="+s.name);
//传参之后,引用s所指向对象的name属性值
}
}
由于引用传递,是实参将自己存储的对象地址,赋值给了形参,这时候两个引用(实参和形参)指向了同一个对象,那么任何一个引用(实参或形参)操作对象,例如属性复制,那么另一个引用(实参或形参)都可以看到这个对象中属性的变量,因为两个引用指向同一个对象----这个时候就相当于两个遥控器,同时控制一台电视机
递归
规则:
- 方法自己调用自己
- 需要推断递归的规则
- 需要有退出条件,否则会无限循环
- 需要有个变量,无限逼近这个条件
优点:
- 节省代码
缺点:
- 内存耗用多
- 效率低
重载
**概念:**类中有多个方法,具有相同的方法名,但是方法的参数各不相同
- 方法的名字必须相同
- 方法的参数列表必须不同
- 参数的类型不同
- 参数的个数不同
- 参数的顺序不同
- 方法的修饰符,返回类型,抛出异常这些地方没有限制(可以相同,也可以不同,但一般都是相同的)
重写
- 方法名必须相同
- 参数列表必须相同
- 访问控制修饰符可以被扩大,但是不能被缩小
- public>protected>default>private
- 方法抛出异常类型的范围可以被缩小,但是不能被扩大
- ClassNotFoundException–>扩大–>Exception
- Exception–>缩小–>ClassNotFoundException
- 返回类型可以相同,也可以不同
- 如果父类的返回类型是引用类型,子类重写后的方法返回类型可以和父类方法的返回类型保持一致,也可以是父类方法返回类型的子类型
- 例如,父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型
- 如果父类的返回类型是基本类型,那么子类重写后的返回类型必须和父类的保持一致
- 例如: 父类方法的返回类型是int,子类重写后的返回类也必须是int
- 如果父类的返回类型是引用类型,子类重写后的方法返回类型可以和父类方法的返回类型保持一致,也可以是父类方法返回类型的子类型
- static修饰的方法不能被重写
- final修饰的方法不能重写
- 父类的方法如果是private修饰,子类的方法是新方法
关键字
this
**含义:**表示当前类将来的一个对象
作用:
- 区分成员变量和局部变量
- 调用其他方法
- 调用其他构造方法(此时this代码必须放在第一行)
区别成员变量和局部变量:
public class Student{
public String name;
public void setName(String name){
//=号左边的this.name,便是类中的成员属性name
//=号右边的name,表示当前方法的参数name(就近原则)
this.name = name;
}
}
调用类中的其他方法:
public class Student{
public String name;
public void setName(String name){
this.name = name ;
}
public void print(){
//表示调用当前类中的setName方法
this.setName("tom")
}
}
调用类中的其他构造器:
public class Student{
public String name;
public Student(){
//调用一个参数的构造器,参数的类型是String
this("tom");
}
public Student(String name){
this.name=name;
}
}
注意:this的这种调用构造器的用法,只能在类中的一个构造器,调用另一个构造器,并且不能在普通的方法中调用类的构造器,并且要求,this调用构造器的代码,是当前构造器中的第一句代码,否则编译报错
this关键字的意义:
this代表,所在类的当前对象的引用(地址值),即对象自己的引用
public class Student{
public void sayHello(){
}
public void show(){
}
}
public static void main(String[] args){
Student stu = new Student();
stu.sayHello();
}
Student
类中定义了俩个方法,sayHello和show,想要调用这俩个方法,就需要创建 Student 类的对象,然后使用对象调用这俩个方法。
这里就包含了一个固定的规则:类中的非静态方法,一定要使用类的对象来进行调用,没有其他的方式!
那么在这个情况下,思考一个问题:如果我们使用使用了stu调用了sayHello方法,例如stu.sayHello() ,那么在sayHello中,怎么再调用到这个对象中的show方法?
这个时候,如果在sayHello方法中,能有一个变量,可以表示stu这个对象本身,是不是就可以调用到了show方法?毕竟只有对象自己,才能调用到自己的方法,没有其他方式!
所以,这时候,就可以在sayHello方法中,使用this来表示stu对象本身,那么this.show(),就可以表示这个对象调用自己的show方法了
public class Student{
public void sayHello(){
this.show();//这个this就表示当前类的对象stu
}
public void show(){
}
}
public static void main(String[] args){
Student stu = new Student();
stu.sayHello();
}
也就是说,stu里面存的是0x123,this里面存的也是0x123
如果这时候创建两个对象,那么this又代表哪一个对象呢?
public class Student{
public void sayHello(){
this.show();//问题:这个this代表的是stu1还是stu2
}
public void show(){
}
}
public static void main(String[] args){
Student stu1 = new Student();
stu1.sayHello();
Student stu2 = new Student();
stu2.sayHello();
}
//两个都会代表,输出二者
==可以看出,其实每一个对象中,都有自己的this,和其他对象中的互不干扰,当前执行stu1.sayHello()代码的时候,this代表的就是stu1,当执行stu2,.sayHello()代码的时候,this代表的就是stu2
super
**含义:**代表父类对象
作用:
- 访问父类的属性
- 调用父类的方法
- 调用父类中的构造器(Super代码必须放在第一行)
**重点注意:**this和super这两种调用构造器的代码不能同时出现,因为都必须放在第一行,会产生冲突
static
静态属性
**所属:**静态属性属于类,可以用类调用,也可以用对象调用,非静态属性属于对象,只能用对象调用
**定义:**类中所有对象共享静态属性, 不同对象有各自的非静态成员变量
**存储位置:**静态属性存储在方法区中,非静态属性存储在堆中
**初始化:**静态属性在类加载到方法区时,已经完成初始化赋值,所以可以直接用类名获取属性,非静态属性在创建对象后完成初始化赋值,所以需要通过对象来访问该属性
静态方法
静态方法不能调用非静态方法,非静态方法可以调用静态方法
静态代码块
**作用:**给静态属性做初始化
执行时刻:
- 不能主动调用,它会在类加载的时候,自动执行
- 静态代码块只会自动被执行一次
优先于main方法执行
匿名代码块
会还原到构造器中执行,给非静态属性初始化
静态导入
jdk1.5以上才能使用
在自己的类中,要使用另一个类中的静态属性和静态方法,那么可以进行静态导入,导入完成后,可以直接使用这个类中的静态属性和静态方法,而不用在前面加上类名
除了main方法,静态声明和静态代码块按照先后顺序执行
final
**修饰类:**用final修饰的类不能被继承(String)
**修饰方法:**用final修饰的方法可以被子类继承,但是不能被子类重写(getClass)
修饰变量:
- 用final修饰的变量就变成了常量,并且它只能被赋一次值,第二次赋值就会报错
- 局部变量
- 非静态成员变量:JVM不再为其进行默认赋值
- 静态成员变量:JVM不再为其进行默认赋值
astract
修饰方法:
如果abstract修饰方法,那么该方法就是抽象方法
特点:
- 只有方法的声明:public abstract void run();
- 没有方法的实现
修饰类:
- 如果abstract修饰类,那么该类就是抽象类
- 有抽象方法的类,一定要声明为抽象类
- 抽象类和非抽象类
- 抽象类使用了abstract修饰符,而非抽象类没有使用
- 抽象类中可以编写抽象方法,而非抽象类中不能编写抽象方法
- 抽象类不能进行实例化创建对象,而非抽象类可以实例化创建对象
- 抽象类不能实例化,但是有构造器
abstract 不能和 final 一起使用
封装、继承、多态
封装
封装是把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,就不提供方法给外界访问
作用
- 访问控制
- 减少耦合
- 隐藏信息,实现细节
- 代码复用
修饰符
修饰符 | 同类 | 同包非子类 | 同包子类 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y | N |
不写(默认) | Y | Y | Y | N | N |
private | Y | N | N | N | N |
public : 任何位置都可以访问
protected: 子类即可访问
默认: 同包可以访问
private: 自身类可以访问
注意: 默认修饰是不写 不是default
public class Husband {
/*
* 对属性的封装
* 一个人的姓名、性别、年龄、妻子都是这个人的私有属性
*/
private String name ;
private String sex ;
private int age ;
private Wife wife;
/*
* setter()、getter()是该对象对外开发的接口
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
继承
**概念:**继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或者新的功能,也可以使用父类的功能,但不能选择性的集成父类,通过使用继承能够非常方便的复用以前的代码
子类继承父类 含有父类特性
- 父类私有成员不能继承,但是可以访问
- 构造器不能继承
- 继承局限
- 直接: 单继承
- 间接: 可以继承多个
- Object是所有类的父类
- 子类拥有父类非私有(private)的属性和方法
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
- 子类可以用自己的方法实现父类的方法
注意: 虽然子类无法继承父类构造器,但是子类一定会隐式调用父类的无参构造器
super
代表父类对象的引用
作用
- 调用父类成员变量
- 调用父类成员方法
- 调用父类构造器
💁♂ 注意: super调用父类构造器 只能放第一行
this调用构造器和super调用构造器不能同时使用
重写
子父类中,子类的方法和父类方法一致(父类的方法不满足)
重写规则
- 方法名一致
- 参数一致
- 一大两小
- 一大: 修饰符>父类
- 两小:
- 异常<父类抛出异常小
- 返回类型<=父类的返回类型
哪些方法不能重写
- static修饰的方法(子类写就是为子类自己独有的方法,不是重写父类的方法)
- private修饰的方法(子类写就是为子类自己独有的方法,不是重写父类的方法)
- final修饰的方法
多态
概念:多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须由程序运行期间才能决定
在Java中有两种形式可以实现多态,继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)
定义:
-
子父类
-
父类引用(编译期)指向 子类的实现(运行期)
- 父类无法调用子类独有的方法
基本类型转型
- 自动转型(向上转型) 低精度->高精度
- 手动转型(向下转型) 高精度->低精度
引用类型转型
- 向上转型 子类 转给 父类 多态体现
- 向下转型 父类 转给 子类 强制类型转换
- instanceof (没有关系的类 类型转换异常)
多态分为:编译时多态和运行时多态
编译时多态
方法重载都是编译时多态,根据实际参数的数据类型,个数和次序,Java在编译时能够确定执行重载方法中的哪一个,方法重写表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态
例:声明 p,m引用本类实例,调用toString()方法是编译时多态
public class Test {
public static void main(String[] args) {
Person p = new Person(); //对象引用本类实例
Man m = new Man(); //编译时多态,执行Person类的toString()
System.out.println(p.toString());
System.out.println(m.toString()); //编译时多态,执行Man类的toString()
}
}
class Person{
public String toString() {
String name = "Person";
return name;
}
}
class Man extends Person{
public String toString(){
String name = "Man";
return name;
}
}
运行时多态
Java支持运行时多态,意为p.toString()实际执行p所引用实例的toString(),究竟执行Person类还是Man类的方法,运行时再确定。如果Man类声明了toString()方法,则执行之;否则执行Person类的toString()方法。
程序运行时,Java从实例所属的类开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。寻找p.toString()匹配执行方法的过程如下图所示。
因此,父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法,如toString(),不能执行子类增加的成员方法
将上述例子中toString方法改为getName,因为在Object类中有toString类,无法测试Person与Man中所匹配的执行方法。
public class Test { //例子2
public static void main(String[] args) {
Person p = new Man();
System.out.println(((Man) p).getName()); //返回结果为Man
}
}
class Person{}
class Man extends Person{
public String getName(){
String name = "Man";
return name;
}
}
此例中Person类型要引用Man类的实例,因Person中未定义setName()方法,故需要把Person类显式地转换为Man类,然后调用Man中的getName方法。
将例子1中Person和Man的方法名改为静态的getName()方法,会返回什么结果呢?
public class Test { //例子3
public static void main(String[] args) {
Person p = new Man();
System.out.println(p.type); //返回结果为P
System.out.println(p.getName()); //返回结果为Person
}
}
class Person{
String type = "P";
public static String getName() {
String name = "Person";
return name;
}
}
class Man extends Person{
String type = "M";
public static String getName(){
String name = "Man";
return name;
}
}
例子中子类Man隐藏父类Person的属性,而 Person p = new Man() 表示“先声明一个Person类的对象p,然后用Man类对 p进行实例化”,即引用类型为Person类,实际代表的是Man类。因此,访问的是Person的属性及静态方法,详细解释如下。
所谓静态,就是在运行时,虚拟机已经认定此方法属于哪个类。“重写”只能适用于实例方法,不能用于静态方法。对于静态方法,只能隐藏,重载,继承
子类对于父类静态方法的隐藏(hide),子类的静态方法完全体现不了多态,就像子类属性隐藏父类属性一样,在利用引用访问对象的属性或静态方法时,是引用类型决定了实际上访问的是哪个属性,而非当前引用实际代表的是哪个类。因此,子类静态方法不能覆盖父类的静态方法。
父类中属性只能被隐藏,而不能被覆盖;而对于方法来说,方法隐藏只有一种形式,就是父类和子类存在相同的静态方法
接口
定义:
内容
接口:interface
- 成员变量 public abstract final
- 成员方法 public abstract
- 无构造器
注意
类和类的关系:单继承
类和接口的关系:多实现
接口和接口的关系:多继承
使用:
package com.briup.testinterface;
// Student 具体的行为
public class Student implements Action,Mark{
@Override
public void run() {
System.out.println("学生跑");
}
@Override
public void star() {
System.out.println("学生画画");
}
public static void main(String[] args) {
// 注意: 不是接口 右边是运行类
// 编译期间
Action a = new Student();
a.run(); // 学生跑
a = new Act(); // 运行时类 Act
//a.star(); // 以前是一个类
// 左边是编译 右边是运行 Student
if(a instanceof Student){
Mark m = (Mark) a;
m.star();
}
// 以前: zzb -> pig 没有任何关系
// 问题: 从Action接口 -> Mark接口 类型转换错误
// 运行的类是Student
}
}
// 动作
interface Action{
void run(); // 没有办法具体 含义
}
// 绘画
interface Mark{
void star();
}
class Act implements Action{
@Override
public void run() {
}
}
内部类
作用
- 加深类和类之间的关系
- 内部类获取外部类任意资源(私有),前提是编译器通过的
分类
- 成员内部类
- 静态内部类 static
- 只有静态内部类才可以拥有静态成员
- 不可以访问外部类的非静态成员
- 局部内部类(基本上用不到)
- 匿名内部类
- 所在方法的局部变量是final修饰的
总结
- 内部类可以访问外部类的所有资源
- 内部类比外部类多3种访问修饰符
- protected private static
- 只有静态内部类才可以拥有静态成员
局部变量能否有static修饰?
不能,static会将实例的成员变成类的成员(原本是方法内的成员,不能提高)
包装类
每个基本类型都有对应的包装类
基本类型 | 包装类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
- 包装类型可以调用方法,变量
- 自动装箱:基本类型->引用类型
- 方法
- 十进制->其他进制
- Integer.toString(5,2)
- 其他进制->转十进制
- Integet.parseInt(“0101”,2)
- 十进制->其他进制
注意:
自动装箱的前提是类型匹配
静态内部类 IntegetCache
缓存-128-127的地址,超出这个数值的地址会重新创建对象来存放
💁♂
- 如果是new出来的两个包装类,不走缓存
- 包装类型和对应的基本类型比较,比较的是数值
Object
toString
- 本质上是物理地址,经过hash算法得到的hashcode
- toString会重写该方法
equals
默认使用==号来重写方法
getClass()
比较运行时的对象
hashcode
- 对象(equals)相等,hashcode一定相等
- hashcode相等,对象不一定相等(equals)
- 先比较hashcode是否相等,再比较equals是否相等(效率比较高)
规定:重写equals的同时需要重写hashcode方法 可以使用快捷键alt+insert
String
package com.briup.teststring;
public class TestString {
public static void main(String[] args) {
// String c = "a";
// String d = "a";
// System.out.println(c == d); // true
// new String() 创建了几个对象
// 第一: 堆中的字符串常量池
// 第二: 堆中的地址
// String a = new String("a");
// String b = new String("a");
// System.out.println(a == b); // false
// System.out.println(a.equals(b)); // 只比较字符
// String d = "ab"; // 堆中种字符串常量池
// String e = "a" + "b"; // e: 字符串常量池
System.out.println( d == e);
// String c = a + b; // 堆中地址
// 拼接的本质是:
// new StringBuilder().append(a).append(b)
// System.out.println(d == c);
// String a ="a"; // 变量 可能动态赋值
// final String a = "a";
// final String b = "b"; // 常量/宏变量
// String c = a + b; // "a" + "b"
// String ab = "ab";
// System.out.println(ab == c); // final // true
// String a = new String() 字符串常量和堆中
//a指向堆种地址
String a = new String("a"); // 手动new出来
a.intern(); // a指向堆中地址(常量池已经存在a)
// String aNew = a.intern(); // 返回产量池的引用
String b = "a"; // b指向字符串常量
System.out.println(a == b);
// String a = "a";
// String b = "b";
// String c = a + b;
// String d = "ab"; // false ab已经在常量池了
// // 没写返回 引用变量
// c.intern(); // 已经在常量池 不会放进去 (还是指向堆中地址,但是会返回常量池的地址)
// System.out.println(c == d); // true
// intern
// 如果常量池已经存在,不会放入常量池中,但会返回常量池中地址
// 如果不存在,放入常量池,重新指向常量池地址,也会返回常量池中地址
}
public void hello() {
StringBuilder sb = new StringBuilder();
// String s = 凭借来 "ab";
String s = sb.append("a").append("b").toString(); // new String("ab");
System.out.println(s);
}
}
intern
- 如果堆中的串池中已经存在,就不会存入常量池中,但是会返回常量池(StringTable)中的地址
- 如果不存在,将会方法串池中,重新指向常量池中的地址,也会返回串池(StringTable)中的地址
字符串不可变
- 自身类没有提供操作value[]的公有方法
- 此类由final修饰,没有子类继承,也无法通过子类去操作此value[]
private final char value[];
集合
Collection
单列集合
List
- 有序(不是排序),指的是存取顺序有序
- 内容可以重复
- 带索引下标
ArrayList
package com.briup.collection.list;
import java.util.ArrayList;
import java.util.List;
/**
* @Auther: vanse(lc))
* @Date: 2022/8/2-08-02-10:10
* @Description:List接口
*/
public class TestList {
public static void main(String[] args) {
// List继承Collection接口
// 1.顺序
// 2.可重复
// 3.带索引
// 多态 接口调用方法,编译器跟实现类
List list = new ArrayList();
list.add(1);
list.add(7);
list.add(3);
list.add(3);
list.add("hello");
list.add("hello");
print(list);
// 把指定索引以及以后对应的元素往后移动
list.add(2, 9);
print(list);
list.set(2,10); // 设置值
print(list);
list.remove(2);
print(list);
System.out.println(list.indexOf(3));
}
private static void print(List list) {
for (int i = 0; i < list.size(); i++) {
// 为什么返回Object 接收的是Object
Object o = list.get(i);
// 打印运行的值
System.out.print(o + " ");
}
System.out.println();
}
}
LinkedList
Vector
public class TestSave {
public static void main(String[] args) {
// List list = new Vector(); // 线程安全
List list = new ArrayList();
// 5个线程
for (int i = 0; i < 5; i++) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
list.add(j,"hello" + j);
}
}).start();
System.out.println(list);
}
}
}
Set
- 无序(不是排序,指的是存取顺序无序)
- 单个字符串和数字,是排序状态(特殊)
- 内容不可以重复
- 没有索引下标
HashSet
本质上借助HashMap实现
package com.briup.collection.set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
/**
* @Auther: vanse(lc))
* @Date: 2022/8/2-08-02-11:22
* @Description:HashSet
*/
public class TestHashSet {
public static void main(String[] args) {
// 左边是接口 右边是实现类
// 1.存的顺序和取的顺序不一致
// hashset没有排序功能
// 2.没有下标
// 3.不能重复,会覆盖
Set set = new HashSet(); // 看似是数字排序状态
set.add(new String("ab"));
set.add(new String("ba"));
set.add(new String("bad"));
set.add(new String("a"));
set.add(new String("d")); // 如何判断重复
set.add("d"); // hashcode 97
set.add(new Student("tom"));
set.add(new Student("tom"));
// 遍历
//print(set);
print2(set);
}
private static void print2(Set set) {
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next() + " ");
}
}
private static void print(Set set) {
for (Object o:set) {
System.out.print(o + " ");
}
System.out.println();
}
}
class Student{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return name.equals(student.name);
}
// 默认各有各的地址 物理地址算出来hashcode
@Override
public int hashCode() {
// 统一计算 name一样
return Objects.hash(name);
}
}
TreeSet
- 有序的(排序状态)
- Comparable 自然排序
- comparaTo(Object o)
- 需要被排序的类去实现(实现之后就可以调用来排序)
- Comparator 比较器排序
- conparaTo(Object o1,Object 02)
- 实现类放在TreeSet的构造器
Map
特点
- Map是一个双列集合,一个元素包含两个值(key,value)
- Map集合中的元素,key和value的数据类型可以相同,也可以不同
- Map中的元素,key不允许重复,value可以重复
- Map里的key和value是一一对应的
双列集合
HashMap
- 线程不安全
- key不能重复
- 底层是哈希表,查询速度很快(JDK1.8之前是数组+单向链表,1.8之后是数组+单向链表/红黑树,链表长度超过8时,换成红黑树)
- HashMap是无序的集合,存储元素和取出元素的顺序有可能不一致
- 集合是不同步的,也就是说是多线程的,速度快
- HashMap存储自定义类型的键值,Map集合保证key是唯一的,作为key的元素,必须重写hashCode方法和equals方法,以保证key的唯一
HashTable
-
底层是哈希表,是同步的,是一个单线程的集合,线程安全,但是速度慢
-
线程安全的
-
key和value是不能为空的(不能存储null键,null值)
TreeMap
-
实现SortMap接口,能够把保存的记录根据键来排序,默认升序
-
key不能为空
-
根据key做排序,和TreeSet类似
-
自然排序(key)(默认)
-
比较器排序(key)
LinkedHashMap
-
底层是哈希表+链表(记录元素顺序)
-
存取顺序一致(有序集合)
案例
package com.briup;
import java.util.*;
/**
* @Auther:lanjianbo
* @Date: 2022/8/3-08-03-9:51
* @Description:完成斗地主游戏的生成牌、洗牌、发牌、查看三名玩家的牌及三张底牌
*/
// alt+shift+m
public class CardGame {
public static void main(String[] args) {
// 1.生成牌
List cards = new ArrayList();
prepareCard(cards);
// 2.洗牌 shuffle()
Collections.shuffle(cards);
// 3.发牌 51 平局分给3人 + 3张底牌
List play1 = new ArrayList();
List play2 = new ArrayList();
List play3 = new ArrayList();
List three = new ArrayList();
givingCard(cards, play1, play2, play3, three);
// 4.看玩家的牌和底牌
// 排序 按照牌的大小 3 4 5 6 J Q K A 2 小王 大王
// 自己设计牌面的大小 3-1 4-2 5-3 大王 15
sordCard(play1, play2, play3, three);
System.out.println(play1);
System.out.println(play2);
System.out.println(play3);
System.out.println(three);
}
private static void sordCard(List play1, List play2, List play3, List three) {
Collections.sort(play1);
Collections.sort(play2);
Collections.sort(play3);
Collections.sort(three);
}
private static void givingCard(List cards, List play1, List play2, List play3, List three) {
for (int i = 0; i < cards.size(); i++) {
Card card = (Card) cards.get(i);
if(i < cards.size() - 3){
// 玩家
if( i % 3 == 0){ // 0 1 2
// 1
play1.add(card);
}else if(i % 3 == 1){
// 2
play2.add(card);
}else if(i % 3 == 2){
// 3
play3.add(card);
}
}else{
// 底牌
three.add(card);
}
}
}
private static void prepareCard(List cards) {
// 1.生成牌 54 52+大小王 4种花色+13个牌面组成
// 牌的设计 花色 牌面
// 1.1 声明4种花色
List colors = new ArrayList(Arrays.asList("♥","♦","♠","♣"));
// 1.2 声明13种牌面 大小王另算
List nums = new ArrayList(Arrays.asList("3","4","5","6","7","8","9","10","J","Q","K","A","2"));
// 1.3 声明权重 借助索引 [给每个牌面 赋权重]
Map weights = new HashMap<>();
// 外循环: 牌面
for (int i = 0; i < nums.size(); i++) {
String num = (String) nums.get(i);
weights.put(num,i+1); // 3-1 2-13
// 内循环: 花色
for (int j = 0; j < colors.size(); j++) {
String color = (String) colors.get(j);
int weight = (int) weights.get(num);
// 组牌 牌面+花色+权重
Card card = new Card(color,num,weight);
cards.add(card);
}
}
// 1.4 大小王 额外处理
cards.add(new Card("","小王",weights.size()+1));
cards.add(new Card("","大王",weights.size()+2));
// System.out.println(cards);
}
}
// 设计牌 花色 牌面 权重(真实的牌大小)
class Card implements Comparable{
private String color; // 花色
private String num; // 牌面
private int weight; // 权重 真实大小
@Override
public String toString() {
// 不需要权重
return color + num;
}
public Card() {
}
public Card(String color, String num, int weight) {
this.color = color;
this.num = num;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public int compareTo(Object o) {
Card card = (Card) o;
return card.weight-this.weight;
}
}
泛型
- 特点:类型参数化(类型当做参数做传递)
- 实际使用含有泛型的时候Point,真正传的类型会确定泛型的类型
- 如果不传,默认传的是Object类型
- 必须传的是引用类型
种类
- 泛型类ArrayList<T>
- 泛型接口Collection<E>
- 泛型方法publicT hello(T t)
extends符合上述规则,super不符合
注意
含有泛型的类没有多态
List<Object> List<Integer>
通配符List<?>
- 所有类型的泛型父类
- 只能查看数据,不能修改数据
泛型边界
-
上限? extends Number
-
List<? extends Number> list = new ArrayList<Integer>()
-
-
下限? super Integer
-
List<? super Integet> list = new ArrayList<Integer>()
-
泛型擦除
泛型只在编译期间有效,当编译成字节码文件后,所有泛型信息会被擦除,变为原始类型Object
枚举
Compiled from "Color.java"
// 该类无法被继承 也无法继承第二个类
public final class com.briup.enumerate.Color extends java.lang.Enum<com.briup.enumerate.Color> {
// 静态常量对象的声明
public static final com.briup.enumerate.Color BALCK;
public static final com.briup.enumerate.Color WHITE;
// 该方法返回 该类的实例数组
public static com.briup.enumerate.Color[] values(); // BALCK /WHILTE
// 通过字符串名称 返回枚举对象
public static com.briup.enumerate.Color valueOf(java.lang.String);
public static void main(java.lang.String[]);
static {};
}
- 枚举类默认由final修饰,无法被继承
- 默认继承了Enum类,所以无法再继承其他类
- 构造器私有,无法在外部创建枚举的实例
- 暴露公有静态常量对象(static{}初始化的)每个对象有个名字,编号
- BLACK对象 名字 black
- BLACK对象 编号 编写顺序 第一个位置 编号为0
- Collr[] values 返回该枚举类的所有实例对象
- Color valueOf(String name)根据名字返回枚举对象
- 父类方法
- name()返回名称
- ordinal()返回编号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z4i6qD8s-1678333215278)(…/1659610751584.png)]
获取枚举对象
- 枚举类.对象
- valueOf(String name)
- Enum.valueIf(class.forName(),name)
属性和方法
构造器
- 无参
- 有参
通过Class.forName会调用到构造器(只针对枚举)
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException {
// 触发类的初始化
// 枚举: 初始化过程 创建类对象 调用了无参构造器
Class.forName("com.briup.enumrate.Gender");
}
}
enum Gender {
MALE(), FEMALE();
private String name;
private Gender(){
System.out.println("无参构造器");
}
// 私有构造
private Gender(String name){
System.out.println("有参构造器");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
// return super.toString();
return this.name;
}
}
抽象方法
- 可以写抽象方法,但是需要本类中的每个实例都去实现 默认类有final修饰
- public abstract void show();
接口
- 可以实现接口 但是需要实现接口中的所有抽象方法
- 统一在类中实现该方法
注解
- 注释----给人看的
- 注解----给编译器看的
定义注解
@interface Test{
// 属性 以方法形式
// 如果对方只用了该属性,可以省略
String value() default "";
String name() defalut "";
}
public class Hello{
@Test("vanse")
public void hello(){}
}
- 默认该注解可以放在任意位置
- 默认保留范围 class
元注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface Test{
// 属性 以方法形式
// 如果对方只用了该属性,可以省略
String value() default "";
String name() defalut "";
}
public class Hello{
@Test("vanse")
public void hello(){}
}
反射方法:
-
获取Class对象
- 获取基本类型的Class对象
Class c = int.class
- 获取接口类型的Class对象
Class c1 = Action.class
Class c2 = Class.forName("com.briup.demo.Action")
- 获取数组类型的Class对象
Class c1 = int[].class
int[] arr = new int[4];Class c2 = arr.getClass()
- 获取类类型的Class对象
Class c1 = Student.class
Class c2 = Class.forName("com.briup.demo.Student")
Student stu = new Student();Class c3 = stu.getClass()
- 获取基本类型的Class对象
-
获取类的信息
- 获取类的名字
c.getName
全限定名c.getSimpleName
简单类名
- 获取类所属包的名字
c.getPackage().getName()
- 获取类的修饰符
Modifier.toString(c.getModifiers())
- 获取类的父类型的名字
c.getSuperclass().getName()
- 获取类实现的所有接口
Array.toString(c.getInterfaces())
- 判断A代表的类型是不是B代表类型的父类型
A.isAssignableFrom(B)
- 获取类中声明的属性
Field[] getFields()
Field getField(String name)
- 获取类中声明的属性(包含私有的),但是不能获取父类中继承的属性
Field[] getDeclaredFields()
Field getDeclareField(String name)
- 获取类中声明的方法
Method[] getMethods()
Method getMethod(String name,Class<?>... parameterTypes)
- 获取类中声明的方法(包含私有的),但是不能获取从父类中继承过来的方法
Method[] getDeclareMethods()
Method getDeclareMethod(String name,Class<?>... parameterTypes)
- 获取类中声明的public构造器
public Constructor<?>[] getConstructors()
public Constructor<T> getConstructor(Class<?>... parameterTypes)
- 获取类中声明的所有构造器,包括私有的
public Constructor<?>[] getDeclaredConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
- 获取类的名字
异常
Throable
- error
- Exception
- 编译时异常 Exception 需要声明
- 运行时异常 RuntimeException
声明异常
- 放于方法后面 throws关键字
抛出异常
自动/手动 将异常抛给JVM,本身不处理,同时JVM也会停止运行
- 自动抛出
- JVM自动处理
- 运行时异常
- 编译时异常(需要声明)
- JVM自动处理
- 手动抛出
- 方法中
- throw new 异常对象
- 运行时异常
- 编译时异常(需要声明)
捕获异常
方法内直接处理异常,不向上抛,JVM不会因为异常停止运行
//方式1:
try{
//可能出现异常的代码
}catch(异常1|异常2|异常3 e){
e.printStackTrace()
//方式2:
try{
//可能出现异常的代码
}catch(异常1 e){
e.printStackTrace()
}catch(异常2 e){
e.printStackTrace()
}
最简单的写法
try{
//可能会出现异常的代码
}catch(Exception e){
e.printStackTrace()
}
//后面不能再捕获异常了,子异常需要放在父异常的前面
try catch虽然能够捕获异常,但是可能会导致代码流程错乱
此时finally能够确保代码最终执行
总结 try catch finally
-
try中如果出现了异常或者return语句,finally中没有出现return,最终仍然会执行
-
try中有return,finally中也有return,最终会执行finally中的return
-
try中或者catch中有return或者异常,finally中没有,那么不影响finally中的代码执行,而且最后会返回try中的return
自定义异常
将来需要---- 统一异常处理
public class Test7 {
public static void main(String[] args) {
int i = 0;
if(i == 0){
throw new CustomerException("i不能为0");
}
}
}
// 编译时异常 Exception
// 运行时异常 RuntimeException 项目中常用
class CustomerException extends RuntimeException{
public CustomerException(String message) {
// 调用父类构造器 自定义异常信息
super(message);
}
}
-
默认不处理 JVM抛出异常
-
定制异常
- 编译时异常 javac
- 抛出 JVM停在报错的那一行
- 捕获 JVM会继续执行后续代码,然后正常停止
- 运行时异常 java
- 抛出 JVM停在报错的那一行
- 捕获 JVM会继续执行后续代码,然后正常停止
- 编译时异常 javac
-
try catch 可能会导致流程错乱
- 虽然捕获异常 jvm正常执行
- 从catch代码块之后执行
try{ sout("hello") // 1. int i = 1/0; // 2. 自动 sout("world"); }catch(Exception e){ e.printStackTrace(); // 3.打印异常信息 // 可以继续抛异常 // throw new RuntimeException(); //手动 } sout("jvm正常执行") // 4.
String s = new String(“A”)+new String(“B”)
问:创建了几个对象?
答:6个
new String(“A”):堆中/字符串常量池中(StringTable)
new String(“B”):堆中/字符串常量池中(StringTable)
StringBuilder.toString():1
new String:1