CoeJava
1.自我介绍
程序员小许
电话(同微信):15515807430
QQ:577266939
chp1_Java概述与环境搭建
1.Java的发展史
百度一下
2.Java的优势
简单性
跨平台(操作系统,数据库,服务器)
纯面向对象
开源,丰富的编程资源
3.语言的运行机制
1.编译型语言 C C++ C# uc oc
源文件----[编译器转换]-- 机器码 --- 运行机器码文件
优点: 效率高 编译一次 运行多次
缺点:不能跨平台
2.解释型语言 javascript
源文件---[解释器]---逐行将源文件中的代码解释成机器码 ---- 逐行执行
优点:可以跨平台
缺点:效率比较低
3.先编译,再解释执行 Java
源文件--编译器编译生成平台中立字节码文件---解释器逐行解释字节码文件(.class),逐行执行
特点:跨平台,效率相对较高
4.名词解释
1.JVM
虚拟机 使用软件在不同的操作系统中模拟出相同的环境
2.JRE
运行环境 包含 JVM+解释器 完整的Java运行环境
3.JDK
开发环境 包含 JRE+类库+开发工具包(编译器+调试工具)
5.安装JDK,配置环境变量
1.获取安装包 (.exe)
下一步---下一步---下一步.....
2.环境变量的配置
1.在系统变量中新建 --- 告知操作系统软件的安装位置
变量名 JAVA_HOME
变量值 C:\Program Files\Java\jdk1.8.0_92
2.在系统变量中新建 --- 告知操作系统运行软件所需指令集的位置
变量名 PATH
变量值 C:\Program Files\Java\jdk1.8.0_92\bin
3.在系统变量中新建 --- 告知字节码文件生成的位置
变量名 CLASSPATH
变量值 .
3.检测环境
javac
java
java -version
6.常见的黑窗口操作指令
盘符切换 d: c: e:
查看当前目录结构 dir
进入某一个文件夹 cd 文件夹的名字
回到上一级目录 cd..
清屏 cls
7.第一个Java程序
class 类名{
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
编译: javac 源文件名.java
执行: java 类名
注意:
1.java源文件,编译之后,会生成一到多个字节码文件,且字节码文件名和类名一致
2.java 类名 | java 字节码文件名
3.先写类 ---- 主函数 ---- 输出语句 注意层级缩进
4.一个源文件中可以书写多个类
5.每一个类内部都有一个主函数 public static void main(String[] args){}
6.公开类的类名必须与源文件名一致 javac 公开类类名.java
8.编码规范
1.类名首字母大写,如果类名由多个单词拼接而成,每个单词的首字母都大写
2.函数名全小写,望文生义 如果函数名由多个单词拼接而成,第一次单词首字母小写,其他单词首字母大写[驼峰命名]
3.java的命名规范
由字母,数字,下划线,$ 且数字不能开头
不能使用关键字,保留字
4.常量的命名 字母全大写,若为多个单词拼接,单词之间以 下划线连接 _
5.编码要求层级缩进
9.package
作用:使用文件夹来管理字节码文件
package 包名;
1.必须书写在源文件第一行
2.编译运行指令
编译 javac -d . 源文件名.java
运行 java 包名.类名
3.生成多级文件夹 包名之间使用 . 连接
4.尽量望文生义 采用域名倒置 www.baidu.com --- com.baidu
5.包名不能以 . 收尾
10.注释
// 单行注释
/*
多行注释
*/
/**
文档注释
*/
注释不参与代码的编译。
package day1.day2.day3;
/*
这是一个公开类
用来打印语句
*/
public class TestHello{
public static void main(String[] args){
// 输出语句
System.out.println("充钱使你快乐");
}
}
class TestObject{
}
chp2_数据类型与运算符
1.变量
1.概念
是内存中的一块存储空间,主要作用是存储数据,是存储数据的基本单元.
2.使用
变量的组成: 数据类型 变量名 = 值(数据);
语法:
1.声明并赋值
数据类型 变量名 = 数值 例如 int a = 10;
2.先声明,再赋值
数据类型 变量名;
变量名 = 数值;
例如: int a;
a = 10;
3.同时声明多个相同类型的变量并赋值
数据类型 变量1,变量2,变量3=值3;
例如: int a,b,c=10;
等同于
int a;
int b;
int c = 10;
3.数据类型
1.基本数据类型
4类8种
整 型 byte short int long
浮点型 float double
字符型 char
布尔类型 boolean
2.引用类型(对象类型)
String+数组+任意对象类型
4.基本数据类型
数据类型 | 字节长度 | 取值范围 | 进制 | 举例 |
---|---|---|---|---|
byte | 1B | -128 – 127 | -27—27-1 | byte a = 10 |
short | 2B | -32768 – 32767 | -2^15 – 2^15-1 | short a = 130 |
int | 4B | -2147483648 – 2147483647 | -2^31 — 2^31-1 | int a = 32770 |
long | 8B | -9223372036854775808 | ||
–9223372036854775807 | -2^63 — 2^63-1 | long a = 20L |
注意: Java中默认的整数类型为 int 最长用的类型 也是 int
补充:
计算机就是一系列的电路开关,每个开关都有两种状态 开|关 开就代表 1 关代表0
每个1或者0 都存为一个 位(bit 比特) 最小的存储单元
一个字节(B)由八位(b)组成 字节是最小的存储单位
通常使用 b 代表 位 B 代表 字节 1B=8b
数据类型 | 字节长度 | 负数取值范围 | 正数的取值范围 | 举例 |
---|---|---|---|---|
float | 4B | -3.4E+38 ~ -1.4E-45 | 1.4E-45 ~ 3.4E+38 | float a = 1.2F float b = 1F |
double | 8B | -1.7E+308 ~ -4.9E-324 | 4.9E-324 ~ 1.7E+308 | double a = 1.2 double b = 15 |
注意: Java中默认的浮点型 double double 也是最常用
浮点型是近似存储。
数据类型 | 字节长度 | 取值范围 | 举例 |
---|---|---|---|
char | 2B | 0–65535 | char a = ‘A’ char b = 65 char c = ‘中’ |
char类型变量的赋值方式
char a = 'A';
char b = 65;
char c = '.';
char d = '''; // error!
转义字符 \
在java中某些具有特殊含义的符号不能直接打印,需要使用到转移字符
char a = '\'';
char b = '\\';
char d = '\"';// 双引号建议进行转义
char e = '\t'; // 水平跳格 tab
char f = '\n';
数据类型 | 取值 | 应用场景 |
---|---|---|
boolean | true/false | 条件判断 |
注意: boolean 类型不参与数据运算
5.引用类型
String 代表 字符串 由一组字符组成的串 必须使用 "" 括起来
String str = "hello";
1.任意类型的数据与字符串进行 + 操作 实际为 拼接
//案例
package day2;
public class TestObject{
public static void main(String[] args){
int a = 5;
int b = 2;
int c = 7;
int d = a+b; // 等同于 int d = 5+2;
System.out.println(d); //打印 7
System.out.println("a减去b等于"+(a-b));// 打印 3
System.out.println("a/b="+(a/b));//打印 2 "a/b="+(a/b) 打印内容 a/b=2
System.out.println(a%b);//打印 1
String s = "3";
System.out.println(s+a);//字符串的相加操作 实为 拼接 打印 字符串35
//任意类型的数据与字符串进行相加 其结果都是字符串
}
}
6.数据类型转换
直接给一个变量进行赋值,还可以通过一个变量给另一个变量赋值
int a = 10;
int b = 10;
int c = a;
short d = 15;
int e = d;
short f = a; // error!
自动类型转换
目标类型 = 源类型;
条件
|-两种类型相互兼容。
|-目标类型大于源类型。
例如: int a = 10; long b = a;
转换规则:
|-byte ---> short ---> int ---> long ---> float ---> double
|-char ---> int ---> long ---> float ----> double
//案例
package day2;
public class TestObject{
public static void main(String[] args){
byte a = 10;
short b = a;
int c = b;
long d = c;
float e = d;
double f = e;
char i = 'A';
//error! short j = i; // 源类型: 0 ~ 65535 目标类型 -32768 ~ 32767
/*
char g = a; error! a可能为负数
*/
}
}
强制类型转换
1.两种类型相互兼容。
目标类型小于源类型。
2. 目标类型 = (目标类型)源类型
当目标类型小于源类型,不可直接进行转换,需要强转。
//案例
package day2;
public class TestObject{
public static void main(String[] args){
//1.大类型转换为小类型 若数值不超出范围 数据完整
short a = 10; // -32768 ~ 32767
byte b = (byte)a; // -128 ~ 127 1.有可能成功 2.强转符号 编译通过
System.out.println(b); // 打印 10
//2.大类型转换为小类型 若数值超出范围 直接舍弃高位字节,数据截断
short c = 10000;
byte d = (byte)c; // 打印16 --- 数据被截断
//3.小数类型转换为整数类型,直接舍弃小数部分 没有四舍五入
double e = 1.8;
int f = (int)e;
System.out.println(f);//打印 1
}
}
7.运算符与表达式
表达式:[等同于一个数值]
使用运算符连接起来的两个变量或者数值,并最终获得一个结果.
自动类型提升的规则
1.若两个操作数中有一个是double,则结果提升为 double
2.若两个操作数中没有double,但是有一个float,则计算结果提升为 float
3.若两个操作数没有float,但是有一个为long 则计算结果提升为 long
4.若两个操作数中没有long,但是有一个int 则结果提升为 int
5.若两个操作数均为 byte或者 short 则结果提升为 int
6.若两个操作数中有一个为String 且 为 + 实为拼接 结果为 String
1.算数运算符
+ - * / %
2.自增自减
++ --
3.比较运算符 ---- 其表达式的结果为 boolean 类型的值
> >= < <= == !=
4.复合运算符
+= -= *= /= %=
注意: 不会造成类型的自动提升。
5.逻辑运算符
& &&
| ||
!
注意:
& &&的区别
|-&&
是短路与 逻辑表达式1&&逻辑表达式2 若逻辑表达式1的结果为false,则整体为false,逻辑表达式不执行
|-&
逻辑表达式1&&逻辑表达式2 若逻辑表达式1的结果为false,则整体为false,逻辑表达式2依然会执行
| ||的区别
|- ||
逻辑表达式1&&逻辑表达式2 若逻辑表达式1为true,则整体为true,逻辑表达式2不执行
|- |
逻辑表达式1&&逻辑表达式2 若逻辑表达式1为true,则整体为true,逻辑表达式2依然会执行
6.三元运算符
布尔表达式?表达式1:表达式2
//案例
package day2;
public class TestObject{
public static void main(String[] args){
int a = 10;
int b = 20;
int c = a>b?(a++):(b++); // false --- 整体获取 b++ int c = b++; int c = 20; b---21
System.out.println(c); //20
}
}
//自动类型提升的规则案例代码
byte a = 10;
byte b = 10;
int add = a+b;
int c = 10;
double b = 1.2;
double f = c+b;
int e = 1;
float f = 1.2F;
float h = e+f; // double h = e + f 1.自动类型提升为 float 2. float自动类型转换为double
String s = "3"+a; // 310 ---- String
//自增自减运算符案例
package day2;
public class TestObject{
public static void main(String[] args){
int a = 10;
int b = 10;
a++;
++b;
// ++ 在前或者在后 相同点: 都会使操作数自增1
System.out.println(a);// 11
System.out.println(b);// 11
/*
++ 在前或者在后 的区别
-- 在前或者在后 的区别
*/
int c = 10;
System.out.println(c++);// 10 先使用,再自增 先使用 10 c自增为 11
System.out.println(c);// 11
int d = 10;
System.out.println(++d);// 11 先自增,再使用
System.out.println(d);//11
int e = 10;
System.out.println(e--); // 10 先使用,再自减
System.out.println(e); // 9
int f = 10;
System.out.println(--f);//9 先自减,再使用
System.out.println(f); // 9
}
}
8.扫描器(键盘输入类)
Scanner : 扫描器 主要作用 接收键盘上录入的数据内容
1.在源文件中引入Scanner --- 导包
2.若使用到的类位于 java.lang包中 则可以直接使用无需导包 编译器会在源文件中默认添加
import java.lang.*;
3.关键字 import
两种用法:
import 包名.*; ---- 引入包中所有的类型
import 包名.类名; --- 引入包中指定的类
Scanner类中常见的方法
接收字符串: String str = scan.next();
接收字符: char c = scan.next().charAt(0);
接收整型值: int num = scan.nextInt();
接收浮点型数据: double d = scan.nextDouble();
接收布尔类型 : boolean flag = scan.nextBoolean();
//Scanner案例代码
package day2;
//1.导包
import java.util.Scanner;
//编译器默认提供 import java.lang.*; System | String 都位于 java.lang 包
public class TestScanner{
public static void main(String[] args){
//2.构建Scanner类的对象
Scanner scan = new Scanner(System.in);
System.out.print("请输入一个整数:");
//3.接收一个整型值,再控制台打印出来
int num = scan.nextInt();
System.out.println(++num);
}
}
//练习1 手动录入两个整型值 求和 控制台打印输出
package day2;
//1.导包
import java.util.Scanner;
public class TestAdd{
public static void main(String[] args){
//2.构建Scanner类的对象 3 4 打印 3+4=7
Scanner s = new Scanner(System.in);
//3.根据需求调用相关的方法
System.out.println("请输入第一个整数:");
int a = s.nextInt();
System.out.println("请输入第二个整数:");
int b = s.nextInt();
//4.数据操作
int add = a+b;
System.out.println(a+"+"+b+"="+add);
}
}
chp3_选择分支
1.选择分支结构
1.if(布尔表达式){
//代码1
}
//代码2
执行流程:首先执行布尔表达式的判断,若布尔表达式的结果为true,则执行代码1,执行代码2
若布尔表达式的结果为false,则执行代码2
2.if-else
if(布尔表达式){
//代码1
}else{
//代码2
}
//后续代码
执行流程:首先执行布尔表达式的判断,若布尔表达式的结果为true,则执行代码1,执行后续代码
若布尔表达式的结果为false,则执行代码2,执行后续代码。
//案例代码
package day3;
import java.util.Scanner;
public class TestIf{
public static void main(String[] args){
int a = 10;
int b = 20;
Scanner s = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num = s.nextInt();
if(num>a){
a++;
}else{ // 对if后的表达式取反 等价于 num <= a
b++;
}
System.out.println(a);
System.out.println(b);
}
}
3.多重if结构
if(布尔表达式1){
//代码1
}else if(布尔表达式2){
//代码2
}else if(布尔表达式3){
//代码3
}else{
//代码4
}
//后续代码
执行流程: 首先执行布尔表达式1的判断,若结果为true,则执行代码1,执行后续代码
若结果为false,则执行布尔表达式2的判断,
若布尔表达式2的结果为true,则执行代码2,执行后续代码
若布尔表达式2的结果为false,则执行布尔表达式3的判断,
若布尔表达式3的结果为true,则执行代码3,执行后续代码
若布尔表达式3的结果为false,则执行代码4,执行后续代码
//案例
package day3;
//1.导包
import java.util.Scanner;
public class TestIf{
public static void main(String[] args){
/*
10W以下 吉利
10W - 15W 思域
15W - 30W 帕萨特 雅阁 迈腾 亚洲龙
30W - 40W 奥迪
40W 以上 房
*/
Scanner s = new Scanner(System.in);
System.out.println("请输入金额:");
int money = s.nextInt();
if(money<=10){
System.out.println("帝豪购买成功");
}else if(money<=15){ // 默认 money>10
System.out.println("思域购买成功");
}else if(money<=30){ //默认 money>15
System.out.println("雅阁购买成功");
}else if(money<=40){ // 默认 money>30
System.out.println("奥迪购买成功");
}else{ // 默认 money>40
System.out.println("买房");
}
}
}
注意:
相互排斥的关系,当有一个条件满足时,其他的就不再执行
if 与 临近的 else if 之间 取反
4.if结构的嵌套
if(布尔表达式1){
if(布尔表达式2){
//代码1
}else if(布尔表达式3){
//代码2
}else{
//代码3
}
}else{
//代码4
}
执行流程: 首先执行布尔表达式1的判断,若结果为true,则执行布尔表达式2的判断
若布尔表达式2的结果为true,则执行代码1
若布尔表达式2的结果为false,则执行布尔表达式3的判断
若布尔表达式3的结果为true,则执行代码2
若布尔表达式3的结果为false,则执行代码3
若布尔表达式1的结果为false,则执行代码4
2.分支结构
switch(表达式){ // byte short int char String(JDK1.7及以上)
case 值1:代码块1;break;
case 值2:代码块2;break;
case 值3:代码块3;break;
default:代码块4;
}
//案例1
package day3;
import java.util.Scanner;
public class TestSwitch{
public static void main(String[] args){
System.out.println("*****欢迎光临硅谷五楼大餐园*****");
System.out.println("1号---黄焖鸡米饭 2号---煎饼 3号---老碗面 4号---螺蛳粉 5号---麻辣香锅");
Scanner s = new Scanner(System.in);
System.out.println("请输入点餐编号:");
int num = s.nextInt();
switch(num){
case 1:System.out.println("黄焖鸡米饭出餐");break; // break 可以跳出switch-case结构
case 2:System.out.println("煎饼出餐");break;
case 3:System.out.println("老碗面出餐");break;
case 4:System.out.println("螺蛳粉");break;
case 5:System.out.println("麻辣香锅");break;
default:System.out.println("凉皮");
}
}
}
//案例2
package day3;
import java.util.Scanner;
public class TestSwitch{
public static void main(String[] args){
System.out.println("*****欢迎光临硅谷五楼大餐园*****");
System.out.println("1号---黄焖鸡米饭 2号---煎饼 3号---老碗面 4号---螺蛳粉 5号---麻辣香锅");
Scanner s = new Scanner(System.in);
System.out.println("请输入点餐编号:");
int num = s.nextInt();
switch(num){
case 1:System.out.println("黄焖鸡米饭出餐");break; // break 可以跳出switch-case结构
case 2:System.out.println("煎饼出餐");break;
default:System.out.println("凉皮");break;
case 3:System.out.println("老碗面出餐");break;
case 4:System.out.println("螺蛳粉");break;
case 5:System.out.println("麻辣香锅");break;
}
}
}
3.局部变量
概念:定义在函数内部的变量,称之为局部变量
使用范围:从定义行开始,到定义它的代码块结束
注意:
1.局部变量必须先赋值,再使用
2.在重合的作用范围内,局部变量的命名不允许重复
//案例
package day3;
public class TestDemo{
public static void main(String[] args){
int a = 20;
int b = 20;
if(a<b){
int c = 30; // 7 -- 8
}
int c = 40; // 9 -- 11
System.out.println(c); // 10 ---- 找40
}
}
chp4_循环
1.概念与结构
概念:满足某个条件,重复执行一段代码
循环三要素: 循环初值 循环条件 循环增量 共同决定循环次数
2.循环结构
1.while循环
while(布尔表达式){
//代码1
}
if(布尔表达式){
//代码1
}
while循环的执行特点:
先判断,再执行 循环次数 0 ~ n次
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAfaaUpY-1630564867821)(F:\javase\day4\截图\while与if的对比.jpg)]
package day4;
public class TestWhile{
public static void main(String[] args){
/*
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld"); 重复执行的代码
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
*/
//循环初值
int i = 1;
while(i<=10){//循环条件
System.out.println("HelloWorld");
i+=2;//循环增量
}
}
}
//练习: 录入一个整数n 求和 1+2+3+4+5+6+.....n
package day4;
import java.util.Scanner;
public class TestEct_1{
public static void main(String[] args){
// 1+2+3+4+5+....+n
//请输入一个整数
Scanner s = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = s.nextInt();
int a = 0; // 记录求和
int i = 1; //循环初值
while(i<=n){ //循环条件
a+=i; //循环操作
i++; // 循环增量
}
System.out.println("0+.....+"+n+"="+a);
/*a+=1;
a+=2
a+=3;
a+=4;
a+=5;
a+=6;
...
a+=n
*/
}
}
2.do-while
do{
//循环体
}while(布尔表达式);
执行流程:
1.先执行一次循环体
2.再进行布尔表达式的判断,若布尔表达式的结果为true,则执行循环体-----再次进行布尔表达式的判断----若布尔表达式的结果为true,则执行循环体..........
3.直至布尔表达式的结果为false,则结束循环
执行次数 1 ~ n次
3.for循环
for(循环变量;循环条件;循环增量){
//循环体
}
执行 0 ~ n次
//案例
package day4;
public class TestFor{
public static void main(String[] args){
for(int i=1;i<=10;i++){
System.out.println("helloWorld");
}
}
}
注意:
1.若循环三要素缺失 --- 会造成死循环
2.若缺失循环增量 --- 可能造成死循环
4.循环结构之间的区别
1.while/for 都是先判断,再执行 do-while 先执行一次,再判断
2.while do-while 无法确定循环次数 适用于循环次数不明确的情况
3.for 循环 适用于循环次数明确的情况
5.流程控制
1.break
直接终止直接包含 break 的循环
//案例
package day4;
public class TestBreak{
public static void main(String[] args){
for(int i=1;i<=10;i++){
if(i==6)break;
System.out.println("HelloWorld "+i);
}
}
}
/*
输出结果:
HelloWorld 1
HelloWorld 2
HelloWorld 3
HelloWorld 4
HelloWorld 5
*/
2.continue
终止本次循环,进入下一次
package day4;
public class TestContinue{
public static void main(String[] args){
for(int i=1;i<=10;i++){
if(i==6)continue;
System.out.println("HelloWorld "+i);
}
}
}
/*
输出结果
输出结果:
HelloWorld 1
HelloWorld 2
HelloWorld 3
HelloWorld 4
HelloWorld 5
HelloWorld 7
HelloWorld 8
HelloWorld 9
HelloWorld 10
*/
6.循环嵌套
1.一个循环嵌套另外一个循环.
//案例
package day4;
public class TestEcp_2{
public static void main(String[] args){
/* ----- 打印五个 -
for(int i=1;i<=5;i++){
System.out.print('-');
}
System.out.println();
*/
/*
-----
-----
-----
-----
-----
*/
for(int i=1;i<=5;i++){
//打印一行分隔符
for(int j=1;j<=5;j++){
System.out.print('-');
}
System.out.println();
}
}
}
2.外层循环执行一次,内层循环执行一轮
package day4;
public class TestEcp_3{
public static void main(String[] args){
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
System.out.println(i+" "+j+" ");
}
}
}
}
3.Lable标签(了解)
break默认跳出的是直接包含它的循环,若需要跳出指定的循环,需要使用Lable标签
package day4;
public class TestEcp_3{
public static void main(String[] args){
outer:for(int i=1;i<=5;i++){
inner:for(int j=1;j<=5;j++){
if(i==4)break outer;
System.out.println(i+" "+j+" ");
}
}
}
}
7.练习
/*
1.求 100 以内所有能被 3 整除但不能被 5 整除的数字的和
2.计算五名学生的平均成绩
3.打印图案
*****
*****
*****
*****
*****
i j
第一行 5个
第二行 5个
第三行 5个
第四行 5个
第五行 5个
*
**
***
****
*****
i j
第一行 1个
第二行 2个
第三行 3个
第四行 4个
第五行 5个
*
***
*****
*******
*********
***********
i 6-i 2*i-1
第一行 5个空格 1个* 换行
第二行 4个空格 3个* 换行
第三行 3个空格 5个* 换行
第四行 2个空格 7个* 换行
第五行 1个空格 9个* 换行
第六行 0个空格 11个* 换行
4.“百钱买百鸡”是我国古代的著名数学题。
题目描述:3 文钱可以买 1 只公鸡,2 文钱可以买一只母鸡,1 文钱可以买 3 只小鸡。
用 100 文钱买100 只鸡,那么各有公鸡、母鸡、小鸡多少只?
*/
import java.util.Scanner;
class Test1{
public static void main(String[] args){
int sum = 0;
for(int i=1;i<=100;i++){
if(i%3==0&&i%5!=0){
sum+=i;
}
}
System.out.println(sum);
}
}
class Test2{
public static void main(String[] args){
Scanner s = new Scanner(System.in);
double sum = 0.0;
for(int i=1;i<=5;i++){
System.out.print("请输入第"+i+"位学生的成绩:");
double a = s.nextDouble(); //
sum+=a;
}
System.out.println("平均成绩为:"+(sum/5));
}
}
class Test3{
public static void main(String[] args){
/*
*****
*****
*****
*****
*****
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
System.out.print('*');
}
System.out.println();
}
*/
for(int i=1;i<=5;i++){
for(int j=1;j<=i;j++){
System.out.print('*');
}
System.out.println();
}
}
}
class Test4{
public static void main(String[] args){
for(int i=1;i<=6;i++){ // i控制行
for(int j=1;j<=(6-i);j++){
System.out.print(' ');
}
for(int j=1;j<=(2*i-1);j++){
System.out.print('*');
}
System.out.println();
}
}
}
class Test5{
public static void main(String[] args){
for(int i=0;i<=33;i++){ // i 代表公鸡
for(int j=1;j<=50;j++){ // j代表母鸡
int k = 100-i-j; // k 小鸡
int money = 3*i+2*j+k/3;
if(money==100&&k%3==0){
System.out.println("公鸡:"+i+"只 母鸡:"+j+" 只 小鸡:"+k+"只");
}
}
}
}
}
chp5_函数
冗余代码 :重复出现的功能性代码,会导致程序的可维护性降低
函数 :函数是功能性代码,在保留原有代码功能的基础上,提高程序的可维护性
1.函数的语法
1.自定义函数的语法:
public static void 函数名(){
// 在主函数中重复出现的功能代码
}
注意: 1.定义位置:类以内,其他函数以外
2.命名要符合标识符规范
2.自定义的函数如果需要被执行,需要在主函数中进行调用。
函数名();
3.形式参数:
相当于在函数内部声明的局部变量,局部变量的使用,必须是先赋值,再使用
public static void 函数名(数据类型 变量1,数据类型 变量2){
}
4.调用函数:
函数名(实际参数); // 在主函数中冗余代码出现的位置
实际参数就是具体的数值,其类型,顺序,个数 取决于形式参数的类型,顺序,个数
5.返回值与返回值类型
public static 返回值类型 函数名(形参列表){
//功能代码
}
返回值类型:
void
代表函数调用,不需要返回值[没有结果返回] ----- 该函数没有返回值
8种基本类型 + 引用类型
代表函数调用,会返回一个结果 --- 关键字使用 return
注意:
1. return 不仅可以返回结果.还代表程序的结束,程序的返回.
return 其后的代码就会变成不可达的代码【程序中禁止出现】
2. 返回值 ---- return 值;
3. return 值; 要求 值 的 类型需要与 返回值类型一致或者兼容
4. 函数分为声明部分和实现部分
5. 对于调用者,只能看到函数的声明部分[函数名 形参列表 返回值类型],无法获取实现部分
package day5;
public class TestFun{
public static void main(String[] args){ // 数据类型 变量名; 形式参数... 等价于 局部变量的声明
//冗余代码 :重复出现的功能性代码,会导致程序的可维护性降低
//函数 :函数是功能性代码,在保留原有代码功能的基础上,提高程序的可维护性
printSign(30,'+'); // int len = 30 char c = '+' 30:实际参数
System.out.println("伽罗:羌笛何须怨杨柳,春风不度玉门关");
printSign(40,'*'); // 实际参数给形式参数赋值时: 要求 类型一致 顺序一致
System.out.println("明世隐:昨夜星辰昨夜风,画楼西畔桂堂东");
printSign(50,'-');
System.out.println("李白:今朝有酒今朝醉");
printSign(60,'/');
System.out.println("云想衣裳花想容");
printSign(70,'$');
System.out.println("昔我往矣,杨柳依依");
}
//自定义一个函数 : 打印符号
public static void printSign(int len,char c){//形式参数
for(int i=1;i<=len;i++){
System.out.print(c);
}
System.out.println();
return;// 代表程序的返回
}
}
//练习
//1. 编写一个函数,计算两个数的和并返回
public static int add(int a,int b){
return a+b; // 1.返回结果 2.程序的返回
}
//2.编写一个函数,判断一个数是否为质数
package day5;
import java.util.Scanner;
public class TestFunction{
public static void main(String[] args){
int num = add(5,6);
System.out.println("请输入一个整数:");
Scanner s = new Scanner(System.in);
int a = s.nextInt();
boolean flag = isPrime(a);
if(flag){
System.out.println(a+"是质数");
}else{
System.out.println(a+"不是质数");
}
}
//手动定义函数: 计算两个数的和并返回
public static int add(int a,int b){
return a+b;
}
//判断一个数 是否 为质数
/*
质数: 除了 1 和 自身以外 没有其他因子
*/
public static boolean isPrime(int num){
boolean flag = true;
for(int i=2;i<num;i++){
if(num%i==0){
flag = false;
}
}
return flag;
}
}
package day5;
public class TestFunction_1{
public static void main(String[] args){
String s = m1(12);
System.out.println(s);
System.out.println(m1(9)); // m1(9)---- "9是奇数"
}
//定义一个函数: 判断一个数是奇数还是偶数
public static String m1(int num){ // 无论如何 都会返回一个 字符串的值
if(num%2==0){
String s = num+"是偶数";
return s;
}else{
return num+"是奇数";
}
}
}
2.函数的嵌套调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRBjZMm2-1630564867825)(F:\javase\函数\截图\函数的嵌套调用.jpg)]
chp6_数组
1.概念
是内存中一块 连续的 存储空间.可以存储多个同类型的值. ====== 等价于声明了多个同类型的变量
数组的特点:
1.数组也是一种数据类型 ---- 对象|引用类型
2.数组的长度是固定的,不可变的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6jsTS4q-1630564867826)(F:\javase\数组\截图\数组空间分配.jpg)]
2.数组的使用
1.创建数组
1.先声明,再分配空间
数据类型[] 数组名;
数组名 = new 数据类型[长度];
例如: int[] a;
a = new int[5];
2.声明的同时分配空间
数据类型[] 数组名 = new 数据类型[长度];
例如: int[] a = new int[5];
3.声明并初始化
数据类型[] 数组名 = {值1,值2,值3...};
例如: int[] a = {2,6,7,8,10};
4.声明并初始化
数据类型[] 数组名 = new 数据类型[]{值1,值2,值3...};
例如: int[] a = new int[]{1,5,6,7};
注意:
1.案例1
int[] arr;
arr = {2,6,7,8,10}; // error!
2.数组在Java中也是一种数据类型
int[] a; // 推荐使用
int []a;
int a[];
2.数组的使用
1.数组的长度
|-数组的长度是固定的 通过 数组名.length 可以获取数组长度
|-数组下标:数组中每个存储空间都有一个标识 下标范围 0 ~ 数组长度-1
|-数组元素的访问
|-存储 数组名[下标] = 值
|-获取 数组名[下标]
2.数组元素的遍历
for(int i=0;i<数组名.length;i++){
System.out.println(数字名[i]);
}
//案例
package array;
public class TestArray1{
public static void main(String[] args){
int[] a = new int[7];
a[0] = 60;
a[1] = 70;
a[2] = 90;
a[3] = 50;
a[4] = 100;
a[5] = 120;
a[6] = 130;
// 获取所有数组元素 ===> 数组元素的遍历
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6RGKYaz-1630564867828)(F:\javase\数组\截图\数组元素存储.jpg)]
3.数组使用的细节
1.数组元素的默认值
数据类型 | 默认值 |
---|---|
整型 | 0 |
浮点型 | 0.0 |
布尔类型 | false |
字符类型 | ‘\u0000’-----空格[空字符] |
引用类型 | null |
2.深入数组底层
1.数组属于引用类型 --- 引用类型的变量中,保存的就是地址
2.数组引用或者说数组名,保存的是数组元素的首地址
3.其他数组元素的寻址公式 首地址+数据类型的字节数*下标
4.下标从0开始----寻址效率高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EP6sqOop-1630564867829)(F:\javase\数组\截图\数组元素首地址存储.jpg)]
基本类型变量之间的赋值---传递的是具体的数值
引用类型变量之间的传值---传递的是地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qInh7dD6-1630564867830)(F:\javase\数组\截图\传值1.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U0WgJYQE-1630564867831)(F:\javase\数组\截图\传值2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yzq1uti3-1630564867832)(F:\javase\数组\截图\传值3.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8T4RdSI-1630564867832)(F:\javase\数组\截图\传值4.jpg)]
3.数组扩容
//1.手动扩容
package day6;
public class TestArrayExpend{
public static void main(String[] args){
int[] a = new int[5];
a[0] = 20;
a[1] = 50;
a[2] = 80;
a[3] = 60;
a[4] = 40;
System.out.println(a.length);
a = expend(a);
System.out.println(a.length);
}
//数组扩容
public static int[] expend(int[] m){
//1.创建空间更大的新数组
int[] b = new int[m.length*2];
//2.原数组元素的复制
for(int i=0;i<m.length;i++){
b[i] = m[i];
}
return b;
}
}
2.使用System类中的数组扩容方法
System.arrraycopy(旧数组,旧数组复制起始下标,新数组,新数组的起始下标,复制的长度)
public class TestArrayExpend{
public static void main(String[] args){
int[] a = new int[5];
a[0] = 20;
a[1] = 50;
a[2] = 80;
a[3] = 60;
a[4] = 40;
int[] b = new int[a.length*2];
System.arraycopy(a,0,b,0,a.length); // 等价于 22 ~ 24行
a = b;
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
//数组扩容
public static int[] expend(int[] m){
//1.创建空间更大的新数组
int[] b = new int[m.length*2];
//2.原数组元素的复制
for(int i=0;i<m.length;i++){
b[i] = m[i];
}
return b;
}
}
3.Arrays.copyof(旧数组,新数组的长度)
package day6;
import java.util.Arrays;
public class TestArrayExpend{
public static void main(String[] args){
int[] a = new int[5];
a[0] = 20;
a[1] = 50;
a[2] = 80;
a[3] = 60;
a[4] = 40;
a = Arrays.copyOf(a,a.length*2); // 等价于 expend()
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
//数组扩容
public static int[] expend(int[] m){
//1.创建空间更大的新数组
int[] b = new int[m.length*2];
//2.原数组元素的复制
for(int i=0;i<m.length;i++){
b[i] = m[i];
}
return b;
}
}
4.二维数组[了解]
1. 是一维数组的一维数组 二维数组的元素 是 一维数组
2. 创建语法
1.先声明,再分配空间
数据类型[][] 数组名;
数组名 = new 数据类型[高维数组长度][低位数组的长度];
例如:
int[][] a;
a = new int[3][4];
2.声明的同时并分配空间
数据类型[][] 数组民 = new 数据类型[高维数组的长度][低维数组的长度];
例如: int[][] a = new int[3][4];
3.声明并初始化
数据类型[][] 数组名 = {{值1,值2,值3},{值1,值2,值3},{值1,值2,值3}};
注意:
高维长度:代表二维数组中存储的 一维数组的 数量 高维长度不允许缺失
低维长度:代表一维数组存储元素的个数 可以缺失
//案例
package day6;
public class TestArray3{
public static void main(String[] args){
int[][] a = new int[3][]; // a 中可以存储 3 个 一维数组
int[][] b = new int[3][4]; // a中可以存储 3个 长度为4的一维数组
int[] c = {1,2,3,4};
b[0] = c; // b[0] 保存的是 c 一维数组的地址
int[] d = {5,4,7,8};
b[1] = d; // b[1] 保存的是 d 一维数组的地址
int[] e = {2,5,8,7};
b[2] = e; // b[2] 保存的是 e 一维数组的地址
int[] f = {2,4,8};
a[0] = f;
int[] g = {7,8,9,7,87};
a[1] = g;
int[] h = {8,7};
a[2] = h;
for(int i=0;i<a.length;i++){
// a[i] 是一维数组
for(int j=0;j<a[i].length;j++){
System.out.print(a[i][j]+" ");
}
System.out.println();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FF9HrnyL-1630564867833)(F:\javase\数组\截图\二维数组.jpg)]
5.可变长参数
概念:定义形式参数时,参数的个数不确定 -- 可变长参数
调用函数时,传递的实参数量就是可变长参数的长度
语法:
public static 返回值类型 函数名(数据类型... 变量名){
//函数实现
}
注意:
1.每个函数只能定义一个可变长参数,并且必须定义在形参列表末尾
public static void add(double... d,int n) //error
public static void add(int n,double... d) // OK!
public static void add(int... a,double... d) // error!
2.把可变长参数当做数组使用
//可变长参数:
public static void add(int... a){ // a 引用中可以接受 0 ~ n 个 int类型的值
int sum = 0;
for(int i=0;i<a.length;i++){
sum+=a[i];
}
System.out.println(sum);
}
6.排序算法
//冒泡排序:相邻两个元素比较
for(int j=1;j<a.length;j++){
//每一轮的比较过程
for(int i=0;i<a.length-j;i++){
if(a[i]>a[i+1]){
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
}
//选择排序:固定元素,与其他元素进行比较
for(int i=1;i<a.length;i++){
for(int j=i;j<a.length;j++){
if(a[i-1]>a[j]){
int temp = a[i-1];
int a[i-1] = a[j];
a[j] = temp;
}
}
}
//JDK排序
java.util.Arrays.sort(a);
chp7_面向对象
1.面向过程与面向对象
1.编程思想:
解决问题的思路。
面向过程: 从计算机的角度出发,自顶向下,逐步求精 代码的执行流程是核心
面向对象: 从人的角度出发,从现实角度出发,模拟现实生活,解决现实问题 解决人的问题
2.需求:
编码需求来自于生活
3.现实生活中对象与对象之间的关系
汽车 车 is a 继承|归类
汽车 发动机 has a 关联关系 ---- 一个对象可以充当另外一个对象的属性
司机 汽车 use a 依赖关系
4.面向对象解题思路
1.找对象
构建对象的模板 --- 类
创建对象,并保存 ---- 对象的类型需要与保存的空间 类型一致
2.建立对象与对象之间的关联关系
通过属性的访问和方法的调用
3.确定具体过程
//案例: 饲养员喂狗
package object;
public class TestMaster{
public static void main(String[] args){
//1.构建饲养员对象
Master m = new Master();
Dog dog = new Dog();
//2.饲养员调用喂养行为
m.feed(dog);
}
}
/*
饲养员喂狗
1.找对象
饲养员类 ---- 饲养员对象
狗类------狗对象
2.建立对象与对象之间的联系
伴随属性的访问和方法的调用
3.确定具体过程
*/
class Master{
//方法
public void feed(Dog d){ // 形式参数 等同于 局部变量的声明
d.eat();
}
}
class Dog{
public void eat(){
System.out.println("Dog eat....");
}
}
2.认识对象
1.什么是对象
java眼中的对象:一切客观存在的事物都是对象 世界是由对象组成 ------- 万物皆对象
例如: 水杯 女朋友 空气 错误等等都是对象
2.对象的组成
属性:代表对象有什么
方法:代表对象能做什么,有什么行为,能执行什么
程序中的对象:一组数据的聚合体。内存中开辟一块存储空间,用来存储这组数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0CPmKpi-1630564867834)(F:\javase\面向对象\截图\小新.jpg)]
3.类的概念
类
类是对象的模板 是一组相同或者相似的对象共性的抽取,只保留我们所关注的部分[业务场景相关]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SotqmL5L-1630564867834)(F:\javase\面向对象\截图\类的抽取.jpg)]
4.类和对象的定义与使用
1.定义类
class 类名{
//属性
数据类型 属性名;
数据类型 属性名 = 属性值;
//方法
public 返回值类型 方法名(形式参数){
//行为具体实现的过程
}
}
注意:
1.属性又称之为成员变量|全局变量|成员属性
2.属性(成员变量)具有默认值:等同于数组元素的默认值
2.定义与使用对象
|-定义对象
类名 变量名 = new 类名();
|-属性和方法的访问
|-查看当前属性值
System.out.println(变量名.属性名);
|-改动属性值
变量名.属性名 = 新值;
|-调用对象的方法
变量名.方法名(实参);
//案例
package object;
public class TestObject{
public static void main(String[] args){
//构建对象的语法 new 类名()
Student stu = new Student();
//属性的访问
System.out.println(stu.age);
System.out.println(stu.score);
stu.study();
}
}
/*
类
属性(特征):有什么
方法(行为):能做什么
*/
class Student{
/*
定义属性
|- 声明属性
数据类型 属性名;
|- 声明并初始化
数据类型 属性名 = 值;
*/
String name;
int age = 21;
double score; // 值为默认值 0.0 类内部
/*
public 返回值类型 方法名(形式参数){
// 行为的具体表现过程
}
*/
public void study(){
double score = 10.0; // 局部变量
System.out.println("Student study..."+score); // 使用的是 局部变量 打印 Student study...10.0
}
public void sleep(){
System.out.println("Student sleep....");
}
}
5.成员变量与局部变量区别
区别 | 局部变量 | 成员变量(属性) |
---|---|---|
位置 | 定义在函数内部 | 类的内部,函数的外部 |
默认值 | 没有默认值,必须先赋值,再使用 | 有默认值,默认值等同于数组元素的默认值 |
作用范围 | 从定义行开始,到定义它的代码块结束 | 整个类内部 |
命名冲突 | 在重合的作用范围内,命名不允许重复 | 当成员变量与局部变量命名冲突时,局部变量优先使用 |
4.方法重载
概念:同一个行为因为参数不同导致行为的执行流程有区别,可以定义多个同名的方法
语法要求:
1.访问修饰符,返回值类型无要求
2.函数名相同
3.形参列表不同(顺序,个数,类型)
使用重载方法时:编译器再编译时,会根据实际参数的类型,顺序,个数进行匹配
package object;
public class TestOverrideDemo{
public static void main(String[] args){
TestDemo demo = new TestDemo();
demo.m1(10);// 就近向上匹配原则[遵循类型自动提升的规则]
//demo.m1(1,1); error! 28 31 两个 m1方法 都可以选择 两个实参都可以进行类型就近向上匹配 引用不明确
}
}
class TestDemo{
public void m1(){
System.out.println("m1()");
}
/*
public int m1(int a){
System.out.println("m1(int)");
return a;
}
*/
/* error!
public void m1(int b){ // 形参名不同 不等价于 形参不同[类型 顺序 个数]
System.out.println("m1(b)");
}
*/
/*
public void m1(int a,int b){
System.out.println("m1(a,b)");
}*/
public void m1(int a,double b){
System.out.println("m1(int double)");
}
public void m1(double a,int b){
System.out.println("m1(double int)");
}
public void m1(float a){
System.out.println("float(a)");
}
public void m1(double a){
System.out.println(a);
}
public void m1(byte a){
System.out.println(a);
}
}
5.构造函数
作用
|-构建对象 [JVM构建对象时触发执行]
|—可以用来给属性赋予最终值
语法
1.无参构造
public 类名(){}
|-若未手动提供任何一个构造函数,系统默认提供一个无参构造
若手动提供了构造函数,则系统不再提供无参构造
2.有参构造
public 类名(形参列表){
}
3.构造函数存在重载,具体触发哪一个构造函数的执行,取决于 new 类名(实参)
取决于实参的类型,顺序,个数
//案例
package object;
public class TestConstructor{
public static void main(String[] args){
Cursor c = new Cursor(4);
//构建对象时,给与属性赋值
System.out.println(c.age);
}
}
class Cursor{
String name = "小黑";
int age = 2;
double score;
/*
构造函数: 1.没有 返回值类型 这一项 2.函数名 和 类名一致
3.构造方法只能被JVM构建对象时触发执行
4.构造函数存在重载
5.若未手动提供任何一个构造函数,系统默认提供一个无参构造
若手动填充了任何一个构造函数,系统不再默认填充无参构造
6.JVM具体触发哪一个构造函数的执行,取决于 new 类名(实参) 取决于括号中提供实参的类型,顺序,个数
7.给属性赋予最终值
public 类名(){}
public 类名(形式参数){}
*/
/*
public Cursor(){
System.out.println("构造函数被JVM触发执行,完成对象的构建");
}
*/
public Cursor(int a){
age = a;
}
public void m1(){}
}
6.对象的构建过程
1.分配空间,属性赋予默认值
2.初始化属性
3.触发构造函数的执行
---- 对象构建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4lh8plYx-1630564867835)(F:\javase\面向对象\截图\对象的构建过程.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15geJHxC-1630564867835)(F:\javase\面向对象\截图\成员方法的访问和执行流程.jpg)]
7.this关键字的用法
1. this. this 指代当前对象[在类的内部,对象保存在 this 引用]
this.属性 | this.方法()
使用对象的属性 使用对象的方法
//案例
package object;
public class TestThis{
public static void main(String[] args){
Sub sub = new Sub();
sub.m1();
System.out.println(sub.a);//10 类的外部,对象保存在 sub 引用中
}
}
class Sub{
String filed = "成员属性"; // 成员属性的作用范围:全类内部
int a = 10;// 属性
public void m1(){
int a = 20;// 局部变量
System.out.println(a);// 20
System.out.println(this.a);//10 访问对象的属性 类的内部,this指代的就是当前对象
}
public void m2(){
System.out.println(a);//10 编译器会将 a -------> this.a
}
}
package object;
public class TestStudent{
public static void main(String[] args){
Student stu1 = new Student();
Student stu2 = new Student("王宇希",30);
System.out.println(stu2.age); // 30
stu2.study();
}
}
class Student{
//成员变量
String name;
int age = 18;
public Student(){}
public Student(String name,int age){// 局部变量
this.name = name;
this.age = age;
}
public void study(){
System.out.println("study start.....");
m1();
System.out.println("study end.....");
}
public void m1(){
System.out.println("m1 start....");
System.out.println("m1 end....");
}
}
2. this(实际参数)
|-代表调用本类的其他构造函数
|-只能在构造函数中使用,必须书写在构造函数第一行
//案例
package object;
public class TestThis2{
public static void main(String[] args){
Dog dog2 = new Dog("富贵",2); // 2岁狗
Dog dog = new Dog("大黄"); // 2岁狗
}
}
class Dog{
String name;
int age;
public Dog(){
this.age = 2;
}
public Dog(String name){
//this();// 代表引用本类的其他构造函数 取决于 this 括号中实际参数的类型 顺序 个数
this(name,2);
}
public Dog(String name,int age){
this.name = name;
this.age = age;
}
}
8.引用
package object;
public class TestEcp{
public static void main(String[] args){
ClassA ca1 = new ClassA();
// ca1.a <==> 10
change1(ca1.a);
System.out.println(ca1.a); // 10
change2(ca1);
System.out.println(ca1.a); // 60
change3(ca1);
System.out.println(ca1.a); //10
ca1 = change4(ca1);
System.out.println(ca1.a); //80
}
public static void change1(int a){
a = 50;
}
public static void change2(ClassA ca){
ca.a = 60;
}
public static void change3(ClassA ca){
ca = new ClassA();
ca.a = 100;
}
public static ClassA change4(ClassA ca){
ca = new ClassA();
ca.a = 80;
return ca;
}
}
class ClassA{
int a = 10;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtteecQM-1630564867836)(F:\javase\面向对象\截图\传值1.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Y2TlHlu-1630564867837)(F:\javase\面向对象\截图\传值2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BCHFp1ls-1630564867837)(F:\javase\面向对象\截图\传值3.jpg)]
chp8_三大特性
1.封装
1.作用:
保护对象内部的属性值不被外界随意访问。
2.实现思路
|-属性私有化 private 修饰属性
|—提供出口[获取属性值]和入口[给属性赋值]函数
3.出口和入口函数的定义语法
入口函数
public void set属性名(形式参数){
this.属性 = 形式参数;
}
出口函数
public 属性的数据类型 get属性名(){
return this.属性
}
封装的语法:
语法:
private 数据类型 属性名;
public void set属性名(形参){ // 形式参数的类型与属性的数据类型一致
this.属性名 = 形参
}
public 属性的数据类型 get属性名(){
return this.属性;
}
注意: 属性的封装,分开进行 各个属性的get/set 方法独立
//案例
package object;
public class TestPrivate{
public static void main(String[] args){
Student stu = new Student(); // 学生对象
stu.setAge(10);
stu.setAge(-10);// 赋值失败
}
}
class Student{
private int age;
private String name = "王宇希";
private double score;
public void setName(String name){
this.name= name;
}
public String getName(){
return this.name;
}
public void setScore(double score){
this.score = score;
}
public double getScore(){
return this.score;
}
// 入口:用来给 age 赋值
public void setAge(int age){
if(age>=0&&age<=120){
this.age =age;
}else{
System.out.println("无效数据,赋值失败");
}
}
//出口
public int getAge(){
return this.age;
}
public Student(){}
public Student(int age,String name,double score){
this.age=age;
this.name=name;
this.score=score;
}
}
2.继承
1.生活中的继承: 一般讲究的是一种赠与关系 父亲的属性[财产,行为] 可以 给 儿子使用
Java中的继承: 指的是子类可以使用父类的属性和方法
2.父类是子类共性的抽取
父类中保留诸多子类共性的属性和行为
子类中只需要保留个性化的属性和行为
3.子类和父类之间是一种 继承关系 是一种 is a 关系 是一种 由 特殊 到 一般的过程
4.继承的语法
class 子类名 extends 父类名{}
5.继承的特点:
|-当继承关系建立后,子类可以使用父类中定义的属性和方法
|—子类内部只需要保留自己独有的属性和方法
|-减少代码冗余,提高程序的可复用性
package object;
public class TestExtendDemo{
public static void main(String[] args){
Dog d = new Dog();
d.sleep();
Lion li = new Lion();
// li 可以访问 name age 属性 可以调用 sleep()
}
class Lion extends Animal{
}
// 狗是一种动物 猫是一种动物 鸟是一种动物
// class 子类 extends 父类{} extends: is a
class Dog extends Animal{
String color;
public void eat(){}
public void shout(){}
}
class Cat extends Animal{
String color;
public void shout(){}
//爬树
public void jump(){}
}
class Bird extends Animal{
public void eat(){}
public void fly(){}
}
class Animal{
//父类的属性: 诸多子类共性内容的抽取
String name; // 访问修饰符 就是 默认(default)
int age;
//父类的行为:诸多子类共性行为的抽取
public void sleep(){
System.out.println("sleep....");
}
}
1.访问修饰符
public(公开的) ---> protected(受保护的) ---> 默认(default) ---> private(私有的) 由宽到窄
访问修饰符 | 本类内部 | 同包非本类 | 非同包子类 | 非同包非子类 | 继承性 |
---|---|---|---|---|---|
private | OK | 不可继承 | |||
默认(default) | OK | OK | 同包子类可以继承 | ||
protected | OK | OK | OK | 可以继承 | |
public | OK | OK | OK | OK | 可以继承 |
1.访问修饰符足够的属性和成员方法,子类可以继承使用
//案例
package day1;
public class Super{
protected int a = 10; // a 的访问修饰符为 default --- 同包非本类[同包子类 同包非子类]
}
class Sub1 extends Super{ // Sub1 与 Super 同包子类
public void m1(){
System.out.println(a);
}
}
package day2;
import day1.Super;
public class TestDemo{
public static void main(String[] args){
Sub2 sub = new Sub2();
sub.m1();
}
}
class Sub2 extends Super{ // Sub2 与 Super 非同包子类
public void m1(){
System.out.println(a);
}
}
2.方法覆盖
1.子类继承父类方法时,发现无法满足自身需求,子类使用特殊的方法实现,替换掉父类中的方法实现。
2.语法要求
访问修饰符: 子类方法的访问修饰符与父类方法的访问修饰符相同或者更宽
返回值类型: 子类方法的返回值类型与父类方法的返回值类型相同
子类方法的返回值类型是父类方法返回值类型的子类型
函数名:相同
形参列表:相同
3.若子类提供了方法覆盖,基于子类型引用进行调用时,子类的方法会优先调用。
package object;
public class TestSuper{
public static void main(String[] args){
Sub sub = new Sub();
sub.makeMoney();
}
}
class Super{
/*
方法:
声明部分:代表对象能做什么 访问修饰符 返回值类型 函数名(形式参数)
实现部分:代表对象的行为具体是怎么实施的 {}
父类方法的实现 满足不了子类的需求
--- 方法覆盖[保留父类方法的声明,替换其实现] 子类方法优先被调用
语法要求:
访问修饰符:子类方法的访问修饰符与父类方法的访问修饰符相同或者更宽
Animal Animal
返回值类型:子类方法的返回值类型与父类方法的返回值类型相同
Dog Animal
子类方法的返回值类型是父类方法返回值类型的 子类型
函数名:相同
形式参数:相同
*/
public void makeMoney(){
System.out.println("月入两千");
}
}
class Sub extends Super{
public void makeMoney(){
System.out.println("月入两万");
}
}
package extend;
public class TestExtend{
public static void main(String[] args){
Dog dog = new Dog();
dog.eat(); // 打印 Dog eat... 子类有,就直接访问
dog.shout();// 打印 Animal shout.. 子类没有,可以去父类中继承
}
}
class Animal{
int age;
String name;
void eat(){ // 访问修饰符是 默认的(default)
System.out.println("Animal eat....")
}
void sleep(){
System.out.println("Animal sleep...");
}
public Super m1(){
return new Super();
}
public Super m2(){
return new Super();
}
public void shout(){
System.out.println("Animal shout...");
}
}
class Dog extends Animal{
void eat(){
System.out.println("Dog eat.....");
}
public void sleep(){
System.out.println("Dog sleep...");
}
public Super m1(){
return new Super();
}
public Sub m2(){
return new Sub();
}
}
class Super{}
class Sub extends Super{}
3.存在继承关系对象的构建过程
存在继承关系子类对象的构建过程
1.分配空间,属性赋予默认值
2.初始化父类属性
3.触发父类构造函数的执行
4.初始化子类属性
5.触发子类构造函数的执行
//案例
package object;
public class TestAnimal{
public static void main(String[] args){
new MySub();
/*
存在继承关系子类对象的构建过程
1.分配空间,属性赋予默认值
2.初始化父类属性
3.触发父类构造函数的执行
4.初始化子类属性
5.触发子类构造函数的执行
Super对象的空间
field : null "父类的成员属性初始化结束"
Sub对象的空间
subField: null "子类的成员属性初始化结束"
输出结果:
父类的成员属性初始化结束
父类对象构建成功
子类的成员属性初始化结束
子类对象构建成功
*/
}
}
class MySuper{
String field = "父类的成员属性初始化结束";
public MySuper(){
System.out.println(field);
System.out.println("父类对象构建成功");
}
}
class MySub extends MySuper{
String subFiled = "子类的成员属性初始化结束";
public MySub(){
System.out.println(subFiled);
System.out.println("子类对象构建成功");
}
}
//案例2
package object;
public class TestA{
public static void main(String[] args){
new C();
/*
存在继承关系子类对象的构建过程
1.分配空间,属性赋予默认值
构建A对象
初始化A的属性
触发A构造函数的执行
构建B对象
初始化B的属性
触发B构造函数的执行
构建C对象
初始化C的属性
触发C构造函数的执行
内存中的变化
A对象空间
a : 0 10
B对象空间
b : 0 20
C对象空间
c : 0 30
打印结果
10
A对象构建结束
20
B对象构建结束
30
C对象构建结束
*/
}
}
// A是B的父类 B是C的父类
class A{
int a = 10;
public A(){
System.out.println(a);
System.out.println("A对象构建结束");
}
}
class B extends A{
int b = 20;
public B(){
System.out.println(b);
System.out.println("B对象构建结束");
}
}
class C extends B{
int c = 30;
public C(){
System.out.println(c);
System.out.println("C对象构建结束");
}
}
4.super关键字
super
1. super.
代表当前父类对象
|— 代表访问父类中的属性和方法
//案例
package object;
public class TestSuper{
public static void main(String[] args){
MySubClass sub = new MySubClass();
sub.m1();
}
}
class MySuperClass{
int a = 10;
int b = 10; // 父类的成员属性
}
class MySubClass extends MySuperClass{
int b = 20; // 子类的成员属性
public void m1(){
int b = 30; // 局部变量
System.out.println(a);// 继承于父类的 等价于 super.a
System.out.println(this.b);//20
System.out.println(super.b); // 10 来自于父类对象的成员属性 --- super
System.out.println(b); // 30
}
}
2. super()
代表调用父类的构造方法
|-在子类构造函数中使用,必须书写在构造函数第一行
//案例
package object;
public class TestSuper{
public static void main(String[] args){
new MySubClass(10);
}
}
class MySuperClass{
public MySuperClass(){
System.out.println("父类的无参构造");
}
public MySuperClass(int a){
System.out.println("父类的有参构造 "+a);
}
}
class MySubClass extends MySuperClass{
public MySubClass(){
super(10);
System.out.println("子类的无参构造");
}
public MySubClass(int a){
this();
System.out.println("子类的有参构造");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Rdz2mYc-1630564867838)(F:\javase\三大特性\截图\super.jpg)]
3.多态
父类型的引用指向子类型的对象
Animal a = new Dog();
主观类型 实际类型
父类型 子类型
使用:
编译阶段: 父类型的引用只可以访问父类中已经声明的属性和方法
运行阶段: 根据实际类型去调用对应的方法。若子类型对象具备该方法,则遵循方法覆盖的优先级执行顺序,子类 的方法优先调用.若子类对象不具备该方法,可以继承于父类
//案例
package dt;
public class TestDT{
public static void main(String[] args){
Dog d = new Dog(); // 将狗对象放入狗笼子 狗对象是狗类型 ----- 狗笼子
Cat c = new Cat(); // 将猫对象放入猫笼子 猫对象是猫类型 ----- 猫笼子
Animal a = new Dog(); // 狗是一种动物 狗对象放入动物笼子
/*
主观类型 实际类型
父类型 子类型 父类型的引用指向子类型的对象 ---- 多态
*/
a.eat();
// error! a.shout();
// error! a.catchMouse();
}
}
class Animal{
public void eat(){
System.out.println("Animal eat....");
}
}
// Dog is a Animal 狗是一种动物
class Dog extends Animal{
public void eat(){
System.out.println("Dog eat...");
}
public void shout(){
System.out.println("吠.....");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("Cat eat....");
}
public void catchMouse(){
System.out.println("Cat 捉老鼠");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qsiCFCkE-1630564867838)(F:\javase\三大特性\截图\多态.jpg)]
1.引用类型之间的转换
1.自动类型转换(向上转型|装箱)
Animal a = new Dog();
Animal b = new Cat();
2.强制类型转换(向下转型|拆箱)
Dog dog = (Dog)a;
Cat cat = (Cat)a;
注意:
1.转换的两种类型之间,满足 is a关系
2.向下转型有转换失败的风险,所以在进行强制类型转换时,必须保证实际类型与转换类型一致
2.instanceof关键字
引用 instanceof 类型
作用:判断引用中保存的对象类型与 关键字其后的类型 是否兼容 --- 表达式 true[兼容]|false[不兼容]
Animal a = new Dog();
if(a instanceof Dog){ -- a中保存的对象是否为Dog类型或者Dog的子类型
Dog dog = (Dog)a;
}
//案例
if(a instanceof Dog){
Dog dog = (Dog)a;
}
if(a instanceof Cat){
Cat cat = (Cat)a;
}
3.多态的应用场景
package obj;
import java.util.Scanner;
public class TestMaster{
public static void main(String[] args){
/*
饲养员买动物,买回来以后喂养
狗 2000/只
猫 1500/只
鱼 500/只
1.找对象 饲养员 狗 猫 鱼 null
2.确定对象与对象之间的关联关系
3.具体过程
*/
Scanner s = new Scanner(System.in);
System.out.print("请输入钱数:");
double money = s.nextDouble();
Master m = new Master();
Animal a = m.buyAnimal(money);
if(a!=null){
m.feed(a);
}else{
System.out.println("钱不够,买不到,没办法喂养");
}
}
}
class Master{
// 多态用在方法返回值类型上
public Animal buyAnimal(double money){
if(money<500){
return null;
}else if(money<1500){
return new Fish();
}else if(money<2000){
return new Cat();
}else{
return new Dog();
}
}
public void feed(Animal a){ // 多态用在方法形参上
a.eat();
}
}
class Animal{
public void eat(){
System.out.println("Aniaml eat");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("Dog eat");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("Cat eat");
}
}
class Fish extends Animal{
public void eat(){
System.out.println("Fish eat");
}
}
chp9_三大修饰符
1.abstract
1.abstract修饰类
抽象:似是而非,像却不是。
用法:
1.abstract 修饰类
|-语法:abstract class 类名{}
|-抽象类,似是而非的类,不完整的类
|—只允许声明引用,无法实例化对象,无法 new 对象
|—可以被子类继承,用于保存子类型对象,强制使用多态
|—抽象类内部存在构造函数,供子类使用(构建子类对象时,需要构建父类对象时使用)。抽象父类的构造函数无 法通过 new 关键字触发
package demo;
public class TestAbstract{
public static void main(String[] args){
// error! new Animal();
Animal a = new Dog(); //强制使用多态
}
}
abstract class Animal{ // 抽象类:不允许实例化对象 不允许new对象
int age;
String name;
public void sleep(){}
}
class Dog extends Animal{
boolean sex;
}
class Cat extends Animal{}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBreqjIV-1630564867839)(F:\javase\三大修饰符\截图\抽象类.jpg)]
2.abstract修饰方法
1.抽象方法: 只有方法声明,没有方法实现
完整的方法: 访问修饰符 返回值类型 函数名(形式参数列表){函数体}
抽象方法: 访问修饰符 abstract 返回值类型 函数名(形式参数列表);
2.子类继承了抽象的父类,若子类想要成为完整的类,必须覆盖实现父类中所有的抽象方法,否则子类也是抽象类
//案例
package demo;
public class TestAbstract{
public static void main(String[] args){
// error! new Animal();
Animal a = new Cat();
}
}
abstract class Animal{ // 抽象类:不允许实例化对象 不允许new对象
int age;
String name;
public void sleep(){}
//修饰方法 抽象方法:只有方法声明,没有方法实现 访问修饰符 返回值类型 函数名(形式参数列表);
public abstract void eat();
}
//子类继承了抽象的父类,若抽象父类中存在可继承的抽象方法
// 子类由于继承了抽象方法 导致自身不完整 --- 子类也是抽象类
abstract class Dog extends Animal{
boolean sex;
}
//子类继承了抽象的父类,若子类想要成为完整的类,必须覆盖实现父类中所有的抽象方法
class Cat extends Animal{
public void eat(){
}
}
1.抽象类中不一定有抽象方法
2.抽象方法必须位于抽象类中
3.抽象类中可以正常持有属性和完整方法
4.若一个类中存在抽象方法,该类一定是抽象类
3.抽象类的应用
1.抽象的好处
更加的贴近现实,符合客观规律
更加符合父类抽取子类共性的特点
标准分离---方法声明放在父类中,而方法的实现交给子类完成
2.抽象与多态的结合好处 --- 解耦合
//案例
package demo;
public class TestLamp{
public static void main(String[] args){
//在灯座上安装灯泡,打开开关,灯发光
GreenBulb blub = new GreenBulb();
RedBulb red = new RedBulb();
Lamp lamp = new Lamp();
//将灯泡放置在灯座
lamp.setShine(red); // Shine shine = new GreenBulb(); Shien shine = new RedBulb();
//打开开关
lamp.turnOn();
}
}
//耦合:两个类之间的紧密程度
//强耦合:一方改变,影响另一方 ---> 解耦合
//灯座
class Lamp{
private Shine shine;
public void setShine(Shine shine){
this.shine=shine;
}
public Shine getShine(){
return this.shine;
}
public void turnOn(){
shine.shine();
}
}
//发光的抽象类
abstract class Shine{
//建立发光的规范
abstract void shine();
}
class RedBulb extends Shine{
public void shine(){
System.out.println("发红光......");
}
}
class GreenBulb extends Shine{
public void shine(){
System.out.println("发绿光......");
}
}
2.static
静态:归类所拥有。
1.修饰属性
静态属性,又称之为静态变量,类变量
语法:
访问修饰符 static 数据类型 属性名;
or
static 访问修饰符 数据类型 属性名;
特点
1.静态属性归类所拥有,被类的对象所共享,有且只有一份
2.对于静态属性的访问,可以使用 类名直接发起访问
3.即使基于保存对象的引用发起静态属性的访问---编译阶段,编译器也会将引用提升为 类名.静态属性
4.对于静态属性的访问,不依赖于对象
package demo;
public class TestStatic{
public static void main(String[] args){
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
mc1.a = 10;
mc2.a = 20;
System.out.println(mc1.a);// 10
System.out.println(mc2.a);// 20
mc1.b = 30;
mc2.b = 50;
System.out.println(mc1.b); //50 编译器会进行提升 mc1.b --- MyClass.b
System.out.println(mc2.b); // 50 编译器会进行提升 mc2.b --- MyClass.b
System.out.println(MyClass.b); // 类名.静态属性 ----- 推荐使用
System.out.println(MyClassA.c);// 静态属性的访问和对象没关系
}
}
class MyClass{
int a;
static int b; // 静态属性|类属性 : 被类的对象所共享,有且只有一份 可以通过类名直接发起访问
}
class MyClassA{
static int c = 50;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RXAtsueh-1630564867840)(F:\javase\三大修饰符\截图\静态属性.jpg)]
//统计MyClassA 对象构建次数
package demo;
public class TestStatic{
public static void main(String[] args){
// 统计MyClassA对象的构建次数
new MyClassA();
new MyClassA();
new MyClassA();
new MyClassA();
new MyClassA();
new MyClassA();
System.out.println(MyClassA.sum);
}
}
class MyClassA{
static int c = 50;
static int sum = 0;
public MyClassA(){
sum++;
}
}
2.修饰方法
静态方法|类方法
1.静态方法的语法
访问修饰符 static 返回值类型 函数名(形式参数列表){方法实现}
or
static 访问修饰符 返回值类型 函数名(形式参数列表){方法实现}
//案例
public static void main(String[] args){}
static public void main(String[] args){}
static public void main(String[] a){}
//error! public void static main(String[] args){}
2.有且只有一份,可以使用类名直接发起调用 ----- 类名.方法名(实参)
静态方法使用时注意:
1.静态方法内部无法使用类的非静态成员(成员变量,成员方法)
2.静态方法可以被继承
3.父类的静态方法可以被子类的静态方法所覆盖,但是没有多态
//案例
package demo;
public class TestStaticMethod{
public static void main(String[] args){
MyClassB mb = new MyClassB();
mb.m1();
MyClassB.m2(); // 编译阶段 编译器会将 mb 提升为所属的类型 MyClassB.m2();
MyClassB sub = new MySub();
sub.m1();
sub.m2(); // 编译阶段 编译器将引用提升为所属类型 sub.m2()----- MyClassB.m2();
}
}
class MyClassB{
int a = 10;//成员变量 (非静态变量)
static int b = 20;//静态变量
public void m1(){
System.out.println("父类的成员方法 静态属性:"+b);
System.out.println("父类的成员变量"+a);
}
public static void m2(){
System.out.println("父类的静态方法 "+b);
//error! System.out.println("成员变量 "+a); // 静态方法内部无法引用本类的非静态成员(成员方法,成员变量)
}
}
class MySub extends MyClassB{
public void m1(){
System.out.println("子类覆盖父类的成员方法..");
}
public static void m2(){
System.out.println("子类覆盖父类的静态方法");
}
}
3.类加载
1.类加载
当JVM第一次使用到某个类的信息时,需要将字节码文件找那个类的描述信息读取到程序中并保存.
2.类加载的时机
1.创建对象
2.创建子类对象
3.访问类的静态属性
4.调用类的静态方法
5.Class.forName("包名.类名");
3.描述信息的加载顺序
先加载静态成员(静态属性,静态方法),再加载非静态成员(成员变量,成员方法)
4.动态代码块
1.动态代码块 --- 等同于普通的成员方法
书写位置:类以内,其他函数以外。最好是书写在属性和方法之后
书写语法:
{
//功能语句
}
执行:创建对象时,会触发动态代码块的执行
执行顺序:初始化属性之后,构造方法执行之前
作用:可以给实例属性赋值(给实例属性进行必要的赋值)
//案例
package day2;
public class TestStaticDemo{
public static void main(String[] args){
new Super();
}
}
class Super{
int a = 10;
public void m1(){
System.out.println("m1...");
}
//动态代码块
{
System.out.println("动态代码块开始执行...");
System.out.println("成员属性:"+a);
m1();
System.out.println("动态代码块执行结束...");
}
public Super(){
System.out.println("Super()...对象构建结束");
}
}
5.静态代码块
1.静态代码块 ---- 等同于静态方法
1.定义位置:本类内部
2.语法
static{
//功能代码
}
3.执行:类加载时,会被触发执行一次
4.执行顺序:在构建对象之前
2.静态成员 ---- 只会被加载一次
静态属性 静态方法 静态代码块
非静态成员
成员变量 成员方法 动态代码块
3.静态代码块内部无法使用类的非静态成员
//案例
package day2;
public class TestStaticDemo{
public static void main(String[] args){
new Super();
new Super();
new Super();
}
}
class Super{
int a = 10;
public void m1(){
System.out.println("m1...");
}
//动态代码块
{
System.out.println("动态代码块开始执行...");
System.out.println("成员属性:"+a);
m1();
System.out.println("动态代码块执行结束...");
}
//静态代码块
static{
System.out.println("静态代码块执行......");
}
public Super(){
System.out.println("Super()...对象构建结束");
}
}
6.完整的对象构建过程
1.组成部分
成员属性 成员方法 动态代码块 构造方法 静态属性 静态方法 静态代码块
2.构建过程
注意: 类加载时,给静态属性分配空间,赋予默认值,并初始化
package day2;
public class TestStaticDemo{
public static void main(String[] args){
new Sub();
new Sub();
new Sub();
/*
程序的输出结果:
分配空间
1.加载父类的静态成员(静态属性,静态方法,静态代码块)
2.加载子类的静态成员(静态属性,静态方法,静态代码块)
----- 只加载一次
3.初始化父类属性
4.加载执行父类动态代码块
5.触发父类构造函数的执行
6.初始化子类属性
7.加载执行子类的动态代码块
8.触发子类构造函数的执行 ------ 第一个子类对象构建结束
若构建多个子类对象 重复执行 345678
*/
}
}
class Super{
String field = "父类的成员变量";
static String staticField = "父类的静态属性";
public void m1(){
System.out.println("父类的成员方法");
}
public static void m2(){
System.out.println("父类的静态方法");
}
{
System.out.println(field);
System.out.println("父类的动态代码块");
}
static{
System.out.println(staticField);
System.out.println("父类的静态代码块......");
}
public Super(){
System.out.println("父类的构造方法,父类对象构建结束");
}
}
class Sub extends Super{
String field = "子类的成员变量";
static String staticField = "子类的静态属性";
public void m1(){
System.out.println("子类的成员方法");
}
public static void m2(){
System.out.println("子类的静态方法");
}
{
System.out.println(field);
System.out.println("子类的动态代码块");
}
static{
System.out.println(staticField);
System.out.println("子类的静态代码块......");
}
public Sub(){
System.out.println("子类的构造方法,子类对象构建结束");
}
}
/*
程序的输出结果=========
父类的静态属性
父类的静态代码块......
子类的静态属性
子类的静态代码块......
父类的成员变量
父类的动态代码块
父类的构造方法,父类对象构建结束
子类的成员变量
子类的动态代码块
子类的构造方法,子类对象构建结束
父类的成员变量
父类的动态代码块
父类的构造方法,父类对象构建结束
子类的成员变量
子类的动态代码块
子类的构造方法,子类对象构建结束
父类的成员变量
父类的动态代码块
父类的构造方法,父类对象构建结束
子类的成员变量
子类的动态代码块
子类的构造方法,子类对象构建结束
*/
3.final
最终,最后,不可修改。
1.修饰类
final class 类名{}
1.final 修饰类,该类不可被继承 --- 断子绝孙
2.常见被 final 修饰的类
String System Math
2.修饰变量
1.该变量会成为常量
成员变量 --- 成员常量
局部变量 -- 局部常量
2.值一旦给定,不允许修改
若变量为基本类型,保存的数值不可修改
若变量为对象类型,保存的地址不可修改
3.修饰属性
1.属性没有默认值
2.赋值机会 --- 普通属性
|-声明并赋值
|-借助动态代码块赋值
|-构造函数赋值(必须保证类中所有的构造函数都能对其正确赋值)
三次赋值机会,只能使用其中一种 ----- 必须成功
3.静态属性的赋值机会
|-声明并赋值
|—借助静态代码块赋值
4.常量的命名规范是:字母全大写,若为做个单词,使用下划线连接
3.修饰方法
1.方法无法被子类所覆盖
2.可以被继承
4.修饰符的梳理
public 类 属性 方法 构造方法
protected 属性 方法 构造方法
默认(default) 类 属性 方法 构造方法
private 属性 方法 构造方法
哪些修饰符不可连用?
private abstract 不能连用
final abstract 不能连用
static abstract 不能连用
IDEA的使用
chp10_接口
1.接口的相关概念
1.接口是一种特殊的抽象类
2.接口
属性 : 公开静态常量 public static final
方法 : 公开抽象方法(jdk1.8之前) public abstract
3.接口与抽象类之间的区别
相同点
1.编译之后都会生成字节码文件(.class)
2.接口与抽象类都是Java中的一种数据类型
3.可以声明引用,无法实例化对象
不同点
1.接口中没有构造函数(不允许书写),抽象类中存在构造函数
2.关键字不同 接口的 interface 抽象类 abstract class
3.接口中的属性都是公开静态常量 public static final
接口中的方法都是公开抽象方法 public abstract
//案例
package demo;
public class TestInterfaceDemo{
public static void main(String[] args){
System.out.println(MyInterface.A);
MyClass mc = new MySub(); // 抽象父类型的引用指向子类型对象
MyInterface mc2 = new MySubClass(); // 接口类型的引用指向实现类的对象
}
}
//抽象类特点: 1.无法实例化对象
abstract class MyClass{
int a = 10; // 访问修饰符为 default
abstract void m1();//访问修饰符 default
}
class MySub extends MyClass{
void m1(){}
}
//接口 1.语法关键字 2. 无法实例化对象,但是可以声明引用
interface MyInterface{
int A = 10; // public static final int A = 10;
void m1(); // public abstract void m1();
}
class MySubClass implements MyInterface{
public void m1(){}
}
2.接口使用的语法
定义一个接口
interface 接口名{
}
实现接口
class 实现类类名 implements 接口1,接口2,接口3...{
}
注意:
1.实现类实现了多个接口,若实现类想要成为完整的类,必须覆盖实现接口中所有的公开抽象方法,否则实现类还是一 个抽象类
2.类与类 类与接口 接口与接口
类与类 单继承 多级继承
类与接口 多实现
|-一个类可以实现多个接口
|-一个接口可以有多个实现类
接口与接口 多继承
3.多态
父类型的引用指向子类型的对象
接口类型的引用指向实现类的对象
//案例
package testInterface;
public class TestInterfaceDemo {
public static void main(String[] args) {
/*
类与类
单继承 多级继承
类与接口
多实现
|-一个类可以实现多个接口
|-一个接口可以有多个实现类
接口与接口
多继承
*/
IA ia = new MyImplementClass();// 接口类型的引用指向实现类的对象---多态
IB ib = new MyImplementClass();
IC ic = new MyImplementClass();
ID id = new MyImplementClass();
}
}
class MyImplementClass implements IA,IB{
public void m1(){}
public void m2(){}
public void m3(){}
public void m4(){}
public void m5(){}
public void m6(){}
}
class MySubClass implements IB{
public void m2(){}
}
//一个接口可以继承多个父接口
interface IA extends IC,ID{
void m1(); // public abstract void m1();
}
interface IB{
void m2();
}
interface IC{
void m3();
void m4();
}
interface ID{
void m5();
void m6();
}
3.类型转换
自动类型转换(向上转型|装箱) ---- 多态
父类型引用保存子类型对象
接口型引用保存实现类的对象
强制类型转换(向下转型|拆箱)
父类型(接口型)的引用,赋值给子类型的引用,需要强转。
注意:
1.强转的两种类型,如果包含接口类型,编译一定通过
2.运行之前,使用 instanceof 关键字判断
//案例
package testInterface;
public class TestAnimal {
public static void main(String[] args) {
/*
*编译阶段
* a 是 Animal类型 a中保存的是Animail类型的对象 或者 Animal的子类型对象
* 保存的是 Dog对象 或者 Snoopy对象
* 1.有可能是Snoopy Snoopy----->Person
* 2.强转符号
* 运行阶段
* 关注实际对象和强转类型之间的关系
*/
Animal a = new Dog();
if(a instanceof Person){
Person p = (Person)a;
}
}
}
class Animal{}
class Dog extends Animal{}
interface Person{}
class Snoopy extends Dog implements Person{ }
4.接口的优点
1.接口可以扩充类的能力
2.父类中保存主要功能,接口中保存次要功能,接口在设计的时候:各司其职,分门别类.
3.维护了Java单继承的简单性,弥补了单继承的不足.
4.接口的优点
|—接口是一种规范,是一种标准
|-解耦合
耦合:两个类之间的关系
|-强耦合:一方改变,影响另一方
|-弱耦合:类与类之间的关联关系降低
|-解耦合:一方改变,对另一方没有影响
面向对象的开发思想:两个类的耦合度越低越好。
//案例
package testInterface;
public class TestLight {
public static void main(String[] args) {
Light light = new Light();
RedBulb red = new RedBulb();
GreenBulb green = new GreenBulb();
//安装灯泡
light.setShine(green); // 多态
light.turnOn();
}
}
//发光规范
interface Shine{
void shine();
}
class Light{
private Shine shine;
public Shine getShine() {
return shine;
}
public void setShine(Shine shine) {
this.shine = shine;
}
public void turnOn(){
shine.shine();
}
}
class GreenBulb implements Shine{
public void shine(){
System.out.println("发绿光");
}
}
class RedBulb implements Shine{
public void shine(){
System.out.println("发红光");
}
}
5.接口的分类
1.功能性接口(接口中只有一些方法的声明)
interface IA{
void m(); // public abstract void m();
void m2();
}
2.常量接口(接口中只有公开静态常量)
interface IB{
int A = 10;// public static final int A = 10
}
3.标记接口(接口中没有属性,没有方法)
interface IC{}
4.函数式接口(接口中只有一个抽象方法的声明)
6.适配器设计模式
接口和抽象类 | 接口和类 组合使用 ------- 简化开发
接口中的方法并不都是我们所需求的,此时可以书写(提供)一个父类,让父类实现接口,并对接口中的方法提供默认实现,子类由实现接口改为继承父类,可以选择覆盖需要的方法------简化开发
interface MyIA{
void m1();
void m2();
void m3();
void m4();
void m5();
}
class MyIASuper implements MyIA{
public void m3(){}
public void m4(){}
public void m5(){}
public void m1(){}
public void m2(){}
}
class MyIAClass extends MyIASuper{ // 想让类 拥有 m1()行为
public void m1(){}
}
class MyIBClass extends MyIASuper{
public void m2(){}
}
7.接口回调
先有接口的使用者,再有接口的实现者 先有排序规则的使用者,再有排序规则的实现者
接口可以屏蔽底层实现类的差异。
//案例
package testInterface;
import java.util.Arrays;
public class TestSort {
public static void main(String[] args) {
/*int[] a = {9,8,6,1,4};
Arrays.sort(a); // 支持的排序规则 0 - 9 A-Z sort()[张昊]----使用排序规则
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
*/
Student[] stus = {
new Student(17,80.0,"wangyx"),
new Student(18,90.0,"xuk"),
new Student(20,80.0,"wp"),
new Student(18,90.0,"zh")
};
Arrays.sort(stus);
for (int i = 0; i < stus.length; i++) {
System.out.print(stus[i].getAge()+" ");
System.out.print(stus[i].getName()+" ");
System.out.print(stus[i].getScore());
System.out.println();
}
}
}
// 借助 Comparable 接口 进行 规则使用者 和 规则实现者 之间的通信
class Student implements Comparable<Student>{
private int age;
private double score;
private String name;
// 制定排序规则
/*
* return 0 代表两个比较的学生 内容相等
* return 1 代表第一个比第二个大
* return -1 代表第一个比第二个小
*/
public int compareTo(Student s){
if(this.age>s.age){
return 1;
}else if(this.age<s.age){
return -1;
}else{
if(this.score>s.score){
return 1;
}else if(this.score<s.score){
return -1;
}else{
if(this.name.compareTo(s.name)>0){
return 1;
}else if(this.name.compareTo(s.name)<0){
return -1;
}else{
return 0;
}
}
}
}
public Student() {
}
public Student(int age, double score, String name) {
this.age = age;
this.score = score;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
8.高版本语法补充
JDK8.0
1.接口中定义默认方法
default 返回值类型 方法名(形式参数){
//方法实现
}
注意: 该方法的访问修饰符 public
2.静态方法
static 返回值类型 方法名(形参列表){
//方法实现
}
注意 : 访问修饰符是 public
JDK9.0
1.私有方法
private 返回值类型 函数名(形式参数){
//方法实现
}
chp11_常用类
1.内部类
概念:定义在 类 内部的类
class Outer{
class Inner{
// 内部类
}
}
内部类: 成员内部类 静态内部类 局部内部类 匿名内部类(是一种特殊的局部内部类)
1.成员内部类(了解)
定义位置:定义在类以内,其他函数以外 等同于成员变量
//案例
package object;
public class TestInnerClass1 {
public static void main(String[] args) {
//1.获取外部类的对象
Outer out = new Outer();
System.out.println(out.a);
//2.基于外部类的引用去构建内部类的对象
Outer.Inner inner = out.new Inner();
inner.m1();
}
}
class Outer{
//成员变量
int a = 10;
private int b = 50;
//成员内部类
class Inner{
int a = 20;
public void m1(){
int a = 30;
System.out.println("Inner m1()...");
System.out.println(a);//30
System.out.println(this.a);//20
System.out.println(Outer.this.a);//10
System.out.println(b);//50
}
}
}
注意:
1.成员内部类编译也会生成字节码文件 文件名通常为 外部类类名$内部类类名
例如
class Outer{
class Inner{
}
}
编译之后的产物: Outer.class Outer$Inner.class
2.创建内部类的对象
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
3.成员内部类可以访问外部类的私有属性,不会破坏封装
4.如果成员内部类的成员属性与外部类的成员属性命名冲突---参照代码
5.成员内部类中,不允许出现静态内容(静态属性 静态代码块 静态方法)
2.静态内部类(了解)
定义: 定义在类以内,其他函数以外,且 被 static 修饰 等同于 静态属性|静态方法
定义语法:
class Outer{
static class Inner{
//静态内部类
}
}
//案例
package object;
public class TestInnerClass1 {
public static void main(String[] args) {
Outer.StaticInner inner = new Outer.StaticInner();
inner.m1();
System.out.println("=======");
Outer.StaticInner.m2();
}
}
class Outer{
int a = 10;
static int b = 20;
static int c = 30;
//静态内部类 -- 等同于静态属性|静态方法
static class StaticInner{
int a = 20;
static int b = 40;
public void m1(){
int a = 30;
int b = 50;
System.out.println("StaticInner m1()...");
System.out.println(a);//30
System.out.println(this.a);//20
System.out.println(b);//50
System.out.println(StaticInner.b);//40
System.out.println(Outer.b);//20
}
public static void m2(){
int a = 30;
int b = 30;
System.out.println("StaticInner static m2()....");
System.out.println(a);//30
System.out.println(b);//30
System.out.println(StaticInner.b);//40
System.out.println(Outer.b);//20
}
}
}
注意:
1.静态内部类中只可以访问外部类的静态成员
2.静态内部类的使用参照代码
3.局部内部类[熟悉]
定义: 定义在函数内部 等同于局部变量,编译同样会生成字节码文件
语法;
class Outer{
public void m1(){
int a = 10;//局部变量
class Inner{
//局部内部类
}
}
}
//案例
package object;
public class TestInnerClass1 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
}
}
class Outer{
private int a = 10;
private static int b = 20;
public void m1(){
System.out.println("外部类的成员方法m1()...");
final int C = 30; // 外部类的局部变量 -- 局部常量
//局部内部类 : 定义在函数内部[函数体中] 相当于局部变量
class Inner{
int a = 20;
public void m1(){
int a = 40;
System.out.println(a); //40
System.out.println(this.a); // 20
System.out.println(Outer.this.a); // 10
System.out.println(b); // Outer.b
System.out.println(C);// 30 局部内部类使用外部类的局部变量时,要求该局部变量被final修饰[JDK1.7]
System.out.println("局部内部类Inner的m1()....");
}
}
Inner inner = new Inner();
inner.m1();
}
}
注意:
1.局部内部类可以访问外部类的局部常量
2.局部内部类对象的构建,需要书写在局部内部类之后
3.局部内部类的优点:隐藏类的实现细节
//案例
package object;
import java.util.Scanner;
public class TestSchool { // 测试类 ---- 学生
public static void main(String[] args) {
School s = new School();
Scanner scan = new Scanner(System.in);
System.out.print("*******欢迎光临航院选课系统*******请输入选课编号:");
int num = scan.nextInt();
Teach t = s.getTeacher(num);
t.teach();
}
}
class School{
//选课系统
public Teach getTeacher(int num){
class TeacherA implements Teach{
public void teach(){
System.out.println("王宇希讲JDK的安装");
}
}
class TeacherB implements Teach{
public void teach(){
System.out.println("高世昌讲环境变量的配置");
}
}
if(num%2!=0){
return new TeacherA();
}else{
return new TeacherB();
}
}
}
interface Teach{
void teach();
}
4.匿名内部类[重点]
是一种特殊的局部内部类. 将类的定义,方法的实现,对象的构建三合一
语法: 一般抽象类或者接口中只有一个抽象方法 --- 构建对象
new 抽象类(){
//覆盖抽象方法
};
new 接口名(){
//覆盖抽象方法
};
//改写
package object;
import java.util.Scanner;
public class TestSchool { // 测试类 ---- 学生
public static void main(String[] args) {
School s = new School();
Scanner scan = new Scanner(System.in);
System.out.print("*******欢迎光临航院选课系统*******请输入选课编号:");
int num = scan.nextInt();
Teach t = s.getTeacher(num);
t.teach();
}
}
class School{
//选课系统
public Teach getTeacher(int num){
if(num%2==0){
return new Teach(){
public void teach(){
System.out.println("王宇希安装JDK");
}
};
}else{
return new Teach(){
public void teach(){
System.out.println("高世昌讲解环境变量");
}
};
}
}
}
interface Teach{
void teach();
}
5.Lambda表达式
进一步简化匿名内部类的语法。 --- 获取接口类型的对象
语法:
1.完整写法
(数据类型 变量1,数据类型 变量2)->{方法实现}
2.简化写法(推荐)
(变量1,变量2)->表达式
注意:
1.多句表达式必须使用大括号
2.return 语句不可视为单句实现---必须加上大括号
3.Lambda表达式:实现接口并创建对象 --- 更加关注函数实现
//案例
package object;
import java.util.Scanner;
public class TestSchool { // 测试类 ---- 学生
public static void main(String[] args) {
School s = new School();
Scanner scan = new Scanner(System.in);
System.out.print("*******欢迎光临航院选课系统*******请输入选课编号:");
int num = scan.nextInt();
Teach t = s.getTeacher(num);
t.teach();
/*匿名内部类
Teach tea = new Teach(){
public void teach(){
System.out.println("123456");
}
};
//Lambda表达式 接口或者抽象类中只有一个抽象方法
Teach teach = ()->{System.out.println("123456");};
*/
}
}
class School{
//选课系统
public Teach getTeacher(int num){
if(num%2==0){
return ()->{System.out.println("王宇希安装JDK");};
}else{
return ()->{System.out.println("高世昌设置环境变量");};
}
}
}
interface Teach{
void teach();
}
2.Object类
Object 是根类 超类 是所有类的直接父类或者间接父类,可以保存任意类型的对象
class Animal{} // class Animal extends Object{} Object 是Animal的直接父类
class Dog extends Animal{}
Object常用的方法
1. getClass(): 获取当前引用中保存对象的实际类型
//案例
package object;
public class TestObject {
public static void main(String[] args) {
Animal a = new Dog();
Animal b = new Cat();
//判断 a 和 b 两个引用中保存的对象实际类型是否一致
/*if(a instanceof Dog && b instanceof Dog){
System.out.println("都是狗");
}else if(a instanceof Cat && b instanceof Cat){
System.out.println("都是猫");
}else{
System.out.println("类型不一致");
}*/
if(a.getClass()==b.getClass()){
System.out.println("类型一致");
}else{
System.out.println("类型不一致");
}
}
}
class Animal{} // class Animal extends Object{}
class Dog extends Animal{}
class Cat extends Animal{}
class Wolf extends Animal{}
class Tiger extends Animal{}
class Pig extends Animal{}
2.public int hashCode():获取引用中保存对象的哈希码值 [根据对象的地址 经过哈希算法 算出来的]
//案例
package object;
public class TestHashCode {
public static void main(String[] args) {
Student stu1 = new Student("小王",3,80);
Student stu2 = new Student("小李",3,80);
System.out.println(stu1.hashCode());
System.out.println(stu2.hashCode());
}
}
class Student{
private String name;
private int age;
private double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
3.public String toString():返回当前引用中保存对象的字符串描述
//案例
package object;
public class TestHashCode {
public static void main(String[] args) {
Student stu1 = new Student("小王",3,80);
System.out.println(stu1); // 打印对象 相当于 打印对象 toString 方法的返回值
System.out.println(stu1.toString());
}
}
class Student{ // class Student extends Object{}
private String name;
private int age;
private double score;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
4.public boolean equals(Object o):判断当前引用中保存的对象与 o 引用中保存的对象是否一样(内容是否一致)
== 双等号比较的是地址
//案例
package object;
public class TestHashCode {
public static void main(String[] args) {
Student stu1 = new Student("小王",3,80);
Student stu2 = new Student("小王",3,80);
System.out.println(stu1==stu2); // == 比较的是地址是否相同
System.out.println(stu1.equals(stu2));
}
}
class Student{ // class Student extends Object{}
private String name;
private int age;
private double score;
public boolean equals(Object o){ // 写下来
if(this==o)return true;
if(o==null)return false;
if(this.getClass()!=o.getClass())return false;
Student stu =(Student)o;
if(this.age==stu.age&&this.score==stu.score&&this.name.equals(stu.name)){
return true;
}else{
return false;
}
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
5.finalize(): 销毁垃圾对象时,释放内存空间调用的方法
垃圾对象: 没有任何引用指向的对象
Student stu = new Student("小王");
stu = null; // 小王同学就变成了垃圾对象
垃圾回收机制:
自动回收机制:
JVM的内存耗尽,不能再为新创建的对象分配空间,一次性回收掉所有的垃圾对象(自动调用对象的finalize() 方法)
手动回收机制
System.gc() 通知垃圾回收器进行垃圾回收.如果GC(垃圾回收器)空闲,则回收,若GC繁忙则暂时不回收
3.包装类
为了方便 Object 统一管理所有类型的数据,引入包装类
JDK1.5 以后 , 可以使用包装类型的变量 直接存储基本类型的数据
基本类型转换为包装类型 ---- 自动装箱
包装类型转换为基本类型 ---- 自动拆箱
例如:
int a = 10;
Integer b = a;
基本类型 | 包装类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1.基本类型与包装类型之间的手动转换
package object;
public class TestInteger {
public static void main(String[] args) {
/*
1.基本类型转换为对应的包装类型
|-1.通过构建对象,将基本类型的变量充当构造参数
|-2.通过对应包装类中的静态方法 valueOf(基本类型的参数)
2.包装类型转换为对应的基本类型
|-借助对应包装类型中的方法 包装类型的引用.xxxValue();
3.String|基本类型 ----- 包装类型
public static 包装类型 valueOf(String|基本类型)
例如:
String -- Double
Double d1 = Double.valueof("1.2");
4.String转换为对应的包装类型时,需要保证字符串的内容形式是可转
*/
int a1 = 10;
// int ---- Integer
Integer i1 = new Integer(a1);
Integer i2 = Integer.valueOf(a1);
// Integer --- int
int a2 = i1.intValue();
//double --- Double
double d1 = 1.2;
Double d2 = new Double(d1);
Double d3 = Double.valueOf(d1);
//Double --- double
double d4 = d2.doubleValue();
//String --- Integer
String s1 = "123";
Integer i3 = Integer.valueOf(s1); // 要求 s1 字符串内容符合整型值的形式
//Integer -- String
String s2 = i3+"";
String s3 = i3.toString();
//String --- int
int a3 = Integer.parseInt(s3);
//int --- String
String s4 = a3+"";
//String --- Double
String s5 = "1.2";
Double d5 = Double.valueOf(s5);
//Double --- String
String s6 = d5+"";
String s7 = d5.toString();
}
}
2.包装类的应用场景
类的成员变量:如果属性为基本类型,其默认值与数组元素的默认值一致
整型 0 浮点型 0.0 布尔型 false 字符型 '/u0000'
将基本类型的属性改为对应的包装类型 默认值都是 null
//案例
package object;
public class TestStudent {
public static void main(String[] args) {
Person p1 = new Person("小王",0.0); // 参加考试了 考了0分
Person p2 = new Person("小伟"); // 没参加考试
System.out.println(p1);
System.out.println(p2);
}
}
class Person{
private String name;
private Double score;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
public Person(String name, double score) {
this.name = name;
this.score = score;
}
public Person() {
}
}
2.Integer
Integer:存在一个整数缓冲区,在整数缓冲区中预先保存了 -128 ~ 127 共 256个对象,若直接获取的对象,其数值在这个范围内,则直接从整数缓冲区中获取,反之则创建
package object;
public class TestDemo {
public static void main(String[] args) {
Integer i1 = 12;
Integer i2 = 12;
System.out.println(i1==i2); // == 比较的是地址 true --- i1 和 i2 指向同一个对象
Integer i3 = 127;
Integer i4 = 127;
System.out.println(i3==i4); // == 比较的是地址 true --- i3 和 i4 指向同一个对象
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5==i6);// == 比较的是地址 false --- i5 和 i6 指向不同的对象
Integer i7 = new Integer(12);
Integer i8 = new Integer(12);
System.out.println(i7==i8); // false 两个 12 都来源于堆空间
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJLOPfVL-1630564867841)(F:\javase\常用类\截图\整数缓冲区.jpg)]
4.String类
1.String 类 被 final 修饰,无法被继承
2.字符串串池可以节省内存空间,提高获取对象的效率
String s1 = "hello"
s1+="world";
3.字符串都是常量,创建之后不可改变 --- 不可变长的字符串
注意:
1.String s1 = "abc";//第一次使用,"abc"会在串池中构建
String s2 = "abc";//第二次使用,直接获取串池中已经存在的"abc"
s1 == s2 // true
2.String s3 = new String("abc");
|-堆空间中构建一个"abc"对象,并将地址返回
|-在串池中构建一个"abc"对象
面试题:
1.下列代码执行结束之后,一共构建了多少个对象?(3个 堆空间中有两个,串池中有一个)
String s1 = new String("abc");
String s2 = new String("abc");
2.下列代码执行结束之后,一共构建了多少个对象
String s1 = new String("abc"); (s1构建时,堆空间存放一个,串池中存放一个)
String s2 = s1.intern();
String s3 = "abc";
3.构建对象个数(JDK1.8之前)
String s1 = "abc";
s1+="def";
//案例
package object;
public class TestString {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1==s2);// == 比较两个引用中的地址是否相同 判断两个引用中保存的是否为同一个对象
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s3==s4); // false
String s5 = "abc";
String s6 = s3.intern();
System.out.println(s5==s6);// true
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LbYsK0GG-1630564867841)(F:\javase\常用类\截图\String.jpg)]
public char charAt(int index) :根据下标获取字符
public boolean contains(String str): 判断当前字符串中是否包含str
public char[] toCharArray():将字符串转换成字符数组。
public boolean equals(Object obj): 比较字符串的内容是否相同。
public int indexOf(String str): 在当前串中查找str出现的下标,如果存
在,则返回str第一个字符在str出现的下标;如果不存在,则返回-1。
public int indexOf(String str,int fromindex):同上,从指定下标开始查找。
public int lastIndexOf(String str):查找字符串在当前字符串中最后一次出现的下标索引。
public int length():返回字符串的长度。
public String trim():去掉字符串前后的空格。
public String[] split(String str):根据str做拆分。
public String toUpperCase():将小写转成大写。
public String toLowerCase():将大写转成小写。
public boolean endsWith(String str):判断字符串是否以str结尾。
String intern(): 从串池中获取和当前字符串内容相同的字符串。
5.可变长字符串
String 的不可变性:对当前字符串对象做一个副本,然后在副本基础上进行拼接,从而保证对象的不可变性。
可变长字符串
StringBuilder(JDK1.8 字符串拼接时,底层会默认使用StringBuffer)
jdk1.0 效率低 但是线程安全
StringBuffer
jdk1.5 效率高 但是线程不安全
append(String str):将当前字符串与str进行拼接
insert(int offIndex,String str):将str插入当前字符串的指定下标位置,若 str 为 null 则将 null直接拼进当前字符串
//案例
package object;
public class TestString {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("hello");
StringBuilder sb2 = sb.append("world");
System.out.println(sb2);
//insert(int offIndex,String str):将str放入当前字符串指定下标的位置
StringBuilder s1 = new StringBuilder("abcdefghijk");
// hello 放在 e 下标的位置上 abcdhelloefghijk
StringBuilder s2 = s1.insert(4, "hello");
System.out.println(s2);
}
}
chp12_集合
1.概念
数组:存取同类型元素的容器
存储----扩容(个人处理)----删除元素(需要手动移动元素,保持数组元素的连续性)---修改---查询(获取元素)
不方便:
1.频繁手动扩容(扩充容量需要手动指定,不太科学)
2.删除元素,需要手动移动其他元素(麻烦)
集合:
一种便利操作对象元素的容器,通常用来替换数组。
集合相关的类和接口都位于 java.util包中。
学习方法
1.学习集合特点
2.学习集合中的方法
3.学习集合的实现类
4.遍历方式
2.集合体系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qbCdFaq-1630564867843)(F:\javase\集合\截图\集合体系.jpg)]
3.Collection(父接口)
特点:存储任意类型的对象
方法:
add(Object obj):将obj对象存入集合
remove(Object obj):删除集合中的obj对象
clear():清空集合元素
size():获取集合中元素的个数
contains(Object obj):判断集合找那个是否包含obj对象
isEmpty():判断集合是否为空
toArray():将集合转为数组
//案例
package collection;
import java.util.ArrayList;
import java.util.Collection;
public class TestCollection {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("hello");
c.add("123");
c.add("456");
c.clear();
boolean empty = c.isEmpty();
if(empty){
System.out.println("集合为空");
}
boolean flag = c.contains("123");
if(flag){
System.out.println("包含123字符串");
}else{
System.out.println("123不存在");
}
}
}
4.List集合
特点:有序,有下标,元素可以重复
方法:
add(int index,Object obj):向集合index下标插入obj元素
remove(int index):将指定下标index 对应的元素移除
set(int index,Object obj):把集合中index下标处对应的元素改为obj
get(int index):获取指定下标index处的元素
实现类
1.ArrayList
|-特点
底层使用数组实现,查询快,增删慢 线程不安全,并发效率高 JDK1.2
面试题:
new ArrayList():创建集合,此时底层数组长度为0,第一次发起添加元素,数组的长度扩充为10,后期随着数组元素的添加,数组满了以后再次扩容,扩容的 新长度 = 原始长度/2+原始长度 10--15--22--33--49;
2.LinkedList
|-特点
底层使用链表实现,查询慢,增删快
3.Vector
|-特点
底层使用数组实现,查询快,增删慢 线程安全,并发效率低 JDK1.0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQe4gj7i-1630564867844)(F:\javase\集合\截图\数组与链表.jpg)]
5.集合的遍历方式
遍历方式
1.for循环
for(int i=0;i<集合名.size();i++){
System.out.pritln(集合名.get(i));
}
2.forEach
for(元素类型 变量名:目标集合){
System.out.println(变量名);
}
3.迭代器遍历
Iterator it = 集合名.iterator();
while(it.hasNext()){//判断指针下方单元格是否存在元素
// it.next(); 指针向下移动,获取元素
}
4.自遍历-forEach(Consumer cs);
/*
Consumer 是一个接口,内部有一个抽象方法 public abstract void accept(Object obj);
*/
集合名.forEach((obj)->System.out.println(obj));
//案例
package collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
public class TestList {
public static void main(String[] args) {
List list = new ArrayList();
list.add("徐克"); // 下标为 0
list.add("王鹏"); // 下标为 1
list.add("徐克"); // 下标为 2
list.add("周千"); // 下标为 3
list.add("王宇希");// 下标为 4
//获取王鹏
Object o = list.get(1);
System.out.println(o);
//将王鹏改为小王
list.set(1,"小王");
Object o1 = list.get(1);
System.out.println(o1);
print4(list);
}
//下标遍历(for循环遍历)
public static void print1(List list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
//foreach遍历
/*
for(元素类型 变量名:目标集合或者目标数组){
// 通过变量名获取每一个元素
}
*/
public static void print2(List list){
for(Object o:list){
System.out.println(o);
}
}
//迭代器遍历
public static void print3(List list){
//1.将集合转换为迭代器
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
//调用 forEach(Consumer c)
/*
* interface Consumer{
* void accept(Object obj);
* }
* 其中 obj 代表每一个被遍历的元素
* */
public static void print4(List list){
/*
list.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
(掌握)工作流程:
Iterator it = list.iterator();
Consumer c = 对象;
while(it.hasNext()}{
Object obj = it.next();
c.accept(obj);
}
*/
list.forEach((o)-> System.out.println(o));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jna9n1EZ-1630564867844)(F:\javase\集合\截图\迭代器.jpg)]
6.泛型
作用:Java提供的一种安全及便捷的验证机制
1.泛型集合
1.作用:约束集合中存储元素的类型,只能限定某一类型,保证集合安全提高集合使用的便捷性。
引入泛型以后,类型不兼容的对象存入集合,编译报错
2.给集合加泛型
List<Student> list1 = new ArrayList();// list1 集合中只能存储学生对象
|- JDK1.7及以后,JVM提供了自动推断
List<String> list2 = new ArrayList<String>();// list2 集合只能存储String对象
// error! 泛型不支持多态 List<Animal> list3 = new ArrayList<Dog>();
List<Animal> list4 = new ArrayList<Animal>();// list4 集合可以存储Animal类型及其子类型对象
3.使用泛型集合
遍历list1
for(Student stu:list1){
System.out.println(stu.name);
}
遍历list2
for(String s:list2){
System.out.println(s);
}
遍历list4
for(Animal a:list4){
if(a instanceof Dog){
Dog d = (Dog)a;
System.out.println(d.name); // name属性是Dog独有的
}
}
//案例
package collection;
import java.util.ArrayList;
import java.util.List;
public class TestArray {
public static void main(String[] args) {
List list1 = new ArrayList();
/*
* 问题:
* 1.存储元素时,元素的类型不可控
* 2.遍历元素时,使用集合不方便
* */
list1.add(new Student("小黑",12));
list1.add(new Student("小白",12));
list1.add("abc");
for(Object o:list1){
if(o instanceof Student){
Student stu =(Student)o;
System.out.println(stu.name);
}
}
//为集合添加泛型
List<Student> list2 = new ArrayList();
list2.add(new Student("小白",3));
list2.add(new Student("小白",3));
list2.add(new Student("小白",3));
//Error! list2.add("123");
for(Student stu:list2){
System.out.println(stu.name);
}
}
}
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}
2.泛型通配符
作用:扩宽泛型的验证机制,不再只限定于某一类型
泛型通配符 | 适用类型 | 例子 |
---|---|---|
? | 任意类型 | List<?> |
? extends A | A或者A 的子类型[A可以是父类也可以是接口] | List<? extends Animal> |
? super A | A 或者A的父类 | List<? super Comparable> |
3.泛型方法
作用:提高方法的适配度,使用更加便捷
泛型可以使用任意的大写英文字母代替,建议是 T K V ,字母等同于类型占位符,当调用该方法时,根据实参的类型决定泛型的类型。
//Collection体系遍历案例
static <T> void print1(Collection<T> c){
for(T t:c){
System.out.println(t);
}
}
public static <T> void print2(Collection<T> c){
Iterator<T> it = c.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
static <T> void print3(Collection<T> c){
c.forEach((o)->System.out.println(o));
}
//Map集合的体系遍历
//键遍历
static <K,V> void print1(Map<K,V> map){
//1.获取所有的键对象
Set<K> keys = map.keySet();
for(K key:keys){
V value = map.get(key);
System.out.println("key:"+key+" value:"+value);
}
}
//值遍历 -- 只能获取值
static <K,V> void print(Map<K,V> map){
//1.获取所有的值对象
Collection<V> values = map.values();
values.forEach((value)-> System.out.println(value));
}
//键值对遍历
static <K,V> void print3(Map<K,V> map){
//1.获取所有的键值对对象
Set<Map.Entry<K,V>> entries = map.entrySet();
for(Map.Entry<K,V> entry:entries){
K key = entry.getKey();
V value = entry.getValue();
System.out.println("key:"+key+" value:"+value);
}
}
//forEach(BiConsumer bi)
static <K,V> void print4(Map<K,V> map){
/* map.forEach(new BiConsumer<K, V>() {
@Override
public void accept(K key, V value) {
System.out.println("key:"+key+" value:"+value);
}
});*/
map.forEach((key,value)->System.out.println("key:"+key+" value:"+value));
}
使用
<T> 放置在返回值类型之前,修饰符之后
<T extends A> 允许A或者A的子类型 A可以是接口也可以是父类
<T extends A & B> 该类型是A的子类型,且实现了B接口
4.泛型类与泛型接口
class 类名<T>{
//用于获取T类型的对象
public T getBean(形参列表){
}
}
interface 接口名<T>{
}
5.泛型擦除
概念:在JVM中并不存在泛型,泛型只是为了完善Java体系,增加程序员编程的便捷性与安全性的一种机制。在JVM中所用的泛型都有其具体的类型,JVM会把所有的泛型参数全部擦除,用确定的类型代替,擦除过程中使用的替代类型,称为原始类型,一般使用第一个原始类型来替换,若没有类型替换,默认使用Object替换
7.Set集合
1.特点
无序,无下标,元素不能重复(元素内容不能重复)
2.方法
方法继承于父接口Collection
3.实现类
HashSet
LinkedHashSet
SortedSet( interface )
TreeSet
4.支持的遍历方式
forEach方式
迭代器遍历
forEach(Consumer<T> c);
//案例
package collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class TestSet {
public static void main(String[] args) {
Set<Worker> set = new HashSet<Worker>();
set.add(new Worker("小王",40));
set.add(new Worker("小曹",23));
set.add(new Worker("徐克",30));
//foreach方式
for(Worker w:set){
System.out.println(w);
}
//迭代器遍历
Iterator<Worker> iterator = set.iterator();
while(iterator.hasNext()){
Worker w = iterator.next();
System.out.println(w);
}
//forEach(Consumer c);
set.forEach((w)->System.out.println(w));
}
}
class Worker{
private String name;
private int age;
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Worker worker = (Worker) o;
if (age != worker.age) return false;
return name.equals(worker.name);
}
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Worker(String name, int age) {
this.name = name;
this.age = age;
}
public Worker() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.HashSet的底层结构
1.特点:
无序,无下标,元素内容不能重复
2.如何保证元素不重复
通过哈希表(散列表)存储时触发的哈希算法。
3.要求
元素所属的类型必须覆盖 equals 和 hashCode 方法
hashCode方法覆盖时
1.保证相同的元素返回相同的哈希码值
2.尽可能保证不同的元素返回不同的哈希码值
4.面试题
HashSet的底层实现?
new HashSet().创建长度为16的哈希表{(散列表:外层是数组,数组单元格内部是链表)[数组+链表+红黑树JDK1.8]},首先获取元素的哈希码值,然后让哈希码值对16取模,模值为元素在哈希表中存储的数组下标.
|-若两个对象的哈希码对16取模,模值相等,则会触发equals方法,若equals返回false,则两个对象在同一个数组单元格中链式存储[当链表的长度超过8,会转为红黑树存储]若equals方法返回值为true,则第二个元素添加失败.
|-若两个对象的哈希码对16取模,模值不等,则直接添加。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyUphgw3-1630564867845)(F:\javase\集合\截图\HashSet的底层实现.jpg)]
2.其他实现类
LinkedHashSet
特点:
|-底层使用哈希表+链表实现
|-可以预知遍历的元素顺序
Set----SortedSet----TreeSet(实现类)
|-集合元素需要实现Comparable接口,覆盖compareTo方法
|-通过排序实现去重
|-遍历的顺序由排序规则决定
8.Map集合
map: 元素 键值对形式存储 key-value key:不可重复,没有顺序 value:可重复
hello --- 你好 hi --- 你好
常见方法
put(Object key,Object value):将key-value添加到map集合中,如果key已经存在了,那么新的value会覆 盖掉旧的value.
例如:
map.put(key1,value1);
map.put(key1,value2);---------- 保存在map集合中的元素为 key1-value2
get(Object key):根据key去找value
remove(Object key):删除key对应的 key-value 键值对
containsKey(Object key):判断map集合中是否存在key
containsValue(Object value):判断map集合中是否存在value
1.实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbKCBQ2e-1630564867846)(F:\javase\集合\截图\map.jpg)]
底层使用哈希算法去重的实现类,要求键对象必须覆盖equals 和 hashCode 方法。
HashMap
特点:
1.键值对存储,键不可以重复,值可以重复 可以使用 null 作为键和值
2.底层使用哈希表+链表 | 哈希表+红黑树
3.JDK1.2版本产物,并发效率比较高,线程不安全
Hashtable
特点:
1.键值对存储,键不可重复,值可以重复 不可以使用 null 作为键和值
2.JDK1.0 线程安全,但是并发效率低
LinkedHashMap
特点:
1.可以记录元素存入集合的顺序
2.底层使用哈希表+链表
SortedMap---TreeMap
特点:
1.按照键对象进行排序,去重
2.键对象所属的类型必须实现Comparable接口,覆盖compareTo方法
Properties
特点:
键和值都是String类型 --- 经常用于读取配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmmDyfiy-1630564867847)(F:\javase\集合\截图\hashmap.jpg)]
2.遍历方式
1.键遍历
Set<Key> set = map.keySet();
for(Key key:set){
System.out.println("key:"+key+" value:"+map.get(key));
}
2.值遍历
Collection<Value> values = map.values();
values.forEach((value)->System.out.println(value));
3.键值对遍历
Set<Map.Entry<Key,Value>> entries = map.entrySet();
for(Map.Entry<Key,Value> entry:entries){
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
4.forEach(BiConsumer<Key,Value> bi)
map.forEach((key,value)->System.out.println("key:"+key+" value:"+value);)
//案例
package map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class TestMap {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(2020,null);
map.put(2016,"里约热内卢");
map.put(2012,"伦敦");
map.put(2008,"北京");
map.put(2004,"雅典");
map.put(2000,"悉尼");
map.put(1996,"亚特兰大");
map.put(1992,"巴塞罗那");
map.put(1988,"首尔");
map.put(1984,"洛杉矶");
map.put(1980,"莫斯科");
map.put(1976,"蒙特利尔");
map.put(1972,"慕尼黑");
map.put(1968,"墨西哥");
map.put(1964,"东京");
map.put(1960,"罗马");
map.put(1956,"墨尔本");
map.put(1952,"赫尔辛基");
map.put(1948,"伦敦");
map.put(1944,"伦敦");
map.put(1940,"赫尔辛基");
/* String value = map.get(2004);
System.out.println(value);*/
print4(map);
}
//键遍历
static void print1(Map<Integer,String> map){
//1.获取所有的键对象
Set<Integer> keys = map.keySet();
for(Integer key:keys){
String value = map.get(key);
System.out.println("key:"+key+" value:"+value);
}
}
//值遍历 -- 只能获取值
static void print(Map<Integer,String> map){
//1.获取所有的值对象
Collection<String> values = map.values();
values.forEach((value)-> System.out.println(value));
}
//键值对遍历
static void print3(Map<Integer,String> map){
//1.获取所有的键值对对象
Set<Map.Entry<Integer,String>> entries = map.entrySet();
for(Map.Entry<Integer,String> entry:entries){
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println("key:"+key+" value:"+value);
}
}
//forEach(BiConsumer bi)
static void print4(Map<Integer,String> map){
/* map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer key, String value) {
System.out.println("key:"+key+" value:"+value);
}
});*/
map.forEach((key,value)->System.out.println("key:"+key+" value:"+value));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaQLFPUR-1630564867848)(F:\javase\集合\截图\遍历方式.jpg)]
9.Collections工具类
倒序: Collections.reverse(list);
随机:Collections.shuffle(list);
排序:Collections.sort(list);
|-排序时,要求集合元素所属的类型必须实现Comparable接口覆盖compareTo方法
//案例
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
public static void main(String[] args) {
List<Employee> c = new ArrayList<>();
c.add(new Employee(1,"小黑"));
c.add(new Employee(2,"小花"));
c.add(new Employee(8,"小白"));
c.add(new Employee(4,"小黄"));
c.add(new Employee(5,"小王"));
c.add(new Employee(6,"小杨"));
// 反转 Collections.reverse(c);
//随机[打乱顺序] Collections.shuffle(c);
Collections.sort(c);
c.forEach((o)-> System.out.println(o));
}
}
class Employee implements Comparable<Employee>{
private int age;
private String name;
@Override
public String toString() {
return "Employee{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Employee(int age, String name) {
this.age = age;
this.name = name;
}
public Employee() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Employee o) {
if(this.age>o.age){
return 1;
}else if(this.age<o.age){
return -1;
}else{
if(this.name.compareTo(o.name)>0){
return 1;
}else if(this.name.compareTo(o.name)<0){
return -1;
}else{
return 0;
}
}
}
}
排序
方法: 集合名.sort(Comparator c);
使用外置的比较器,需要书写一个Comparator接口的实现类,类内部覆盖compare方法
public <T> int compare(T t1,T t2){
// 比较 t1 和 t2
}
list.sort((t1,t2)->{
//比较过程
});
//案例
package collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
public static void main(String[] args) {
List<Employee> c = new ArrayList<>();
c.add(new Employee(1,"小黑"));
c.add(new Employee(2,"小花"));
c.add(new Employee(8,"小白"));
c.add(new Employee(4,"小黄"));
c.add(new Employee(5,"小王"));
c.add(new Employee(6,"小杨"));
// 反转 Collections.reverse(c);
//随机[打乱顺序] Collections.shuffle(c);
c.sort((t1,t2)->{ // list.sort(Comparator c):使用比较灵活,排序规则可以在使用时去指定
if(t1.getAge()>t2.getAge()){
return 1;
}else if(t1.getAge()<t2.getAge()){
return -1;
}else{
if(t1.getName().compareTo(t2.getName())>0){
return 1;
}else if(t1.getName().compareTo(t2.getName())<0){
return -1;
}else{
return 0;
}
}
});
c.forEach((o)-> System.out.println(o));
}
}
class Employee{
private int age;
private String name;
@Override
public String toString() {
return "Employee{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Employee(int age, String name) {
this.age = age;
this.name = name;
}
public Employee() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
chp13_异常
1.异常的概念
程序运行过程中出现的非正常情况。
取钱
插卡
输入密码
输入取款金额 --取2k
余额-=2000
断电|网络中断....... 有可能出现
余额+=2000 ----- 当取款失败时的补救措施(取款失败时,触发执行)
ATM吐钱
拿钱
取卡
2.异常的分类
异常的分类
Throwable 所有异常的直接父类或者间接父类
|-Error 错误 严重错误,不可避免,不可处理
|-Exception 异常 可以处理
|-RuntimeException的子类 运行时异常 未检查异常(编译时不会出现) 可以避免,可以处理
|-非RuntimeException的子类 已检查异常 编译时异常 不可避免,必须处理
|—自定义异常类 自定义的异常类继承对应的异常类,需要填充无参和有参(String str)两个构造函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyZYyFkV-1630564867849)(F:\javase\异常\截图\异常.jpg)]
3.异常的产生
1.逻辑业务异常
由程序运行时产生的异常对象,最终传递给虚拟机,将异常信息在控制台上展示。
2.手动抛出异常
throw 异常对象 [其后不允许书写代码,异常出现以后,程序会直接返回,等价于 return]
4.异常的传递过程
沿着方法的调用链,反向传递
//案例
package exception;
import java.util.Scanner;
public class TestException {
public static void main(String[] args) {
System.out.println("main start");
Scanner s = new Scanner(System.in);
System.out.println("请输入 1 2 3...");
int num = s.nextInt();
m1(num);
System.out.println("main end.....");
}
public static void m1(int num){
System.out.println("m1 start.....");
m2(num);
System.out.println("m1 end.....");
}
public static void m2(int num){
System.out.println("m2 start...");
m3(num);
System.out.println("m2 end...");
}
public static void m3(int num){
System.out.println("m3 start...");
switch(num){
case 1:throw new ArrayIndexOutOfBoundsException();
case 2:throw new ClassCastException();
case 3:throw new NullPointerException();
default:
System.out.println("程序正常运行");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdxEeS8Q-1630564867849)(F:\javase\异常\截图\异常的传递.jpg)]
5.异常处理
1.消极方式
声明并抛出异常 throws 异常 ---- 书写在函数的声明部分
修饰符 返回值类型 函数名(形式参数列表) 抛出的异常{
//函数实现
}
异常并未得到处理,并没有得到解决.---- 若异常始终未得到解决,抛给虚拟机之后,程序依然会因为异常而终止。
2.积极方式
try-catch
try{
//可能产生异常的代码
}catch(异常类 引用){ // 引用用于捕捉try{}产生异常对象
//异常对象的处理
}
catch会将产生的异常对象保存在引用中 捕捉到的异常类型可以是本类型也可以是其子类型
catch中常用的异常方法------来自于 Throwable
printStackTrace(): 打印异常的栈追踪信息
getMessage():获取异常对象产生时其构造函数传递的数据
常用结构
try{}catch(){} [重点]
try{}catch(){}catch(){}catch(){}
try{}catch(){}finally{}[重点]
try{}catch(){}catch(){}catch(){}finally{}
try{}finally{}[重点]
try{ try{}catch(){}finally }catch(){}
try{}catch{ try{}catch(){}finally{} }
try{}catch(){}finally{ try{}catch(){}finally{} }
try必须搭配catch 或者 搭配finally使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrEEni8O-1630564867850)(F:\javase\异常\截图\异常处理.jpg)]
package exception;
import java.util.Scanner;
public class TestException {
//省长
public static void main(String[] args) {
System.out.println("main start");
Scanner s = new Scanner(System.in);
System.out.println("请输入 1 2 3...");
int num = s.nextInt();
try{
m1(num);
}catch(Exception e){
System.out.println("空指针或者类型转换异常被处理[海啸或者地震被处理]");
}
System.out.println("main end.....");
}
//市长
public static void m1(int num)throws Exception{
System.out.println("m1 start.....");
m2(num);
System.out.println("m1 end.....");
}
//区长
public static void m2(int num)throws ClassCastException,NullPointerException{
System.out.println("m2 start.....");
m3(num);
System.out.println("m2 end.....");
}
//乡长
public static void m3(int num)throws ClassCastException,NullPointerException{
System.out.println("m3 start.....");
m5(num); // 可能会接收到 地震|海啸 异常
System.out.println("m3 end.....");
}
//村长
public static void m5(int num)throws ClassCastException,NullPointerException{
System.out.println("m5 start...");
try{ // try{} 语句中只要出现异常,直接进入 catch
switch(num){
case 1:throw new ArrayIndexOutOfBoundsException(); // 两口子吵架
case 2:throw new ClassCastException();//地震
case 3:throw new NullPointerException();//海啸
default:
System.out.println("程序正常运行");
}
System.out.println("try后续代码");
}catch(ArrayIndexOutOfBoundsException ex){
System.out.println("下标越界异常被处理[两口子吵架打死......]");
}
System.out.println("m5 end....");
}
}
//案例
package exception;
import java.util.Scanner;
public class TestException {
//省长
public static void main(String[] args) {
System.out.println("main start");
Scanner s = new Scanner(System.in);
System.out.println("请输入 1 2 3...");
int num = s.nextInt();
try{
m1(num);
}catch(Exception e){
System.out.println("空指针或者类型转换异常被处理[海啸或者地震被处理]");
}
System.out.println("main end.....");
}
//市长
public static void m1(int num)throws Exception{
System.out.println("m1 start.....");
m2(num);
System.out.println("m1 end.....");
}
//区长
public static void m2(int num)throws ClassCastException,NullPointerException{
System.out.println("m2 start.....");
m3(num);
System.out.println("m2 end.....");
}
//乡长
public static void m3(int num)throws ClassCastException,NullPointerException{
System.out.println("m3 start.....");
m5(num); // 可能会接收到 地震|海啸 异常
System.out.println("m3 end.....");
}
//村长
public static void m5(int num){
System.out.println("m5 start...");
try{ // try{} 语句中只要出现异常,直接进入 catch
switch(num){
case 1:throw new ArrayIndexOutOfBoundsException(); // 两口子吵架
case 2:throw new ClassCastException();//地震
case 3:throw new NullPointerException();//海啸
default:
System.out.println("程序正常运行");
}
System.out.println("try后续代码");
}catch(ArrayIndexOutOfBoundsException ex){
System.out.println("下标越界异常被处理[两口子吵架打死......]");
}catch(NullPointerException e){
System.out.println("空指针异常被处理....");
}catch(Exception e){
/*
* 若罗列多个catch 进行异常捕捉
* 子类型异常放上面 父类型异常放下面
*/
}
System.out.println("m5 end....");
}
}
6.finally
王鹏开店
开门
营业
码货
-- 生病了
打扫卫生
中午进货
下午营业
晚上关门 --- 无论异常产生与否,都必须执行
finally{
//无论异常与否都会得到执行
}
try-catch-finally try-finally
//案例
package exception;
public class TestFinally {
public static void main(String[] args) {
System.out.println(m1(3));// 40
System.out.println(m1(4));// 40
System.out.println(m2(3));// 30
System.out.println(m2(4));// 20
}
static int m2(int num){
int a = 10;
try{
if(num%2==0){
a = 20;
return a;
}else{
throw new NullPointerException();
}
}catch(Exception e){
a = 30;
return a;
}finally{
a = 40;
}
}
static int m1(int num){
int a = 10;
try{
if(num%2==0){
a = 20;
return a;
}else{
throw new NullPointerException();
}
}catch(Exception e){
a = 30;
return a;
}finally{
a = 40;
return a;
}
}
}
finally 中的 return 语句总是会被执行,所以无论num的值是多少, m1() 返回的都是 40
所以在编码中,一般 finally 中不放置 return
finally 主要的作用:一般用于关闭资源。
7.自定义异常类
1.书写自定义的异常类,继承JDK中对应的异常类
2.自定义异常类中填充两个构造函数
无参构造
带有一个String类型参数的有参构造,函数体中填充 super()
例如:
class MyException extends Exception{
public MyException(){}
public MyException(String str){
super(str);
}
}
//案例
package exception;
import java.util.Scanner;
public class TestMyException {
public static void main(String[] args) {
//校验用户录入密码的长度 长度>=6 否则视为非法密码【产生异常】
System.out.println("请输入不低于6位的密码");
Scanner s = new Scanner(System.in);
String pwd = s.next();
int length = pwd.length();
if(length>=6){
System.out.println("密码合法");
}else{
try{
throw new MyException("密码长度不合规,异常产生,请处理");
}catch(MyException e){
String message = e.getMessage();
System.out.println(message);
}
}
}
}
//自定义异常类
class MyException extends Exception{
public MyException(){}
public MyException(String message){
super(message);
}
}
8.方法覆盖的语法要求
访问修饰符 子类方法的访问修饰符与父类方法的访问修饰符相同或更宽
返回值类型 子类方法的返回值类型与父类方法的返回值类型相同或者是其子类型
函数名 相同
形参列表 相同
抛出的异常 子类不能抛出比父类方法更多或者更大的异常
//案例
class Super{
public void m1()throws MyException,MyBZException{}
}
class Sub extends Super{
//OK! public void m1(){}
//OK! public void m1() throws MyException,MyBZException{}
//error! public void m1()throws NullPointException{}
}
//自定义异常类
class MyException extends NullPointerException{
public MyException(){}
public MyException(String message){
super(message);
}
}
class MyBZException extends ClassCastException{
public MyBZException(){}
public MyBZException(String message){
super(message);
}
}
chp14_IO流
思考:在之前的程序中,处理的数据以及结果保存在哪?
|-变量,数组,集合,对象---都保存在内存中(堆空间,栈空间)---处理效率高,但是无法永久化保存
可以通过IO技术,将数据保存在硬盘上,完成永久化存储。
1.简介
I/O Input(输入)/Output(输出)
流:是一种对象,可以实现数据的传输(完成内存与硬盘之间的数据传输),类似于管道|水管
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVpoK5FP-1630564867851)(F:\javase\IO\截图\简介.jpg)]
2.流的分类
1.按照数据的传输方向
输入流:将数据由硬盘传输到内存(JVM)
输出流:将数据由内存(JVM)传输到硬盘
2.按照数据的传输单位
字节流:以字节为单位进行数据传输 ----- 可以传输任意类型的文件(文本|视频|音频...)
字符流:以字符为单位进行数据传输 ----- 可以传输文本文件(使用文本编辑器打开,人还能看得懂)
3.按照流的功能
节点流:负责数据的读写
过滤流:无法独立完成数据的读写,需要借助节点流,主要作用是增强节点流的功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcJG2rNb-1630564867852)(F:\javase\IO\截图\流的分类.jpg)]
3.字节流
1.字节流的父类
InputStream 字节输入流 抽象类
OutputStream 字节输出流 抽象类
2.常用的子类:
FileInputStream/FileOutputStream
3.路径说明
盘符:/文件夹/文件名 -- 文件夹必须事先创建好,文件若不存在,会尝试新建 F:/abc/a.txt
盘符:/文件名 -- 文件若不存在,尝试新建 F:/a.txt
文件名 -- 保存在当前项目根目录下
4.FileOutoutStream
OutputStream out = new FileOutputStream("F:/a.txt");
OutputStream out = new FileOutputStream("F:/a.txt",false);
在F:/a.txt文件中写入内容,原文件内容会被覆盖
OutputStream out = new FileOutputStream("F:/a.txt",true);
在F:/a.txt文件中下入内容,原文件内容不会被覆盖,新内容在原内容其后去追加
方法:
write(int b):写出一个字节 (ASCII表)
write(byte[] bs):一次性往外写出一个数组的字节
write(byte[] bs,int startIndex,int length):一次性往外写出bs数组中的多个 由 startIndex 下标开始,写出length个
//案例
package io;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestOutputStream {
public static void main(String[] args) throws Exception {
//1.构建字节节点输出流
OutputStream out = new FileOutputStream("F:/a.txt",true);
//2.写操作
out.write(67);
byte[] bs = {'A','B','C','D','E'};
out.write(bs,3,2);
//3.关闭资源
out.close();
}
}
//案例
package io;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestOutputStream {
public static void main(String[] args) {
OutputStream out = null;
try{
out = new FileOutputStream("F:/a.txt");
out.write(65);
}catch(Exception e){
e.printStackTrace();
}finally{
if(out!=null){
try{
out.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
FileInputStream
字节节点输入流 硬盘------内存
创建语法
InputStream in = new FileInputStream("F:/d.txt"); //文件必须存在,不会自动创建
常用方法
int read():每次读取一个字节的数据,返回值代表读到的内容 若到达文件末尾,返回-1
//案例
package io;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestFileInputStream {
public static void main(String[] args) throws Exception{
//1.构建字节节点输入流
InputStream in = new FileInputStream("F:/a.txt");
//2.获取指定文件中所有的数据
while(true){
int read = in.read();
if(read==-1)break;
System.out.println((char)read);
}
//3.关闭资源
in.close();
}
}
===============================
int read(byte[] bs):尝试读满bs数组,返回值代表读到的字节个数 当返回值为 -1时,代表到达文件末尾
//案例
package io;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestFileInputStream {
public static void main(String[] args) throws Exception{
//1.构建字节节点输入流
InputStream in = new FileInputStream("F:/a.txt");
//2.获取指定文件中所有的数据
byte[] bs = new byte[5];
while(true){
int num = in.read(bs); //num 代表每次读到的字节个数 <=5
if(num==-1)break;
for(int i=0;i<num;i++){
System.out.print((char)bs[i]+" ");
}
System.out.println();
}
//3.关闭资源
in.close();
}
}
int read(byte[] bs,int startIndex,int length):尝试读满bs数组中的一段 当返回值为 -1 时,代表读到文件末尾
关闭资源的标准写法
JDK1.7 try-catch-resources 实现自动关流
语法:
try(实现了AutoCloseable接口的对象1;实现了AutoCloseable接口的对象2){
}catch(Exception e){
}
try-catch中的代码执行完,资源会自动关闭
//案例
package io;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestFileInputStream {
public static void main(String[] args){
/* InputStream in = null;
try{
in = new FileInputStream("F:/a.txt");
byte[] bs = new byte[5];
while(true){
int num = in.read();
if(num==-1)break;
for(int i=0;i<num;i++){
System.out.print((char)bs[i]+" ");
}
System.out.println();
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(Exception e){
e.printStackTrace();
}
}
}*/
try(InputStream in = new FileInputStream("F:/a.txt")){
byte[] bs = new byte[5];
while(true){
int num = in.read(bs);
if(num==-1)break;
for(int i=0;i<num;i++){
System.out.print((char)bs[i]+" ");
}
System.out.println();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
BufferedInputStream/BufferedOutputStream
字节过滤输入流/字节过滤输出流
缓冲流:提高IO流的传输效率,减少内存与硬盘的交互次数
编码步骤:
1.创建字节节点流
2.包装过滤流
3.读写操作
4.关闭资源
//案例
package io;
import java.io.*;
public class TestBuffered {
public static void main(String[] args)throws Exception {
//1.创建字节节点输入流
InputStream in = new FileInputStream("F:/a.txt");
//2.包装过滤流
BufferedInputStream bis = new BufferedInputStream(in);
//3.读操作
int read = bis.read();
//4.关闭资源
bis.close();
//1.创建字节节点输出流
OutputStream out = new FileOutputStream("F:/a.txt");
//2.包装过滤流
BufferedOutputStream bos = new BufferedOutputStream(out);
//3.写操作
bos.write(65);
//4.关闭资源
bos.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R5B9igCg-1630564867852)(F:\javase\IO\截图\缓冲流.jpg)]
文件拷贝
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ip5y0Jt-1630564867853)(F:\javase\IO\截图\文件拷贝.jpg)]
package io;
import java.io.*;
public class TestFileCopy {
public static void main(String[] args) {
fileCopy4("F:/javase/集合/视频/录像2.wmv","E:/小安.wmv");
}
//文件拷贝
static void fileCopy1(String srcFile,String descFile){
/*//1.构建字节节点输入|输出流
InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(descFile);
//2.读写操作(边读边写)
while(true){
int read = in.read();
if(read==-1)break;
out.write(read);
}
//3.关闭资源
out.close();
in.close();*/
long start = System.nanoTime();
try(InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(descFile)){
while(true){
int read = in.read();
if(read==-1)break;
out.write(read);
}
}catch(Exception e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println((end-start)/1E9+"秒");
}
static void fileCopy2(String srcFile,String descFile){
long start = System.nanoTime();
try(InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(descFile)){
byte[] bs = new byte[1024];
while(true){
int num = in.read(bs);
if(num==-1)break;
out.write(bs,0,num);
}
}catch(Exception e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println((end-start)/1E9+"秒");
}
static void fileCopy3(String srcFile,String descFile){
long start = System.nanoTime();
try(InputStream in = new FileInputStream(srcFile);
BufferedInputStream bis = new BufferedInputStream(in);
OutputStream out = new FileOutputStream(descFile);
BufferedOutputStream bos = new BufferedOutputStream(out);){
while(true){
int read = bis.read();
if(read==-1)break;
bos.write(read);
}
}catch(Exception e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println((end-start)/1E9+"秒");
}
static void fileCopy4(String srcFile,String descFile){
long start = System.nanoTime();
try(InputStream in = new FileInputStream(srcFile);
BufferedInputStream bis = new BufferedInputStream(in);
OutputStream out = new FileOutputStream(descFile);
BufferedOutputStream bos = new BufferedOutputStream(out);){
byte[] bs = new byte[1024*1024];
while(true){
int num = bis.read(bs);
if(num==-1)break;
bos.write(bs,0,num);
}
}catch(Exception e){
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println((end-start)/1E9+"秒");
}
}
DataInputStream/DataOutputStream
字节过滤输入流 / 字节过滤输出流
过滤流:读写8种基本数据类型和String
常用的方法:
writeByte(int n)/readByte()
writeShort(int n)/readShort()
writeInt(int n)/readInt()
writeLong(long n)/readLong()
writeFloat(float n)/readFloat()
writeDouble(double n)/readDouble()
可以操作String writeUTF(String str)/readUTF();
//案例
package io;
import java.io.*;
public class TestData {
public static void main(String[] args) {
/*try(OutputStream out = new FileOutputStream("F:/a.txt");
DataOutputStream dos = new DataOutputStream(out)){ // 字节过滤输出流
dos.writeInt(65);
}catch(Exception e){
e.printStackTrace();
}*/
try(InputStream in = new FileInputStream("F:/a.txt");
DataInputStream dis = new DataInputStream(in)){
int i = dis.readInt();
System.out.println(i);
}catch(Exception e){
e.printStackTrace();
}
}
}
ObjectInputStreanm/ObjectOutputStream --- 过滤流:读写对象
1.对象序列化:将对象放在流上传输
2.只有实现了Serializable接口,支持序列化
3.若传输的对象具有 对象类型的属性,则要求该属性所属的类型也是可序列化的,若该属性为集合或者数组,要求集合元素或者数组元素是可序列化的
4.被transient修饰的属性,变为临时属性,对应的对象属性值不参与序列化
//案例
package io;
import java.io.*;
public class TestObject {
public static void main(String[] args) {
/* Student stu1 = new Student("小王", 50, 59.0); 对象持有的是属性值 类持有属性
Student stu2 = new Student("小鹏", 20, 79.0);
Student stu3 = new Student("小安", 50, 89.0);
try(OutputStream out = new FileOutputStream("F:/stu.dat");
ObjectOutputStream oos = new ObjectOutputStream(out);){
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.writeObject(stu3);
}catch(Exception e){
e.printStackTrace();
}*/
try(InputStream is = new FileInputStream("F:/stu.dat");
ObjectInputStream ois = new ObjectInputStream(is)){
while(true) {
try{
Object o = ois.readObject(); // 读到文件末尾,会出异常
System.out.println(o);
}catch(Exception e){
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class Student implements Serializable { // 标记接口
String name;
transient int age; // 该属性就会变成临时属性,不参与序列化
double score;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
}
//案例
package io;
import java.io.*;
public class TestObject {
public static void main(String[] args) {
/*Student stu1 = new Student("小王", 50, 59.0);
Student stu2 = new Student("小鹏", 20, 79.0);
Student stu3 = new Student("小安", 50, 89.0);
try(OutputStream out = new FileOutputStream("F:/stu.dat");
ObjectOutputStream oos = new ObjectOutputStream(out);){
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.writeObject(stu3);
}catch(Exception e){
e.printStackTrace();
}*/
try(InputStream is = new FileInputStream("F:/stu.dat");
ObjectInputStream ois = new ObjectInputStream(is)){
while(true) {
try{
Object o = ois.readObject(); // 读到文件末尾,会出异常
System.out.println(o);
}catch(Exception e){
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class Student implements Serializable { // 标记接口
String name;
int age;
double score;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
}
1.System.out.println("HelloWorld");
System 被 final 修饰,不能被继承
out 静态属性 类型为 PrintStream:可以读写多种类型的数据 与控制台的交互
2.Scanner s = new Scanner(System.in);
in 是 System 的静态属性 类型为 InputStream
4.字符流
以字符为单位进行传输
Reader/Writer 一组抽象类 是字符流的父类
FileReader/FileWriter 字符节点输入流 字符节点输出流
write(int n):往外写出一个字符
//案例
package io;
import java.io.FileWriter;
import java.io.Writer;
public class TestWriter {
public static void main(String[] args) {
// 向文件中输出一个 中
try(Writer out = new FileWriter("F:/字符流.txt")){
out.write('中');
}catch(Exception e){
e.printStackTrace();
}
}
}
write(String str):往外写出一个字符串
//案例
package io;
import java.io.FileWriter;
import java.io.Writer;
public class TestWriter {
public static void main(String[] args) {
// 向文件中输出一个 中
try(Writer out = new FileWriter("F:/字符流.txt")){
out.write("aaa");
out.write("aaa");
out.write("小王");
}catch(Exception e){
e.printStackTrace();
}
}
}
int read():返回值代表读到的字符内容 读到文件末尾,返回 -1
//案例
package io;
import java.io.FileReader;
import java.io.Reader;
public class TestReader {
public static void main(String[] args) {
try(Reader in = new FileReader("F:/字符流.txt")){
while(true){
int read = in.read();
if(read==-1)break;
System.out.println((char)read);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
BufferedReader 字符过滤输入流
String readLine():每次读一行 当返回null时,代表到达文件末尾
PrintWriter|BufferedWriter 字符过滤输出流
println():每次往外写出一行,然后换行 --- printWriter
write(String str):每次往外写出一个字符串,不会换行 --- BufferedWriter
//案例
package io;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;
public class TestPrintWriter {
public static void main(String[] args) {
/*
* 1.创建字符节点输出流
* 2.包装字符过滤输出流
* 3.写操作
* */
try(Writer out = new FileWriter("F:/字符流.txt");
PrintWriter pw = new PrintWriter(out)){
pw.println("羌笛何须怨杨柳");
pw.println("春风不度玉门关");
pw.println("鹅鹅鹅");
pw.println("曲项向天歌");
}catch(Exception e){
e.printStackTrace();
}
}
}
package io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Reader;
public class TestBufferedReader {
public static void main(String[] args) {
try(Reader in = new FileReader("F:/字符流.txt");
BufferedReader br = new BufferedReader(in)){
while(true){
String s = br.readLine();
if(s==null)break;
System.out.println(s);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
1.乱码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcXGMvPD-1630564867854)(F:\javase\IO\截图\乱码.jpg)]
2.桥转换流
桥转换流:将字节流转换为字符流,同时可以设置编解码格式
编码流程:
1.创建字节流
2.将字节流转换为字符流,同时设置编解码格式-------创建桥转换流 [字符节点流]
3.包装过滤流
4.读写操作
5.关闭资源
package io;
import java.io.*;
public class TestOutput {
public static void main(String[] args){
/* //1.创建字节节点流
OutputStream os = new FileOutputStream("F:/字符流.txt");
//2.创建桥转换流【将字节流转换为字符流,同时制定编解码格式】
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");// 字符节点流
//3.包装过滤流
PrintWriter pw = new PrintWriter(osw);
//4.写操作
pw.println("羌笛何须怨杨柳");
pw.println("春风不度玉门关");
pw.println("鹅鹅鹅");
//5.关闭资源
pw.close();*/
try(OutputStream os = new FileOutputStream("F:/字符流.txt");
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
PrintWriter pw = new PrintWriter(osw);){
pw.println("羌笛何须怨杨柳");
pw.println("春风不度玉门关");
pw.println("鹅鹅鹅");
}catch(Exception e){
e.printStackTrace();
}
try(InputStream is = new FileInputStream("F:/字符流.txt");
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
BufferedReader br = new BufferedReader(isr);){
while(true){
String s = br.readLine();
if(s==null)break;
System.out.println(s);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
5.File
IO流:用于操作文件内容
File:用于操作文件或者文件夹
创建语法:
File file = new File("文件路径"); // F:/a.txt F:/abc/def
常见的方法
public boolean createNewFile():
创建文件,创建成功返回true,如果文件存在返回值为false。
public boolean mkdir():
创建文件夹,创建成功返回true,如果文件存在返回值为false。
public boolean delete():
删除文件夹/文件,删除成功返回true,否则为false。
当删除文件夹时,只有当文件夹为空时,才可以删除;
而删除文件时,不管文件内容是否为空,都会被删除。
public String getName():
获取文件名(含扩展名)、文件夹名。
public boolean exists():
判断一个文件或是目录是否存在,存在返回true,否则为false。
public String getAbsolutePath ():
获得绝对路径。
public File[] listFiles():
获取当前文件夹下所有的文件、文件夹。
public boolean isFile():
判断File对象所对应的是否为文件,而不是目录。
public boolean isDirectory():
判断File对象所对应的是否为目录,而不是文件
//案例
package file;
import java.io.File;
public class TestFile {
public static void main(String[] args) throws Exception{
//1.在F盘新建一个文件夹
File file = new File("F:/abc");
System.out.println(file.getName());
/*
boolean flag = file.mkdir();
if(flag){
System.out.println("新建成功");
}else{
System.out.println("新建失败");
}*/
//在 F:/abc 新建 hello.txt 新建文件
File file1 = new File("F:/abc/hello.txt");
/* boolean flag1 = file1.createNewFile();
if(flag1){
System.out.println("新建成功");
}else{
System.out.println("新建失败");
}*/
String name = file1.getName();
System.out.println(name);
}
}
//删除指定的文件夹
package file;
import java.io.File;
public class TestDelete {
public static void main(String[] args) {
File f = new File("F:/");
deleteFile(f);
}
//删除指定的文件夹
static void deleteFile(File f){
File[] files = f.listFiles(); //包含当前文件夹下所有的 文件夹|文件
//1.清空 f 文件夹下所有的 [文件和文件夹]
for(File file:files){
//判断遍历到的file对象是否为文件
boolean flag = file.isFile();
if(flag){
file.delete();
}
//判断遍历到的file对象是否为文件夹
boolean flag1 = file.isDirectory();
if(flag1){
deleteFile(file);
}
}
//2.删除f文件夹
f.delete();
}
}
//列出指定文件夹下所有的 java文件
//检索文件 -- 列出指定文件夹下所有的 java文件
static void listJavaFile(File file){
File[] files = file.listFiles();
for(File f:files){
if(f.isFile()){
String name = f.getName();
if(name.endsWith(".java")){
System.out.println(f.getAbsolutePath());
}
}
if(f.isDirectory()){
listJavaFile(f);
}
}
}
chp15_线程
1.进程与线程的概念
进程:是系统进行资源分配和调度的基本单位,同一时间,真正运行的程序称之为进程。 JVM-----一条进程(运行的软件)
进程下可以运行多条线程,同一时间点,只有一条线程再运行 宏观并行,微观串行
线程:一个程序的顺序执行流程
例如: 主线程(main函数自上向下执行)
任何一个线程的基本组成部分
1.CPU时间片:
操作系统(OS)为每个线程分配执行时间 ---- CPU时间片
2.运行数据
堆空间:存储线程需要使用的对象,多个线程是可以共享堆空间中的对象
栈空间:存储线程需要使用的局部变量,每个线程都有独立的栈
3.任务
|-线程的逻辑代码(程序员写的)
2.线程对象的构建方式
1.实现Runnable接口,覆盖run方法 Runnable代表任务对象
2.继承Thread类
//案例 ---- 交替打印 CPU 可以给 t1 t2 主线程 分配时间片
package thread;
public class TestThread {
public static void main(String[] args) {
//1.构建任务对象
Runnable task1 = new Task1();
//2.构建线程对象
Thread t1 = new Thread(task1); // 将 task1 任务 交给 t1线程
Thread t2 = new Task2(); // 将 task2 任务 交给 t2线程[将task2当做是t2线程的任务]
//3.开启线程 线程对象--------线程
t1.start();
t2.start();
for(int i=1;i<=1000;i++){
System.out.println("----"+i);
}
}
}
//线程任务类构建方式1 实现Runnable接口 Runnable:代表人物对象
class Task1 implements Runnable{
// run方法中填充任务代码
public void run() {
for(int i=1;i<=1000;i++){
System.out.println("****"+i);
}
}
}
//线程任务类的构建方式2 继承Thread类 Thread 类实现了 Runnable接口
class Task2 extends Thread{
public void run(){
for(int i=1;i<=1000;i++){
System.out.println("++++"+i);
}
}
}
//--------简化写法
package thread;
public class TestThread {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
for(int i=1;i<=1000;i++){
System.out.println("++++"+i);
}
}
});
Thread t2 = new Thread(()->{
for(int i=1;i<=1000;i++){
System.out.println("++++"+i);
}
});
t1.start();
t2.start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YSDcWJuR-1630564867855)(F:\javase\线程\截图\线程状态.jpg)]
3.Thread类中的方法
static void sleep(long time):当前线程会进入睡眠状态(睡眠 time 毫秒),此时线程会释放CPU时间片,时间到期以后,线程回到就绪状态,等待再一次被os选中。
//案例
package thread;
public class TestSleep {
public static void main(String[] args) {
System.out.println("main start......");
Thread t1 = new Thread(new Runnable() {
public void run() {
for(int i=1;i<=100;i++){
System.out.println("*****"+i);
try{
Thread.sleep(100); // t1 的任务代码 --- t1进入休眠状态
}catch(Exception e){
e.printStackTrace();
}
}
}
});
t1.start();
for(int i=1;i<=100;i++){
System.out.println("main "+i);
}
System.out.println("main end....");
}
}
public final void join()------- t.join():当前线程会进入无限期等待状态,会释放持有的时间片。当t线程执行结束以后,当前线程回到就绪状态,继续竞争时间片。
//案例
package thread;
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
System.out.println("main start...");
Thread t1 = new Thread(()->{
for(int i=1;i<=100;i++){
System.out.println("t1...."+i);
}
});
Thread t2 = new Thread(()->{
for(int i=1;i<=100;i++){
System.out.println("t2..."+i);
}
});
t1.start();
t2.start();
t1.join(); // 主线程的任务代码
/*
t1.join()执行以后,主线程进入无限期等待状态,等到t1结束,才会回到就绪状态,参与时间片的竞争,拿到时间片
才会继续执行自己的任务代码
*/
t2.join(); // 主线程的任务代码
/**
* t2.join() 执行以后,主线程进入无限期等待状态,等到t2结束,才会回到就绪状态
* 参与时间片的竞争,拿到时间片以后,才继续执行自己的任务代码
*/
/*
* 主线程进入无限期等待状态,t1 与 t2 交替打印 当t1与t2任务结束以后,主线程才会回到就绪状态
* */
for(int i=1;i<=1000;i++){
System.out.println("main "+i);
}
System.out.println("main end......");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BvL4GRQX-1630564867856)(F:\javase\线程\截图\线程状态图2.jpg)]
4.线程安全
1.概念:
线程安全|线程同步 : 多线程并发操作时,必须保证原子操作不被破坏---保证临界资源的正确性
原子操作:程序的原子性,程序中的代码,作为一个整体,要执行就都执行,要不执行就都不执行
锁|临界资源|临界区|锁标记:多线程并发访问时,共享的一个对象----堆空间
Java中每个对象都具备锁标记,提供给线程使用。
2.加锁|线程安全|线程同步 --- synchronized
同步代码块
synchronized(临界资源){
//原子操作的代码
}
同步方法
访问修饰符 synchronized 返回值类型 函数名(形式参数列表){
//原子操作的代码
}
3.只有获取了锁标记的线程,拿到时间片,才可以执行任务,否则进入阻塞状态
原子操作结束以后,才会释放锁标记。
//案例
package thread;
public class TestThreadArray {
public static void main(String[] args) throws InterruptedException {
final MyList my = new MyList();
// t1 线程的任务 : 将 "C" 添加到 MyList 数组中
Thread t1 = new Thread(()->{
my.add("C");
});
// t2 线程的任务 : 将 "D" 添加到MyList 数组中
Thread t2 = new Thread(()->{
my.add("D");
});
t1.start();
t2.start();
t1.join();
t2.join();
// 期望: 数组中的元素为 A B C D
my.print();
System.out.println(my.index);
}
}
class MyList{
String[] str = {"A","B","","",""};
int index = 2;//数组中有效元素的个数
/*public void add(String s){
synchronized(this){
str[index] = s;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
}
}*/
public synchronized void add(String s){
str[index] = s;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
}
public void print(){
for(String s:str){
System.out.println(s);
}
}
}
4.死锁问题
线程可以持有多个锁标记
当两条线程互相持有对方所需要的锁标记 ----- 死锁
synchronized(o1){
synchronized(o2){
// t1 的任务
}
}
synchronized(o2){
synchronized(o1){
// t2 的任务
}
}
线程之间的通信机制 ----- 等待-通知机制
等待
o.wait():当前线程会进入阻塞状态(进入o的等待队列),线程会释放自身持有的锁标记 必须书写在对o加锁的同步代码块
通知
o.notify():从等待队列中随机释放一条线程(由OS决定) 必须书写在对o加锁的同步代码块中
o.notifyAll():释放等待队列中所有的线程 必须书写在对o加锁的同步代码块中
//案例
package thread;
public class TestSynchronized {
public static void main(String[] args) {
final Object o1 = new Object();
final Object o2 = new Object();
Thread t1 = new Thread(()->{
synchronized (o1){
try {
o1.wait(); // t1 会释放自身持有的锁标记 进入 o1 的等待队列
Thread.sleep(100); // 保证 t1 和 t2 刚开始都能获取锁标记
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
System.out.println("t1 start......");
System.out.println("t1 end......");
}
}
});
Thread t3 = new Thread(()->{
synchronized (o1){
try {
o1.wait();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("t3 start......");
System.out.println("t3 end......");
o1.notifyAll();
}
}
});
Thread t2 = new Thread(()->{
synchronized (o2){
try {
Thread.sleep(100); // 保证 t1 和 t2 刚开始都能获取锁标记
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("t2 start......");
System.out.println("t2 end......");
o1.notifyAll();
}
}
});
t1.start();
t2.start();
t3.start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZxW6DvG-1630564867857)(F:\javase\线程\截图\线程的状态图2.jpg)]
5.生产者消费者
package thread;
public class TestStack {
public static void main(String[] args) {
final MyStack ms = new MyStack();
Thread t1 = new Thread(()->{ // 生产者 1
for(int i=1;i<=26;i++){
ms.push(i+"");
}
});
Thread t2 = new Thread(()->{ // 生产者 2
for(char i='a';i<='z';i++){
ms.push(i+"");
}
});
Thread t3 = new Thread(()->{ //消费者1
for(int i=1;i<=26;i++){
ms.pop();
}
});
Thread t4 = new Thread(()->{//消费者2
for(int i=1;i<=26;i++){
ms.pop();
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyStack{
String[] str = {"","","","","",""};
int index = 0; // 记录数组中元素的数量
//入栈-----生产者
public synchronized void push(String s){
while(index==str.length){ // 说明容器满了---生产者需要阻塞
try{
this.wait(); // !!!!!!
}catch(Exception e){
e.printStackTrace();
}
}
str[index] = s;
index++;
System.out.println(s+" push.....");
print();
this.notifyAll();
}
//出栈-----消费者
public synchronized void pop(){
while(index==0){//说明容器没有元素 ----- 消费者阻塞
try{
this.wait(); // ~~~~
}catch(Exception e){
e.printStackTrace();
}
}
index--;
System.out.println(str[index]+" pop");
str[index] = "";
print();
this.notifyAll();
}
public void print(){
for(String s:str){
System.out.print(s+" ");
}
System.out.println();
}
}
/*
在控制台上打印数字(0-9)和字母(a-z)
1.多线程实现 t1打印数字 t2打印字母
2.要求 打印一个数字,打印两个字母
0 a b
1 c d
2 e f
3 g h
...
9 s t
u v w x y z
*/
package thread;
public class TestNumber {
public static void main(String[] args) throws Exception{
final Object o = new Object();
Thread t1 = new Thread(()->{
synchronized (o){
for(int i=0;i<=9;i++){
System.out.println(i);
try {
o.notifyAll();
o.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(()->{
synchronized (o){
for(char i='a';i<='z';i+=2){
System.out.println(i+" "+(char)(i+1));
try{
o.notifyAll();
if(i<'s'){ // t1 还未结束
o.wait();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
});
t1.start();
Thread.sleep(100);//主线程的任务代码 保证 t2 比 t1 晚激活
t2.start();
}
}
6.线程池
1.线程池: 预先创建好一些线程,放置在线程池中,如果需要执行任务,则分配线程(不需要新建)去执行,任务结束之后,线程不会销毁,会回到线程池中。
2.线程池的获取方式
1.Executors.newFixedThreadPool(2):线程池中会保存两条线程(无论任务有多少,都是只有两条线程)
2.Executors.newCachedThreadPool():动态创建线程,贤臣的数量取决于提交给线程池任务的数量
//案例
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
//1.创建线程池
// ExecutorService es = Executors.newFixedThreadPool(5); 线程池中固定存在5条线程
ExecutorService es = Executors.newCachedThreadPool();
//2.任务对象
Runnable task1 = new Runnable() {
public void run() {
for(int i=1;i<=100;i++){
System.out.println("****"+i);
}
}
};
Runnable task2 = ()->{
for(int i=1;i<=100;i++){
System.out.println("++++"+i);
}
};
Runnable task3 = ()->{
for (int i=1;i<=100;i++){
System.out.println("~~~"+i);
}
};
//3.将任务对象提交给线程池
es.submit(task1);
es.submit(task2);
es.submit(task3);
//4.关闭线程池
es.shutdown();
}
}
7.任务对象的获取方式
1.继承 Thread 类 覆盖run方法
2.实现Runnable接口,覆盖 run方法
3.实现Callable接口,覆盖call方法
区别:
run():不允许抛异常,只能自己处理try-catch,而且没有返回值
call():允许抛异常,有返回值
//案例
package thread;
import java.util.concurrent.*;
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main start.....");
// 1+2+3+4+5+6+7+8+9+...1000 1.计算奇数和 2.计算偶数和
//1.创建线程池
ExecutorService es = Executors.newFixedThreadPool(2); // 线程池中固定2条线程
//2.准备任务对象
Callable<Integer> task1 = new Callable<Integer>() {
public Integer call() throws Exception {
System.out.println("task1 start...");
int sum = 0;
for(int i=1;i<=999;i+=2){
sum+=i;
}
System.out.println("task1 end....");
return sum;
}
};
Callable<Integer> task2 = ()->{
System.out.println("task2 start...");
int sum = 0;
for(int i=2;i<=1000;i+=2){
sum+=i;
}
System.out.println("task2 end....");
return sum;
};
//3.将任务对象提交给线程池
/*
Future对象包含了
1.任务执行结束的返回值[call()方法的返回值]
2.call()方法产生的异常
Future 对象 get():获取任务的执行结果和任务执行过程中产生的异常
task1 task2 main
task1 和 task2 未执行完,主线程会等待 ----- 线程同步 先吃饭,喝水,在吃饭
线程异步:主线程不会等待 ------ 之前交替打印的代码 边打电话,边走路
*/
Future<Integer> f1 = es.submit(task1);
Future<Integer> f2 = es.submit(task2);
Integer sum1 = f1.get(); // 主线程的任务
Integer sum2 = f2.get(); // 主线程的任务
System.out.println(sum1+sum2); //主线程的任务
//4.关闭线程池
es.shutdown();
System.out.println("main end....");
}
}
8.Lock与Condition
1. Lock lock = new ReentrantLock(); 锁对象 互斥锁
2.方法
lock.lock();加锁
lock.unlock():解锁
3.语法
try{
lock.lock();
//原子操作
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
//案例
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args)throws Exception {
final MyArray ma = new MyArray();
Thread t1 = new Thread(()->{
ma.add("C");
});
Thread t2 = new Thread(()->{
ma.add("D");
});
t1.start();
t2.start();
t1.join();
t2.join();
ma.print();
}
}
class MyArray{
String[] str = {"A","B","",""};
int index = 2;
Lock lock = new ReentrantLock();
public void add(String s){
lock.lock();
str[index] = s;
index++;
lock.unlock();
}
public void print(){
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}
4.获取等待队列
Condition c1 = lock.newCondition();
方法:
c1.awit():当前线程进入阻塞状态
c1.signal():由OS随机唤醒一条线程
c1.signalAll():唤醒等待队列中所有的线程
//案例
package thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestProduceConsumer {
public static void main(String[] args) {
final Stack ms = new Stack();
Thread t1 = new Thread(()->{ // 生产者 1
for(int i=1;i<=26;i++){
ms.push(i+"");
}
});
Thread t2 = new Thread(()->{ // 生产者 2
for(char i='a';i<='z';i++){
ms.push(i+"");
}
});
Thread t3 = new Thread(()->{ //消费者1
for(int i=1;i<=26;i++){
ms.pop();
}
});
Thread t4 = new Thread(()->{//消费者2
for(int i=1;i<=26;i++){
ms.pop();
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Stack{
String[] str = {"","","","","",""};
int index = 0;
Lock lock = new ReentrantLock();
Condition produce = lock.newCondition();
Condition consumer = lock.newCondition();
//生产者的任务
public void push(String s){
lock.lock();
while(index==str.length){
try {
produce.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
str[index] = s;
System.out.println(s+" push....");
index++;
print();
consumer.signalAll();
lock.unlock();
}
//消费者
public void pop(){
lock.lock();
while(index==0){
try {
consumer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
System.out.println(str[index]+" pop");
str[index] = "";
produce.signalAll();
lock.unlock();
}
public void print(){
for(String s:str){
System.out.print(s+" ");
}
System.out.println();
}
}
9.读写锁
对于容器(栈,数组,集合.....)的操作: 读|写
读:不会影响容器内容 例如:遍历集合元素
写:会影响容器内容 例如:向集合中添加元素 删除集合元素
写与写:相互阻塞 写阻塞写
写与读:读会阻塞写,写也会阻塞读
读与读:不会阻塞
ReadWriteLock 写锁未分配(没有写操作时) 读锁会反复分配 ---- 读写分离
//案例
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestReadWriterLock {
public static void main(String[] args) {
final MyArrayList ma = new MyArrayList();
Thread t1 = new Thread(()->{ // 写线程
ma.add("C");
});
Thread t2 = new Thread(()->{ // 写线程
ma.add("D");
});
Thread t3 = new Thread(()->{ // 读线程
for(int i=1;i<=100;i++){
System.out.println("读线程t3......"+i);
}
ma.print();
});
Thread t4 = new Thread(()->{ // 读线程
for(int i=1;i<=100;i++){
System.out.println("读线程t4......"+i);
}
ma.print();
});
t1.start();
t2.start();
t3.start();
t4.start();
/*
* t1 与 t2 是写线程
* t3 与 t4 是读线程
* t1 与 t2 相互阻塞
* t1 与 t3 相互阻塞
* t1 与 t4 相互阻塞
* t2 与 t3 相互阻塞
* t2 与 t4 相互阻塞
* t3 与 t4 不会阻塞 ------ 相互之间不会阻塞,交替打印
* */
}
}
class MyArrayList{
String[] str = {"A","B","","",""};
int index = 2;
ReadWriteLock lock = new ReentrantReadWriteLock();//读写锁 读写分离
Lock read = lock.readLock(); // 读锁 会反复分配
Lock write = lock.writeLock();//写锁
//写操作
public void add(String s){
write.lock();
str[index] = s;
index++;
write.unlock();
}
//读操作
public void print() {
read.lock();
for(String s:str){
System.out.println(s);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
read.unlock();
}
}
10.加强版的读写分离
读操作不加锁,写加锁 ---- 加强版的读写分离
读与读:不阻塞
读与写:不阻塞
写与写:阻塞
CopyOnWriteArrayList: 线程安全的集合 并发效率很高,低于ArrayList
写的实现:对容器做一个副本,在副本基础之上进行写操作,结束之后,将副本地址传递给原容器
读:操作的是原容器
牺牲了写的效率,提高了读的效率
CopyOnWriteArraySet
11.线程安全的集合
ConcurrentHashMap 线程安全,并发效率高,低于HashMap
JDK1.8之前 分段锁(16段) 控制锁的粒度,减少线程之间的冲突,进而提高并发效率
JDK1.8 CAS+少量的synchronized
CAS : compare and swap 比较交换 存在三个值: V:备份值 E:预期值 B:新值
只有预期值与备份值相等时,才将新值放入容器
存在ABA问题 解决: 版本号的限制 A-B-A 1A-2B-3A
队列
Queue 队列 是一种集合接口(Collection)
特点:先进先出(FIFO)
常见的方法:
boolean add(E e):向队列中添加元素,若集合满了,添加失败并报错
boolean offer(E e):向队列中添加元素,若集合满了,添加失败,返回false
E element():获取队列的头元素,若集合为空,获取失败报异常
E peek():获取队列的头元素,若集合为空,则返回null
E remove():删除队列的头元素,若队列为空,删除失败,报异常
E poll():删除队列的头元素,若队列为空,删除失败,返回null
常见的实现类
LinkedList
ConcurrentLinkedQueue 线程安全的队列 采用CAS算法
常见的子接口
BlockingQueue 阻塞队列 当某个线程从该队列中获取元素时,若队列为空,获取失败,则该线程会进入阻塞 状态
|-ArrayBlockingQueue 有界队列 当某个线程向队列中填充元素时,若队列已满,添加失败,该线程进入 阻塞状态
|-LinkedBlockingQueue 无界队列
集合整理
Collection:存储任意类型的对象
Set(无序,无下标,元素不能重复)
|-HashSet
|-LinkedHashSet
|-SortedSet(接口)
|-TreeSet
|-CopyOnWriteArraySet
List
|-ArrayList
|-Vector
|-LinkedList
|-CopyOnWriteArrayList
Queue
|-LinkedList
|-ConcurrentLinkedQueue
|-BlockingQueue(接口)
|-ArrayBlockingQueue
|-LinkedBlockingQueue
Map
|-HashMap
|-Hashtable
|-linkedHashMap
|-Properties
|-SortedMap(接口)
|-TreeMap
|-ConcurrentHashMap
chp16_反射
1.概念
是一个底层技术,常用在框架底层的设计或者开发工具的设计中。
2.类对象
类加载:当我们第一次使用到某个类的信息时,会将 字节码文件(.class)中类的信息读取到程序中并保存----类对象
类加载只进行一次-------一个类的类对象有且只有一个。
类对象与类的对象区别:
类对象只有一个 记录老虎信息的铭牌----描述的是一类动物----只有一份
类的对象可以有无穷多个 笼子里面活生生的老虎------可以有很多个
3.类对象的获取方式
1.类名.class
Class c1 = ArayList.class
2.类对象 引用名.getClass()
ArrayList list = new ArrayList();
Class c2 = list.getClass();
3.Class.forName("类的全限定名")----进行类加载
Class c3 = Class.forName("java.util.ArrayList");
注意:
一个类的类对象有且只有一个
c1==c2==c3 ------> true
//案例
package reflect;
import java.util.ArrayList;
public class TestClass {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = ArrayList.class;
Class c2 = Class.forName("java.util.ArrayList");
ArrayList list = new ArrayList();
Class c3 = list.getClass();
System.out.println(c1==c2); //true
System.out.println(c2==c3); //true
}
}
4.常用方法
类对象记录了类的描述性信息
getName(): 获取类的全限定名 包名.类名
getPackage():获取Package对象 包对象
getSuperClass():获取父类的类对象
Class[] getInterfaces():获取全部接口的类对象
//案例
package reflect;
import java.util.ArrayList;
public class TestClassDemo {
public static void main(String[] args) {
Class c = ArrayList.class;
//获取类的全限定名
System.out.println("获取类的全限定名");
String name = c.getName();
System.out.println(name);
//获取包对象
System.out.println("获取包对象与包名");
Package pa = c.getPackage();
//获取包名
String packageName = pa.getName();
System.out.println(packageName);
//获取父类的类对象
System.out.println("获取父类的类对象与父类名");
Class superClass = c.getSuperclass();
String superName = superClass.getName();
System.out.println(superName);
//获取所有已经实现接口的类对象
System.out.println("获取所有实现接口的类对象与接口名");
Class[] interfaces = c.getInterfaces();
for(Class inter:interfaces){
System.out.println(inter.getName());
}
}
}
5.使用类对象访问属性和方法
类的对象
//1.new 类的对象
Student stu = new Student();
//2.属性和方法的访问
stu.age = 30;
stu.study();
类对象
1.获取类对象
2.使用类对象获取类的对象 | 使用类对象直接访问类的属性和方法
1.使用类对象获取类的对象
Class c = Class.forName("包名.类名") --- 类对象
Object o = c.newInstance(); ---- 类的对象(默认触发无参构造)
2.使用类对象获取类的属性,方法,构造方法
方法对象:
Method ----- 记录了方法的详细信息
Method[] c.getMethods():获取本类以及父类中所有的公开方法(方法对象)
c.getDeclaredMethods():获取本类中的所有方法(只有本类,包含私有)
属性对象
Field ----- 记录了属性的详细信息
Filed[] c.getFields():获取本类以及父类中所有的公开属性(属性对象)
c.getDeclaredFields():获取本类所有的属性(只有本类,包含私有)
构造方法对象
Constructor --- 记录了构造方法的详细信息
Constructors[] c.getConstructors():获取本类中所有的构造方法
//案例
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestSuper {
public static void main(String[] args)throws Exception {
//1.使用类对象构建 Sub类的对象
Class c = Class.forName("reflect.Sub");
Object o = c.newInstance();
System.out.println(o instanceof Sub);//true
//2.获取接口名
Class[] cs = c.getInterfaces();
for(Class c1:cs){
System.out.println(c1.getName()); // 只能获取直接实现的接口 IA,IB
}
//3.获取本类以及父类中所有的公开方法
System.out.println("获取本类以及父类中所有的公开方法(包含多级继承)");
Method[] ms = c.getMethods();
for(Method m:ms){
System.out.println(m.getName());
}
//4.获取本类中所有的方法
System.out.println("获取本类中所有的方法(包含私有)");
Method[] declaredMethods = c.getDeclaredMethods();
for(Method m:declaredMethods){
System.out.println(m.getName());
}
//5.获取本类以及父类中所有的公开属性
System.out.println("获取本类以及父类中所有的公开属性");
Field[] fields = c.getFields();
for(Field f:fields){
System.out.println(f.getName());
}
//6.获取本类中所有的属性(包含私有属性)
System.out.println("获取本类中所有的属性(包含私有属性)");
Field[] declaredFields = c.getDeclaredFields();
for(Field f:declaredFields){
System.out.println(f.getName());
}
//7.获取本类所有的构造方法
System.out.println("获取所有构造函数的名字");
Constructor[] cons = c.getConstructors();
for(Constructor con:cons){
System.out.println(con.getName());
}
}
}
interface IA{}
interface IB extends IC{}
interface IC{}
class Sub extends Super implements IA,IB{
private String name;
public boolean sex;
public void m3(){
System.out.println("Sub m3....");
}
private void m4(){
System.out.println("sub m4()");
}
public void m4(int a){
System.out.println("Sub m4(int a)");
}
public Sub(){}
public Sub(String name,boolean sex){
this.name=name;
this.sex=sex;
}
}
class Super{
public int age;
private String name;
public void m1(){
System.out.println("super m1()");
}
void m2(){
System.out.println("super m2()...");
}
public Super(int age, String name) {
this.age = age;
this.name = name;
}
public Super() {
}
}
//案例
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestStudent {
public static void main(String[] args) throws Exception {
//1.获取类对象 --- 获取类的对象
Class c = Class.forName("reflect.Student");
Object o = c.newInstance(); // 触发无参构造函数的执行
System.out.println(o instanceof Student);
//2.使用类对象获取类的对象 ------ 触发有参构造
//构造函数对象
Constructor constructor = c.getConstructor(int.class, String.class); // 获取 public Student(int age,String name)
Object o1 = constructor.newInstance(10, "小王");
System.out.println(o1);
Constructor con = c.getConstructor(String.class, int.class);
Object o2 = con.newInstance("小黑", 5);
System.out.println(o2);
//3.基于类对象 获取类的公开方法
Method study = c.getMethod("study"); // study()
study.invoke(o1); // 方法隶属于对象
Method study1 = c.getMethod("study", int.class);//study(int a)
study1.invoke(o1,10);
Method m1 = c.getDeclaredMethod("m1");
m1.setAccessible(true);// 临时修改私有属性的访问权限 --- 反射可以打破封装
m1.invoke(o1);
//4.基于类对象 操作类的属性
Field filed = c.getDeclaredField("age");
filed.setAccessible(true);
filed.set(o1,20);
System.out.println(filed.get(o1));
}
}
class Student{
private int age;
private String name;
public void study(){
System.out.println("Student study");
}
public void study(int hour){
System.out.println("student study "+hour);
}
private void m1(){
System.out.println("private m1().......");
}
private void m2(int a){
System.out.println("private m2(int a)");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name.equals(student.name);
}
@Override
public int hashCode() {
int result = age;
result = 31 * result + name.hashCode();
return result;
}
public Student(String name,int age){
System.out.println("Student(name age)");
this.name=name;
this.age=age;
}
public Student(int age, String name) {
System.out.println("Student(int age,String name)");
this.age = age;
this.name = name;
}
public Student() {
System.out.println("Student()....");
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
反射的优缺点:
优点:
打破封装
代码使用更加灵活
可以用于框架底层以及工具的开发
缺点:
可读性差
操作繁琐
打破封装,数据不安全
6.设计模式
概念:是一种被反复使用,被众多程序员认可的代码策略。 --- 23种
1.单例模式
概念:创建一个类,该类只能创建一个对象 ----- 不定义成员变量(线程安全问题)
无论多少条线程使用,都是这一个对象.
1.饿汉式—可能浪费空间,并发效率高
//饿汉式
class ClassA{
//预先创建好一个ClassA对象,提供给外界使用
private static ClassA ca = new ClassA();
//外界获取该类对象的方法
public static ClassA new Instance(){
return ca;
}
//将构造方法私有, 禁止外界随意构建对象
private ClassA(){}
}
2.懒汉式----节省空间,但是并发效率低
//懒汉式
class ClassB{
private static ClassB cb = null;
//为了避免出现线程不安全,需要加锁
public static synchronized ClassB newInstance(){
if(cb==null){
cb = new ClassB();
}else{
return cb;
}
}
private ClassB(){}
}
3.双重校验锁----不浪费空间,在保证线程安全的前提下,尽可能的提高并发效率
//双重校验锁
class ClassC{
private static ClassC cc = null;
private ClassC(){}
public static ClassC newInstance(){
/*
1.若 cc==null cc = new ClassC(); 加锁保证线程安全
2.若 cc!=null return cc; 并发效率高
*/
if(cc==null){
synchronized(ClassC.class){ // 类对象是类加载的产物
if(cc==null){
cc = new ClassC();
}
}
}
return cc;
}
}
4.懒加载-----静态内部类实现
class ClassD{
private static class Holder{
static ClassD cd = new ClassD();
}
public static ClassD newInstance(){
return Holder.cd;
}
private ClassD(){}
}
2.工厂模式
工厂:解决对象的创建问题
package reflect;
import java.io.*;
import java.util.Properties;
public class MyFactory<T>{
static Properties pro = null;
//类加载时,静态代码块只会加载一次 所以配置文件只会读取一次
static{
try(InputStream is = MyFactory.class.getResourceAsStream("/conf/factory.properties")){
pro.load(is);
}catch(Exception e){
e.printStackTrace();
}
}
//获取对象
public T getBean(String key){
try {
Class c = Class.forName(pro.getProperty(key)); // key1 来自于配置文件
Object o = c.newInstance();
return (T)o;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
//工厂怎么使用
1.准备配置文件 [1.配置文件的后缀 .properties 2.内容为 key=value]
key1=value1;
key2=value2;
2.构建工厂类的对象,同时设定泛型(控制工厂创建对象的类型)
println(“获取本类中所有的方法(包含私有)”);
Method[] declaredMethods = c.getDeclaredMethods();
for(Method m:declaredMethods){
System.out.println(m.getName());
}
//5.获取本类以及父类中所有的公开属性
System.out.println(“获取本类以及父类中所有的公开属性”);
Field[] fields = c.getFields();
for(Field f:fields){
System.out.println(f.getName());
}
//6.获取本类中所有的属性(包含私有属性)
System.out.println(“获取本类中所有的属性(包含私有属性)”);
Field[] declaredFields = c.getDeclaredFields();
for(Field f:declaredFields){
System.out.println(f.getName());
}
//7.获取本类所有的构造方法
System.out.println(“获取所有构造函数的名字”);
Constructor[] cons = c.getConstructors();
for(Constructor con:cons){
System.out.println(con.getName());
}
}
}
interface IA{}
interface IB extends IC{}
interface IC{}
class Sub extends Super implements IA,IB{
private String name;
public boolean sex;
public void m3(){
System.out.println(“Sub m3…”);
}
private void m4(){
System.out.println(“sub m4()”);
}
public void m4(int a){
System.out.println(“Sub m4(int a)”);
}
public Sub(){}
public Sub(String name,boolean sex){
this.name=name;
this.sex=sex;
}
}
class Super{
public int age;
private String name;
public void m1(){
System.out.println(“super m1()”);
}
void m2(){
System.out.println(“super m2()…”);
}
public Super(int age, String name) {
this.age = age;
this.name = name;
}
public Super() {
}
}
~~~java
//案例
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestStudent {
public static void main(String[] args) throws Exception {
//1.获取类对象 --- 获取类的对象
Class c = Class.forName("reflect.Student");
Object o = c.newInstance(); // 触发无参构造函数的执行
System.out.println(o instanceof Student);
//2.使用类对象获取类的对象 ------ 触发有参构造
//构造函数对象
Constructor constructor = c.getConstructor(int.class, String.class); // 获取 public Student(int age,String name)
Object o1 = constructor.newInstance(10, "小王");
System.out.println(o1);
Constructor con = c.getConstructor(String.class, int.class);
Object o2 = con.newInstance("小黑", 5);
System.out.println(o2);
//3.基于类对象 获取类的公开方法
Method study = c.getMethod("study"); // study()
study.invoke(o1); // 方法隶属于对象
Method study1 = c.getMethod("study", int.class);//study(int a)
study1.invoke(o1,10);
Method m1 = c.getDeclaredMethod("m1");
m1.setAccessible(true);// 临时修改私有属性的访问权限 --- 反射可以打破封装
m1.invoke(o1);
//4.基于类对象 操作类的属性
Field filed = c.getDeclaredField("age");
filed.setAccessible(true);
filed.set(o1,20);
System.out.println(filed.get(o1));
}
}
class Student{
private int age;
private String name;
public void study(){
System.out.println("Student study");
}
public void study(int hour){
System.out.println("student study "+hour);
}
private void m1(){
System.out.println("private m1().......");
}
private void m2(int a){
System.out.println("private m2(int a)");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name.equals(student.name);
}
@Override
public int hashCode() {
int result = age;
result = 31 * result + name.hashCode();
return result;
}
public Student(String name,int age){
System.out.println("Student(name age)");
this.name=name;
this.age=age;
}
public Student(int age, String name) {
System.out.println("Student(int age,String name)");
this.age = age;
this.name = name;
}
public Student() {
System.out.println("Student()....");
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
反射的优缺点:
优点:
打破封装
代码使用更加灵活
可以用于框架底层以及工具的开发
缺点:
可读性差
操作繁琐
打破封装,数据不安全
6.设计模式
概念:是一种被反复使用,被众多程序员认可的代码策略。 --- 23种
1.单例模式
概念:创建一个类,该类只能创建一个对象 ----- 不定义成员变量(线程安全问题)
无论多少条线程使用,都是这一个对象.
1.饿汉式—可能浪费空间,并发效率高
//饿汉式
class ClassA{
//预先创建好一个ClassA对象,提供给外界使用
private static ClassA ca = new ClassA();
//外界获取该类对象的方法
public static ClassA new Instance(){
return ca;
}
//将构造方法私有, 禁止外界随意构建对象
private ClassA(){}
}
2.懒汉式----节省空间,但是并发效率低
//懒汉式
class ClassB{
private static ClassB cb = null;
//为了避免出现线程不安全,需要加锁
public static synchronized ClassB newInstance(){
if(cb==null){
cb = new ClassB();
}else{
return cb;
}
}
private ClassB(){}
}
3.双重校验锁----不浪费空间,在保证线程安全的前提下,尽可能的提高并发效率
//双重校验锁
class ClassC{
private static ClassC cc = null;
private ClassC(){}
public static ClassC newInstance(){
/*
1.若 cc==null cc = new ClassC(); 加锁保证线程安全
2.若 cc!=null return cc; 并发效率高
*/
if(cc==null){
synchronized(ClassC.class){ // 类对象是类加载的产物
if(cc==null){
cc = new ClassC();
}
}
}
return cc;
}
}
4.懒加载-----静态内部类实现
class ClassD{
private static class Holder{
static ClassD cd = new ClassD();
}
public static ClassD newInstance(){
return Holder.cd;
}
private ClassD(){}
}
2.工厂模式
工厂:解决对象的创建问题
package reflect;
import java.io.*;
import java.util.Properties;
public class MyFactory<T>{
static Properties pro = null;
//类加载时,静态代码块只会加载一次 所以配置文件只会读取一次
static{
try(InputStream is = MyFactory.class.getResourceAsStream("/conf/factory.properties")){
pro.load(is);
}catch(Exception e){
e.printStackTrace();
}
}
//获取对象
public T getBean(String key){
try {
Class c = Class.forName(pro.getProperty(key)); // key1 来自于配置文件
Object o = c.newInstance();
return (T)o;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
//工厂怎么使用
1.准备配置文件 [1.配置文件的后缀 .properties 2.内容为 key=value]
key1=value1;
key2=value2;
2.构建工厂类的对象,同时设定泛型(控制工厂创建对象的类型)