Java基础
- 为什么要分数据类型?
优化存储空间,提高内存利用率。
- Java有哪几种数据类型?
Java分基本数据类型和引用数据类型。
基本数据类型(四类8种)
1.整数类型: byte int short long
2.浮点数(小数)类型: float double
3.字符类型: char
4.布尔类型: boolean
运算
赋值运算符
package day02;
/*
* java中的运算符(6种)(运算就会有结果,有结果就一定会有正负,类型)
* 1.算术运算符 + - * / %(取模) ++(自增) --(自减)
* 2.赋值运算符 = += -= *= /= %=
* 3.关系运算符 < > >= <= == != (结果一定是:boolean类型)
* 4.逻辑运算符 &/&& |/|| !
* 5.连接运算符
* 6.三目运算符
*/
public class Demo01赋值运算符 {
public static void main(String[] args) {
/*
* 2.赋值运算符 = += -= *= /= %=
* 就是把=右边的常量赋值给=左边的变量(注意的是=左边必须是变量)
* 简单说:
* 就是把符号左右两边的值进行相运算,然后运算完成后会有结果
* 并把结果赋值给符号的左边的变量
* 注意:
* java中1个等号是赋值 2个等号才是判断
*/
int a = 3;
// 3 = a;
a += 4;//就是把+=号左右两边的值做加法运算符,然后得到结果赋值给+=左边的变量
// 相当于a=a+4
System.out.println(a);//7
/*
* 面试题
*/
short s = 1;
// s = s+1;//byte char short只要相运算结果都是int类型
short s2 = 1;
s2+=10;//s2=(short)(s2+10) +=是运算符,系统底层会自动对其进行强制转换
}
}
逻辑运算符
package day02;
/*
* java中的运算符(6种)(运算就会有结果,有结果就一定会有正负,类型)
* 1.算术运算符 + - * / %(取模) ++(自增) --(自减)
* 2.赋值运算符 = += -= *= /= %=
* 3.关系运算符 < > >= <= == != (结果一定是:boolean类型)
* 4.逻辑运算符 &/&& |/|| !
* 5.连接运算符 +
* 6.三目运算符
*/
public class Demo02逻辑运算符 {
public static void main(String[] args) {
/*
* 4.逻辑运算符 &/&& |/|| !
* 1.逻辑运算符的结果是boolean类型
* 2.逻辑运算符是用来逻辑运算的,它是建立在关系运算符基础之上的,
* 当两个关系需要兼顾考虑的时候,可以用逻辑运算符
* 举例:
* java中要表示某个值,是否在某个区间范围内?如何表示?
*/
//80是否在60-100之间?如果是,就true,否则false
int score = 80;//分数
// boolean result = 60<score<100;//java不允许这么写
boolean result = score>60 & score<100;
// System.out.println(result);
/*
* &/&&:(与、短路与)
* 1.与、短路与的意思都是并且的意思
* 2.用该运算符,记住一句话就可以:
* 遇false(关系运算表达式的结果)score<100
* 则false(逻辑运算表达式的结果)score>60 & score<100
* 只有符号两边结果都为true,则整个逻辑运算表达式结果为true
*/
int a = 10;
int b = 20;
// false & true
// System.out.println(a>b & a<b);//false
/*
* &/&&区别:
* 1.短路与:当符号(&&)左边的代码结果为false,&&右边代码就不执行(实际开发的时候就用&&)
* 2.与:不管&左边的结果是true,还是false,&右边代码都会执行
*/
int x = 3, y = 4;
// false
// System.out.println((++x==3)&(++y==4));//false
System.out.println((++x==3)&&(++y==4));//false
System.out.println(x);//4
System.out.println(y);//4 //实际开发的时候就用&&
/*
* ! 非 非真即假 非假即真
*/
System.out.println(!true);//false
}
}
连接运算符
package day02;
/*
* java中的运算符(6种)(运算就会有结果,有结果就一定会有正负,类型)
* 1.算术运算符 + - * / %(取模) ++(自增) --(自减)
* 2.赋值运算符 = += -= *= /= %=
* 3.关系运算符 < > >= <= == != (结果一定是:boolean类型)
* 4.逻辑运算符 &/&& |/|| !
* 5.连接运算符 +
* 6.三目运算符
*/
public class Demo03连接运算符 {
public static void main(String[] args) {
/*
* 5.连接运算符 +
* 当+左右两边,有一边是字符串的时候(""括起来的内容),
* 那么这个+就是连接运算符,有2个功能:
* 1.进行字符串连接
* 2.类型转换(就是把其他数据类型转换成String(字符串)类型)
*/
int a = 100;
String str = "a=" + a;
System.out.println(str);//"a=100"
// "100" + 200
str = "" + 100 + 200;
System.out.println(str);//"100200"
// 300 + ""
str = 100 + 200 + "";
System.out.println(str);//"300"
}
}
练习
package day02;
public class Demo04练习 {
/*
* 5.连接运算符 +
* 当+左右两边,有一边是字符串的时候(""括起来的内容),
* 那么这个+就是连接运算符,有2个功能:
* 1.进行字符串连接
* 2.类型转换(就是把其他数据类型转换成String(字符串)类型)
*/
public static void main(String[] args) {
int a,b,c;
a = b = c = 100;
//要求用一条语句输出a,b,c的结果
//syso alt+/
System.out.println("a="+a+",b="+b+",c="+c);
//a=100,b=100,c=100
System.out.println(5+6+""+5+6);//"1156"
}
}
package day02;
/*
* java中的运算符(6种)(运算就会有结果,有结果就一定会有正负,类型)
* 1.算术运算符 + - * / %(取模) ++(自增) --(自减)
* 2.赋值运算符 = += -= *= /= %=
* 3.关系运算符 < > >= <= == != (结果一定是:boolean类型)
* 4.逻辑运算符 &/&& |/|| !
* 5.连接运算符 +
* 6.三目运算符 (关系运算表达式)?表达式1:表达式2
* 复杂的业务判断,不要三目,也不要用嵌套三目,虽然可以这么写
* 因为嵌套三目可读性太差了
*/
public class Demo05三目运算符 {
public static void main(String[] args) {
/*
* mysql if(arg1,arg2,arg3) 看arg1结果为null,false,0,结果就是arg3 6.三目运算符
* (关系运算表达式)?表达式1:表达式2 1.执行流程: 1.先计算关系运算表达式的结果(boolean类型) 2.值为true,执行表达式1
* 3.值为false,执行表达式2
*/
int a = 100, b = 200;
String result = (a > b) ? "正确" : "错误";
System.out.println(result);// 错误
// 声明3个变量,用三目求出最大值?
int x = 1123, y = 532, z = 90;
// 先求出x,y的最大
int temp = x > y ? x : y;
System.out.println(temp);// 532
// 选中代码ctrl+shift+f 格式化代码
int max = temp > z ? temp : z;
System.out.println("最大值是:"+max);
}
}
分支结构
if 结构
package day02;
/*
* 程序的执行流程
程序执行的过程:程序一般都是由上往下依次执行
执行流程分类:(3类)
1.顺序结构 左往右,上往下 (默认结构)
2.分支结构(也叫选择结构)
分支结构定义:即为根据一个判断条件,
如果满足执行A,否则执行B。
分支结构包含:if语句 switch语句
什么时候用分支语句:
根据不同的条件而运行不同的代码的时候,可以用分支结构
3.循环结构
*/
public class Demo06分之结构1 {
public static void main(String[] args) {
/*
* if语句(3种格式)
* if(关系运算表达式){结果是boolean类型
* 代码1;
* }
* 代码2;
* 执行流程:
* 1.先计算关系运算表达式的结果(boolean类型)
* 2.结果true,执行代码1,然后if语句结束
* 程序继续向下执行,执行代码2
* 3.结果false,if语句直接结束
* 程序继续向下执行,执行代码2
* 注意事项:
* 1.关系运算表达式不管是简单还是复杂,结果一定是boolean类型
* 2.if语句的语句体,如果是一行代码,{}可以省略不写
* 但是建议写上,不要秀操作(面试题)
* 3.小括号后面不要写分号
*/
//判断你是年满18周岁
int age = 160000;
if(age>=18&&age<=120){ //true
System.out.println("年满18周岁");
System.out.println("可注册");
}
System.out.println("检测完毕");
}
}
if-else 结构
package day02;
/*
* 程序的执行流程
程序执行的过程:程序一般都是由上往下依次执行
执行流程分类:(3类)
1.顺序结构 左往右,上往下 (默认结构)
2.分支结构(也叫选择结构)
分支结构定义:即为根据一个判断条件,
如果满足执行A,否则执行B。
分支结构包含:if语句 switch语句
什么时候用分支语句:
根据不同的条件而运行不同的代码的时候,可以用分支结构
3.循环结构
*/
public class Demo07分之结构2 {
public static void main(String[] args) {
/*
* if语句(3种格式)
* if(关系运算表达式){结果是boolean类型
* 代码1;
* }else{//否则
* 代码2;
* }
* 代码3;
* 执行流程:
* 1.先计算关系运算表达式的结果(boolean类型)
* 2.结果true,执行代码1,然后if语句结束
* 程序继续向下执行,执行代码3
* 3.结果false,执行else里面的代码2,然后if语句结束
* 程序继续向下执行,执行代码3
* 注意:
* if else语句比三目写法复杂,但是比三目更加灵活
* 简单的业务判断用三目,复杂的用if else
*/
int i = 10;
if(i<10) {//false
System.out.println("代码1");
}else{//否则
System.out.println("代码2");
}
System.out.println("代码3");
String str = (i<10)?"代码1":"代码2";
}
}
else-if 结构
package day02;
/*
* 程序的执行流程
程序执行的过程:程序一般都是由上往下依次执行
执行流程分类:(3类)
1.顺序结构 左往右,上往下 (默认结构)
2.分支结构(也叫选择结构)
分支结构定义:即为根据一个判断条件,
如果满足执行A,否则执行B。
分支结构包含:if语句 switch语句
什么时候用分支语句:
根据不同的条件而运行不同的代码的时候,可以用分支结构
3.循环结构
*/
public class Demo08分之结构3 {
public static void main(String[] args) {
/*
* if语句(3种格式)
* if(关系运算表达式1){结果是boolean类型
* 代码1;
* }else if(关系运算表达式2){
* 代码2;
* }else if(关系运算表达式3){
* 代码3;
* }else{
* 代码4;
* }
* 代码5;
* 执行流程:
* 1.先计算关系运算表达式1的结果(boolean类型)
* 2.结果true,执行代码1,然后if语句结束
* 程序继续向下执行,执行代码5
* 3.结果false,计算关系运算表达式2的结果,若结果true,执行代码2
* 然后if语句结束,程序继续向下执行,执行代码5
* 4.以此类推,如果所有的关系运算表达式结果都为false,就执行else里面的代码4,
* 然后if语句结束,程序继续向下执行,执行代码5
* 注意:
* if else语句比三目写法复杂,但是比三目更加灵活
* 简单的业务判断用三目,复杂的用if else
*/
int i = 100;
if(i<10) {
System.out.println("代码1");
}else if(i==10){
System.out.println("代码2");
}else if(i>10){
System.out.println("代码3");
}
System.out.println("代码5");//3 5
/*
*练习:
* 1.给定一个变量,判断奇偶数
* 2.编写一个用户登录验证,给定账号,密码。如果账号密码都正确
* 提示登录成功,否则提示账号密码错误
* 3.给定一个绩效评分,根据成绩显示对应等级
* 90-100 A-优秀
* 80-89 B-良好
* 60-80 C-中等
* 0-60 D-不及格
*/
//把0-100以外的数给屏蔽掉
int score = 91;
if(score>100||score<0) {//score要小于100 大于0 就是false,就可以继续往下走
System.out.println("输入的分数不对");
}else if(score>=90) {
System.out.println("A-优秀");
}else if(score>=80) {
System.out.println("B-良好");
}else if(score>=60) {
System.out.println("C-中等");
}else {
System.out.println("D-不及格");
}
}
}
switch-case 结构
package day02;
public class Demo09分之结构4 {
public static void main(String[] args) {
/*
* 分支结构:switch-case语句
* 格式:
* switch(表达式){
* case 值1://入口
* 代码1;
* break;
* case 值2:
* 代码2;
* break;
* case 值3:
* 代码3;
* break;
* default:
* 代码4;
* }
* 代码5;
* 执行流程:
* 1.计算表达式的值(结果不是boolean)
* 2.若表达式的值和case后面的值匹配上了,
* 就以该case为入口,依次往下执行,
* 直到遇到break,或者} switch语句就结束
* 3.若表达式的值,和所有的case都不匹配,就走default里面值
* 注意事项:
* 1.switch()可以放byte short int char String
* 2.case后面的值,不能是变量,只能常量,不能重复
* 3.break可以省略不写,不写会出现case穿透
* 4.最后一个break可以不写,因为遇到}一样结束
* 5.default可以放在switch语句任意位置,也可以不写
* 建议default放在最后
*/
int i = 1000;
switch (i) {
case 1://入口
System.out.println("代码1");
break;
case 2:
System.out.println("代码2");
break;
case 3:
System.out.println("代码3");
break;
default://入口
System.out.println("代码4");
}
}
}
练习
package day02;
public class Demo10练习 {
public static void main(String[] args) {
/*
* 给定一个月份,输出对应的季节
* 3,4,5 春
* 6,7,8 夏
* 9,10,11 秋
* 12,1,2 冬
*/
int month = 8;
switch(month) {
case 3://入口
case 4:
case 5:
System.out.println("春");
break;
case 6://入口
case 7:
case 8:
System.out.println("夏");
break;
default:
System.out.println("输入的值不对");
}
}
}
循环结构
while、do/while
package day02;
/*
* 循环结构:
1.定义:循环结构在我们java编程语言里面就是,重复执行的某段代码。
循环的分类:(这里我们讲3种)
while、do/while、for
循环3要素(重点)
1.循环初始值: 循环从什么时候开始
2.循环条件: 满足什么条件要循环
3.循环体: 循环体就是要被反复执行的代码
*/
public class Demo11循环结构 {
public static void main(String[] args) {
/*
* 1.循环初始值
* while(2.循环条件){
* 3.循环体(控制循环条件语句)
* }
* 执行流程:
* 1.执行初始值
* 2.计算循环条件
* 3.若循环条件结果true,就执行循环体(控制循环条件语句)
* 4.再回到第2步计算循环条件(一句循环条件的结果来决定是否继续循环)
* 5.依次往复执行,直到循环条件结果为false,循环结束程序继续向下执行
*/
int i = 0;//1.循环初始值 初始值写0,初始值小于几就循环几次
while(i<100) {//2.循环条件 关系运算表达式 结果boolean
System.out.println("HelloWorld"+i);
i++;//控制循环条件语句
}
System.out.println("输出完毕");
/*
* do.while循环
* 和while区别,do.while会先走一遍循环体
* 再去计算循环条件
*/
int a = 111110;
do {
System.out.println("你好世界");
a++;
}while(a<100);
}
}
for循环
package day02;
public class Demo12循环结构2 {
public static void main(String[] args) {
/*
* 循环3要素(重点)
* 1.循环初始值: 循环从什么时候开始
* 2.循环条件: 满足什么条件要循环
* 3.循环体: 循环体就是要被反复执行的代码 执行流程:
* 1.执行初始值
* 2.计算循环条件
* 3.若循环条件结果true,就执行循环体(控制循环条件语句)
* 4.再回到第2步计算循环条件(一句循环条件的结果来决定是否继续循环)
* 5.依次往复执行,直到循环条件结果为false,循环结束程序继续向下执行
*/
int i = 0;
while (i < 100) {
System.out.println("HelloWorld");
i++;
}
int x = 0;
for (; x < 100;) {
System.out.println("HelloWorld");
x++;
}
for (int y = 0; y < 100; y++) {
System.out.println("HelloWorld");
}
}
}
嵌套循环
package day02;
public class Demo13嵌套循环 {
public static void main(String[] args) {
/*
* 嵌套循环:
* 循环的循环体,还是一个循环
* 需求:
* *****
* ****
* ***
* **
* *
* 诀窍:
* 1.分清楚什么是行,什么是列
* 2.外层循环控制行数,内层循环控制列数
*/
for(int i=1;i<10;i++) {//外层循环控制行数
for(int j=1;j<i+1;j++) {//内层循环控制列数
// /转义符 /n /r
System.out.print(j+"*"+i+"="+i*j+"/t");//相当于一个tab键
}
System.out.println();//作用换行
}
}
}
数组
package day02;
/*
* 数组
1.相同数据类型元素的集合(就是可以存储多个同一种数据类型的值)
2.数据既可以存储基本数据类型,也可以存储引用数据类型,比如String
3.数组也是一种数据类型(引用数据类型)
数组定义的格式
数据类型[] 数组名 = new 数据类型[数组的长度];
数组的初始化:(动态初始化,静态初始化)
1.动态初始化
(只给定数组长度,由系统给定数组的初始值)
数据类型[] 数组名 = new 数据类型[数组的长度];
2.静态初始化
(给定数组初始值,由系统给定数组的长度)
数据类型[] 数组名 = new 数据类型[]{1,2,3,4}
数据类型[] 数组名 = {1,2,3,4};
*/
public class Demo14数组 {
public static void main(String[] args) {
/*
* 数组定义的格式
数据类型[] 数组名 = new 数据类型[数组的长度];
1.动态初始化
(只给定数组长度,由系统给定数组的初始值)
*/
int[] arr = new int[3];
System.out.println(arr);//[I@22f71333
/* 了解
* [I@22f71333
* [ 一维数组
* I int
* @ 固定
* 22f71333 数组在堆中的地址
*/
/*
* 2.静态初始化
(给定数组初始值,由系统给定数组的长度)
数据类型[] 数组名 = new 数据类型[]{1,2,3,4}
还可以简写成如下的格式
数据类型[] 数组名 = {1,2,3,4};
*/
int[] arr1 = {1,2,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5};
System.out.println(arr1);//[I@13969fbe
/*
* 如何获取数组里面的值(元素)
* 通过下标 index,从0开始
* 格式:
* 数组名[index]
* 使用元素就是使用数组里面的值
*/
// System.out.println(arr1[0]);
// System.out.println(arr1[1]);
// System.out.println(arr1[2]);
// System.out.println(arr1[3]);
// System.out.println(arr1[4]);
/*
* 如何快速获取数组的长度
* 通过length属性,返回值是int
* 格式:
* 数组名.length
*/
System.out.println(arr1.length);
//循环取遍历数组
for(int i=0;i<arr1.length;i++) {
System.out.println(arr1[i]);
}
// 当访问超数组范围外的值时,就会出现越界异常
// System.out.println(arr1[5]);//indexOutofBoundsException
//思考?什么时候用动态初始化创建数组,什么时候用静态初始化创建数组???
//如果开始知道数组的值,并且不是特别多情况夏就用静态就可以,常用还是动态
/*
* int 0
* double 0.0
* char ' '
* boolean false
* String
*/
int[] iArr = new int[3];
System.out.println(iArr[0]);// 0
double[] dArr = new double[3];
System.out.println(dArr[0]);// 0.0
char[] cArr = new char[3];
System.out.println(cArr[0]);// ' '
boolean[] bArr = new boolean[3];
System.out.println(bArr[0]);//false
String[] sArr = new String[3];
System.out.println(sArr[0]);//null
}
}
往数组里面存值(2种方式)
package day02;
public class Demo15数组 {
public static void main(String[] args) {
/*
* 如何往数组里面存值(2种方式)
* 方式1:
* 通过给数组的元素赋值可以给数组里面存值
* 方式2:
* 静态初始化赋值 int[] arr2 = {1,2,3,4,5};
*/
int[] arr1 = new int[5];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = 100;
System.out.println(arr1[i]);
}
}
}
练习
package day02;
public class Demo16练习 {
public static void main(String[] args) {
/*
* 创建一个数组,往里面存10个100以内的随机数,
* 求出最大值,最小值,以及最大最小对应的下标
*/
//Math.random() 可以生成0,1之间随机小数 12 23
int[] arr3 = new int[10];
for (int i = 0; i < arr3.length; i++) {
arr3[i] = (int) (Math.random()*100);//ctrl+1回车
System.out.print(arr3[i]+" ");
}
//求出最大值
int max = 0;//存最大值
int maxIndex = 0;//存最大值的下标
for (int i = 0; i < arr3.length; i++) {
if(arr3[i]>max) {
max = arr3[i];//求最大值
maxIndex = i;//求最大值的下标
}
}
System.out.println();
System.out.println("arr3最大值:"+max);
System.out.println("arr3最大值下标:"+maxIndex);
}
}
数组API
package day03;
import java.util.Arrays;
public class Demo01数组API {
public static void main(String[] args) {
int[] arr = {3,1,7,5,2};
//打印数组元素
System.out.println(Arrays.toString(arr));
//数组排序(只是数组里面的元素替换位置)
Arrays.sort(arr);
System.out.println("排序后的数组");
System.out.println(Arrays.toString(arr));
}
}
方法
package day03;
/*
* 什么是方法?
功能的意思:完成特定功能的代码块
方法的作用?
就是为了复用
方法的格式
修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...){
方法体语句;
return 返回值;
}
1.修饰符:public static(其他的后期在讲)
2.返回值类型:方法(功能)最终的结果的数据类型 注意:没有结果就写void
3.方法名:满足规范规定即可,方便调用
4.参数类型:参数的数据类型
5.参数:
1.实际参数:实际参与运算的
2.形式参数:方法上小括号里面写的,用于接收实际参数用的
6.方法体语句:具体完成功能的代码
7.return:结束方法
8.返回值:就是功能的结果,由return带给调用者
方法的注意事项
a.方法不调用不执行
b.方法和方法是平级关系,不能嵌套定义
c.方法定义的时候参数用逗号隔开
d.方法调用的时候,有参数必须传递参数,但是不用传递参数的数据类型
e.如果方法有明确返回值,一定要写return带回一个值
*/
public class Demo02方法 {
/*
* 如何写一个方法
* 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...){
方法体语句;
return 返回值;
}
1.修饰符:public static(其他的后期在讲)
2.返回值类型:方法(功能)最终的结果的数据类型 注意:没有结果就写void
3.方法名:满足规范规定即可,方便调用
4.参数类型:参数的数据类型
5.参数:就是方法里面需要参与运算的数
6.方法体语句:具体完成功能的代码
7.return:结束方法
8.返回值:就是功能的结果,由return带给调用者
*/
//public static
public static void main(String[] args) {
int sum = sum(5,6);//调用求和方法
System.out.println(sum);
}
//求和方法:
public static int sum(int a,int b){
//求和的逻辑
int sum = a+b;
return sum;
}
}
java中方法的种类
package day03;
/*
* java中方法的种类:
* 1.无参无返回值
* 2.无参有返回值
* 3.有参无返回值(常用)
* 4.有参有返回值(常用)
* 注意:
* 1.方法不调用不执行
* 2.方法没有返回值,返回值类型就写void,同时方法体里面的return可以省略不写
* 3.方法指定了返回值类型,就一定要写return 返回值
* 4.方法创建的时候指定了参数,那么在调用的时候,就一定要传参数,并且类型要匹配
* 5.如果有个参数,用逗号隔开,并且顺序不能乱
* 6.方法和方法是并列关系,不能嵌套定义,但是可以嵌套调用
* 记住下面的:
* 1.当有一个要用方法来解决的需求时,如何选择对应的方法?
* (如何写方法?要考虑以下两点)
* 1.明确返回值类型(考虑是否方法是否有返回值)
* 2.明确参数列表(考虑是否有需要参与运算的数据)
* 思考:求两个数的和?
*/
public class Demo03方法 {
public static void main(String[] args) {
sayNum(10,"张三");
}
//1.无参无返回值
public static void say() {
num();
System.out.println("HelloWorld");
return;//如果方法没有返回值,return可以不写,jvm会自动给你加return
}
//2.无参有返回值
public static int num() {
return 10;//指定了返回值类型,就一定要写返回值
}
//3.有参无返回值
public static void sayNum(int a,String name) {//int a = "张三"
System.out.println(a);
}
//4.有参有返回值
public static int showNum(int a) {
return a;
}
}
练习
package day03;
public class Demo04练习 {
/*
* 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...){
方法体语句;
return 返回值;
}
* 1.当有一个要用方法来解决的需求时,如何选择对应的方法?
* (如何写方法?要考虑以下两点)
* 1.明确返回值类型(考虑是否方法是否有返回值)
* 2.明确参数列表(考虑是否有需要参与运算的数据)
*/
public static void main(String[] args) {
//1.封装一个方法求出两个数的最大值
//2.封装加减乘除的方法
//3.封装一个方法,动态打印三角形,
// 输入1,就打印正的,输入2打印倒的,行数可以控制
printSJ(2,60);
//4.封装数组的打印(toString)方法
//5.封装数组的排序(sort)方法
//以上的方法封装完成后,都要在main中调用,看结果
}
//3.封装一个方法,动态打印三角形,
// 输入1,就打印正的,输入2打印倒的,行数可以控制
public static void printSJ(int a,int rows) {//int rows = 60
switch(a) {
case 1:
System.out.println("正的三角形"+rows);
zSJ(rows);//正三角形
break;
case 2:
System.out.println("倒的三角形"+rows);
dSJ(rows);//倒三角形
break;
default:
System.out.println("输入不合法");
}
}
private static void dSJ(int rows) {
for(int i=0;i<rows;i++) {//外层循环控制行数
for(int j=0;j<rows-i;j++) {//内层循环控制列数
System.out.print("*");//相当于一个tab键
}
System.out.println();//作用换行
}
}
//方法抽取:选中你要抽取成方法的代码 alt+shift+m 给它起名字
private static void zSJ(int rows) {//int rows = rows
for(int i=0;i<rows;i++) {//外层循环控制行数
for(int j=0;j<i+1;j++) {//内层循环控制列数
System.out.print("*");//相当于一个tab键
}
System.out.println();//作用换行
}
}
}
方法重载(overload)
package day03;
/*
* 重载(overload):方法名相同,参数列表不同
* 优势:
* 1.偷懒不用再花心思去给相同逻辑的方法起一个新的名字
* 2.简化代码,提高代码的可读性
* 重载的分类?
* 1.参数的个数不同
* 2.参数的类型不同
* 3.参数的顺序不同(算重载,实际生产不用,没有意义)
*/
public class Demo05方法重载 {
public static void main(String[] args) {
//求2个数的和
sum(3,4);
//求3个数的和
sum(3,4,5);
}
/*
* 如何写一个方法:
* 1.明确返回值类型
* 2.明确参数列表
*/
public static int sum(int a,int b) {
return a+b;
}
public static String sum(int a,String b) {
return a+b;
}
public static double sum(double a,double b) {
return a+b;
}
public static int sum(int a,int b,int c) {
return a+b+c;
}
}
面向对象(封装)
package day03;
/*
什么是面向过程
强调的是过程
第一步做什么,第二步做什么
什么是面向对象
强调的是对象(对象里面第一步,第二步)
当你有一个需求的时候,你利用或者创建一个工具(对象)来完成这个需求
那么你和这个工具之间的关系就是面向对象
举例
洗衣服
面向对象的特点
1.可以将复杂的事情简单化
2.可以让我们从执行者成为指挥者(角色发生了转变)
面向对象的特征
1.封装(encapsulation)
2.继承(inheritance)
3.多态(polymorphism)
*/
public class Demo06面向对象 {
public static void main(String[] args) {
}
}
类和对象
package day03;
/*
* 类和对象
我们学习编程的目的是什么?
就是为了把我们日常生活中的事物用编程语言描述出来,并解决一些问题
我们如何描述现实世界中的事物呢?
属性:该事物描述的信息(举例:人类:姓名,性别,五官等)
行为:该事物的能做什么(举例:人类:说,跑,走,跳等)
java中最基本单位是类(class)
类
抽象出一类事物共有属性和行为的集合,简单的说就是对事物模糊概括,抽象的
对象
是该类具体的提现,简单说就是对类的精准实例化
定义类
其实就是定义类的成员(成员变量和成员方法)
成员变量:(是事物的属性)跟之前学的变量是一个概念,只不过写在类中,方法之外
成员方法:(是事物的行为)和之前的方法类似,只不过去掉static(static后期再说)
举例
类:学生
对象:具体的某个学生就是一个对象
举例:
类:王者荣耀坦克
对象:程咬金,猪八戒
类的使用
一个.java文件中可以有多个class,但是由public修饰的只能有一个
总结:
类和对象之间的关系:
对象就是对类的精准实例化,
就是让模糊的事物变得更加清晰。
java代码中,类封装好的工具
*/
//测试类
public class Demo07类和对象 {
public static void main(String[] args) {
//在java中创建任何的东西用new (实例化也是创建,声明也是创建)
Student stu = new Student();
stu.age = 23;
stu.name = "张三";
System.out.println(stu.age);
System.out.println(stu.name);
stu.eat();
stu.study();
}
}
/*
* 学生类
* 属性(成员变量):name,age
* 行为(成员方法): eat() study()
*/
class Student{
//成员变量:(是事物的属性)跟之前学的变量是一个概念,只不过写在类中,方法之外
String name;//null
int age;//成员变量,不用手动初始化,jvm会自动给它初始化
//成员方法:(是事物的行为)和之前的方法类似,只不过去掉static(static后期再说)
public void eat(){
System.out.println("吃饭功能");
}
public void study(){
System.out.println("学习功能");
}
}
练习
package day03;
//一个.java文件中可以有多个class,但是由public修饰的只能有一个
public class Demo08练习 {
/*
* 如何使用类???
* 创建对象(专业属于叫实例化)并使用
* 格式:
* 类名 对象名 = new 类名();
* 如何使用类中成员变量
* 对象名.变量名
* 如何使用类中成员方法
* 对象名.方法名(参数)
*/
/*
* 1.创建手机类,老师类,汽车类
* 2.实例化手机类,老师类,汽车类
* 并使用成员变量和成员方法
*/
public static void main(String[] args) {
}
}
class Car{
//成员变量(属性)
double price;
String brand;
//成员方法(行为)
public void run() {
System.out.println("开动");
}
}
class Teacher{
String name;
int age;
public void teach() {
System.out.println("教书");
}
}
class Phone{//类名首字母大写
double price;
String brand;
//打电话的功能
public void call(int phoneNum) {
System.out.println(phoneNum);
}
}
package day03;
/*
* 如何使用类???
* 创建对象(专业属于叫实例化)并使用
* 格式:
* 类名 对象名 = new 类名();
* 如何使用类中成员变量
* 对象名.变量名
* 如何使用类中成员方法
* 对象名.方法名(参数)
*/
public class Demo09类的使用 {
public static void main(String[] args) {
// 类名 对象名 = new 类名();
Cat cat = new Cat();
//对象名.变量名
cat.color = "white";
cat.legs = 4;
System.out.println("猫的颜色是"+cat.color);
System.out.println("猫的腿有"+cat.legs);
//对象名.方法名(参数)
cat.eat();
}
}
class Cat {
String color;
int legs;
public void eat() {
System.out.println("猫吃饭");
}
}
成员变量和局部变量区别
package day03;
/*
* 成员变量和局部变量区别:
* 1.在类中的位置不同
* 成员变量:类中方法外
* 局部变量:类中方法内
* 2.在内存中存放位置不一样(目前了解即可)
* 成员变量:在堆中(随着类的消失而消失)
* 局部变量:在栈中(会随着方法弹栈消失而消失)
* 3.初始值不同
* 成员变量,jvm给初始值
* 局部变量,需要手动给初始值
* 注意:
* 局部变量的名称和成员变量的名称和相同,
* 在使用方法的时候,会采用就近原则
* 基本数据类型变量有哪些?
* byte short int long char double float boolean
* 引用数据类型变量有哪些?
* String 数组 类,接口
*/
//测试类
public class Demo10成员变量和局部变量 {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
}
}
class Dog{
int legs;//成员变量
public void eat() {
String GT = "骨头";//局部变量
int legs = 2;//局部变量的名称和成员变量的名称和相同
System.out.println(GT);
System.out.println(legs);
}
}
形参和实参
package day03;
/*
* 形式参数(形参)
* 创建方法时,写在小括号里面的参数,要带数据类型的
* 实际参数(实参)
* 调用方法时,写在小括号里面的参数,不要带数据类型
* 类做形式参数(常用)
*/
public class Demo11形参和实参 {
public static void main(String[] args) {
Car c = new Car();
c.brand="奥迪";
c.price=500000.0;
showCar(c);//实际参数
}
//类做形式参数
public static void showCar(Car car) {//Car car = new Car();
System.out.println(car.brand+","+car.price);
}
//老师、手机按上面的写法来一遍
}
private关键字
package day03;
/*
* private关键字
private(私有)关键字特点
a:是一个权限修饰符
b:可以修饰成员变量和成员方法
c:被其修饰的成员只能在本类中被访问
案例演示
封装和private的应用:
把成员变量用private修饰
提供对应的getXxx()和setXxx()方法
private仅仅是封装的一种体现形式,不能说封装就是私有
*/
//测试类
public class Demo12private关键字 {
public static void main(String[] args) {
Hero h = new Hero();
//h.name;//被private修饰的成员只能在本类中被访问,在其他类中不能访问
//h.pingA();//被private修饰的成员只能在本类中被访问,在其他类中不能访问
Girl g = new Girl();
g.name = "Andy";
// g.age =123; //想在这里用age
g.setAge(1000000);
System.out.println(g.name+","+g.getAge());
}
}
class Girl{
String name;
private int age;//pirvate修饰了
//思考:为什么要在另外一个类中通过set get方法对age进行存 取 操作
//而不是直接用public修饰age????
//原因是可以在外面调用set方法的时候,在本类中添加自己的业务逻辑,
//以此来保障我们自己的业务数据安全
public void setAge(int num) {
num = 18;//写入自己的业务逻辑
age = num;
}
//取这个age
public int getAge() {
return age;
}
}
//类
class Hero{
private String name;//名字
private String job;//职业
private void pingA() {
System.out.println("平A");
}
//被private修饰的成员只能在本类中被访问
public void hit() {
name = "猪八戒";
job = "坦克";
System.out.println(name+","+job);
pingA();//调用上面的平A方法
}
}
this关键字
package day03;
/*
* this关键字
this关键字特点
代表当前对象的引用
案例演示
this的应用场景
用来区分成员变量和局部变量重名
*/
//测试类
public class Demo13this关键字 {
public static void main(String[] args) {
Boy b = new Boy();
b.setAge(-1000);
System.out.println(b.getAge());//0
}
}
class Boy{
//从现在开始,但凡写属性全部用private修饰
private int age;//成员变量
//往age里面存值
public void setAge(int age) {//局部变量
/*局部变量的名称和成员变量的名称和相同,
在使用方法的时候,会采用就近原则*/
age = 18;
//代表当前对象的引用
this.age = age;
}
//取age里面的值
public int getAge() {
return age;
}
}
this 关键字
this 关键字用于引用类的当前实例。 例如:
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp);
this.report();
}
void report() { }
}
在上面的示例中,this 关键字用于两个地方:
- this.employees.length:访问类 Manager 的当前实例的变量。
- this.report():调用类 Manager 的当前实例的方法。
此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。
练习
package day03;
public class Demo014练习 {
public static void main(String[] args) {
}
}
//创建一个类,要有私有属性,要有私有属性对应的set get方法
class Person{
private String idCard;//身份证
private String name;//姓名
private int age;//年龄
/*
* alt+shift+s r 勾选 点击generate
*/
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
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;
}
}
继承
package day03_1;
/*
* 继承
概念
让类和类之间产生父子关系
同时子类可以拥有父类的所有非私有属性和行为(方法)
好处
简化代码
让类和类之间建立了关系,是多态的前提
弊端
代码和代码的耦合度太高
语法
子类 extends 父类
注意事项:
1.java里面不允许多继承
2.继承是有传递性的
3.java中所有类的顶级父类就是Object(对象)
要么直接继承,要么间接继承
4.子类只能继承父类所有非私有的成员(成员变量、成员方法)
5.子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法
6.子类是通过引用父类属性地址来实现继承的!!
*/
public class Demo01继承 {
public static void main(String[] args) {
Cat c = new Cat();
c.color = "white";
c.legs = 4;
System.out.println(c.color);
System.out.println(c.legs);
c.eat();
// c.a;//a是Animal的私有属性,所以c调用不了
Tom t = new Tom();
t.eat();
}
}
//动物类(父类)
class Animal{
private int a;
String color;
int legs;
public void eat() {
System.out.println("吃饭");
}
}
//猫类(子类)可以拥有父类的所有非私有属性和行为(方法)
class Cat extends Animal{
}
//狗类(子类)可以拥有父类的所有非私有属性和行为(方法)
class Dog extends Animal{
}
/*
* Tom猫(孙子类)
*/
class Tom extends Cat{
}
方法重写
package day03_1;
/*
*方法重写
* 概念
子父类出现了一模一样的方法(注意:返回值类型可以是子父类,这个我们学完面向对象讲)
什么时候使用
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
这样,即沿袭了父类的功能,又定义了子类特有的内容。
注意事项
父类中私有方法不能被重写
因为父类私有方法子类根本就无法继承
子类重写父类方法时,访问权限不能更低,最好就一致
类重写父类方法的时候,最好声明一模一样。
*/
public class Demo02方法重写 {
public static void main(String[] args) {
Ios8 i = new Ios8();
i.siri();
}
}
//父类
class Ios7{
public void siri() {
System.out.println("speak English");
}
}
//子类
class Ios8 extends Ios7{
//方法重写
public void siri() {
//当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
super.siri();//super 表示ios8的父类
System.out.println("说中文");
}
}
构造方法
package day03_1;
/*
* 构造方法:
* 作用:
* 实例化对象,同时给成员变量初始化赋值
* new后会自动调用构造方法实例化对象(可以实现自动调用)
* 写法:
* public 类名(参数列表){}
* 注意:
* 1.无参构造可以不写,计算机会自动帮你写
* 2.如果你写了,就会用你写的构造方法(可能是无参的,可能是有参的)
* 那么原先计算机默认给的无参构造方法就会被删除
*
*
*/
public class Demo03构造方法 {
public static void main(String[] args) {
Man man1 = new Man();
man1.setName("Andy");
man1.setAge(23);
System.out.println(man1);
//Man [name=Andy, age=23]
//day03_1.Man@3498ed
}
}
/*
* 现在开始:
* 创建一个类:
* 所有成员变量都用private修饰
* 生成set get方法
* 生成有参 无参构造
* 后期我们用lombok插件,可以直接通过注解来自动生成上面所有的代码
*/
class Man{
private String name;
private int age;
//alt+shift+s o 回车 有参构造
//alt+shift+s c 回车 无参参构造
//alt+shift+s r 勾选 点击geneate set get方法
//alt+shift+s s 回车 toString方法
public Man(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Man() {
super();
}
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;
}
//重写Object类里面的toString方法,作用就是为了看到当前类里面具体的值,而不是堆中的地址
@Override
public String toString() {
return "Man [name=" + name + ", age=" + age + "]";
}
}
super关键字
/*
为什么经常会遇到有的构造函数会有super(),而有的却没有,其实super就比如 对数函数,log的底数为10,如果为10 ,我们可写可不写,如果不为10,那么我们就要加上底数
在子类构造方法中,super();是默认省略的,(super();的作用是继承父类构造方法)。记住:默认省略不是不存在,而是存在,不过被省略了。
因为super();存在,所以如果父类是有参构造方法,那问题就出现了,因为子类的构造过程中必须调用父类的构造方法,如果有参,那么不能省略。就要自己调用父类有参构造方法。
要讲super就不能不提this,下面从4个角度讲解一下super(需要对比的时候拿this对比一下,加深理解)
1. super关键字表示超(父)类的意思。this变量代表对象本身。
2. super访问父类被子类隐藏的变量或覆盖的方法。当前类如果是从超类继承而来的,当调用super.XX()就是调用基类版本的XX()方法。其中超类是最近的父类。
3.调用super() 父类构造函数的时候只能调用在子类构造函数的第一行
4.this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this。并且this只和特定的对象关联,而不和类关联,同一个类的不同对象有不同的this
*
*/
class A{
public A() {
System.out.println("A的无参数构造器");
}
public A(int num) {
System.out.println("A的有参数构造器,参数为:" + num);
}
}
class B extends A{
public B() {
System.out.println("B的构造器");
}
}
public class test {
public static void main(String[] args) {
B bb = new B();
}
}
结果是:
A的无参数构造器
B的构造器
//也就是子类默认会隐式调用父类的构造器,默认在B构造器第一行添加了super();
/************************************************************************************/
/*接着一个例子:*/
class A{
public A() {
System.out.println("A的无参数构造器");
}
public A(int num) {
System.out.println("A的有参数构造器,参数为:" + num);
}
}
class B extends A{
public B() {
super(123);
System.out.println("B的构造器");
}
}
public class test {
public static void main(String[] args) {
B bb = new B();
}
}
结果是:
A的有参数构造器,参数为:123
B的构造器
/*显示调用了父类的构造器,就不会隐式调用空构造器了*/
/************************************************************************************/
//最后一个例子:
class A{
public A(int num) {
System.out.println("A的有参数构造器,参数为:" + num);
}
}
class B extends A{
public B() {
System.out.println("B的构造器");
}
}
public class test {
public static void main(String[] args) {
B bb = new B();
}
}
/*此时A没有空构造器,B也没有显示调用A的构造器,默认隐式调用super A();
但是此时A的空构造器没有定义,所以会报错。*/
//综上所述,如果父类有空构造器,super()可以不写,但如果父类只有含参数的构造器,这个super(...)一定要写,并且要对应父构造器的参数,否则就会报错!
多态
package day04;
/*
* 多态
概念
多态就是事物存在的多种形态
举例 在不同的场景下,扮演不同角色
+ 左右两边是数字的时候就是算术运算符,左右两边有一边是字符串的时候就是连接运算符
水 固态叫冰,气态叫水蒸气
冰或者水蒸气本质都是水在现实世界中存在的多种形态
java中多态要满足3个条件
1.要有继承关系(有2个或者2个以上的类)
2.有方法重写(子类继承了父类的方法,但是不想用父类的方法体,就可以做方法重写)
3.向上造型(父类引用指向子类对象)
*/
//测试类
public class Demo01多态 {
public static void main(String[] args) {
//3.向上造型(父类引用指向子类对象)
// Animal a = new Cat();//猫是一只动物
// a.eat();//猫吃猫粮
// Animal d = new Dog();//狗是一只动物
// d.eat();//狗吃骨头
// Cat c = new Cat();
// c.eat();
// Dog dog = new Dog();
// dog.eat();
method(new Cat());
method(new Dog());
method(new Pig());
}
//实际开发中,多态一般是把父类作为形参写在方法中,用于接收子类对象
public static void method(Animal a) {//Animal a = ???; 动态加载
a.eat();
}
}
//1.要有继承关系(有2个或者2个以上的类)
class Animal{//动物类(父子)
public void eat() {
System.out.println("动物吃饭");
}
}
//猫类(子类)
class Cat extends Animal{
//2.有方法重写(子类继承了父类的方法,但是不想用父类的方法体,就可以做方法重写)
public void eat() {
System.out.println("猫吃猫粮");
}
}
//狗类(子类)
class Dog extends Animal{
//2.有方法重写(子类继承了父类的方法,但是不想用父类的方法体,就可以做方法重写)
public void eat() {
System.out.println("狗吃骨头");
}
}
class Pig extends Animal{
//2.有方法重写(子类继承了父类的方法,但是不想用父类的方法体,就可以做方法重写)
public void eat() {
System.out.println("猪吃饭");
}
}
注意事项
package day04;
/*
* java代码执行过程,是先编译,后运行
* xxx.java -- xxx.class -- 计算机执行00101010
多态的注意事项
1.多态中对成员变量的访问规则
编译看左边(父类),运行看左边(父类)
2.多态中对成员方法的访问规则
编译看左边(父类),运行看右边(子类)
3.多态中对静态方法的访问规则
编译看左边(父类),运行看左边(父类)
注意:多态中的静态方法,因为隶属类,所以不能算是方法重写
4. (父类引用)不能使用子类的特有属性和行为(多态的弊端)
必须得把父类引用做向下造型(强制转换)
*/
public class Demo02多态的注意事项 {
public static void main(String[] args) {
method(new 猪八戒());
// method(new 程咬金());
}
public static void method(坦克 t) {//坦克 t = new 猪八戒();
//静态方法:编译看左边(父类),运行看左边(父类)
/*
* static修饰的方法和变量,隶属类
* 因为隶属于类,所以可以直接通过类名.出来,
* 不用创建对象就可以直接调用
*/
t.staticMethod();//坦克.staticMethod();
坦克.staticMethod();
//成员方法:编译看左边(父类),运行看右边(子类)如果右边没有,就运行左边(方法继承)
t.PingA();//猪八戒平A
//编译看=左边(父类),运行看=左边(父类)
//坦克 t = 猪八戒();
System.out.println(t.legs);//4 思考:父引用如何调用子类的成员变量?
// int a = (int) 2.34;//基本数据类型的强制转换
//向下造型,其实就相当于是基本数据类型的强制类型转换
//做判断只有t指向的是猪八戒的时候,才把t强转成猪八戒
//java中判断某个引用是否指向的是某个对象的时候,用instanceof
if(t instanceof 猪八戒) {
猪八戒 z = (猪八戒) t;// (猪八戒) 程咬金 ClassCastException
System.out.println(z.legs);//2
z.圈养时刻();//猪八戒的大招:圈养时刻
}else {
System.out.println("程咬金");
}
}
}
/*
* 创建一个坦克类 (父类)
* 创建 2个子类 猪八戒 程咬金 继承父类
* 都一个方法:平A
* 两个子类重写平A方法
* 在测试类中分别创建子类对象,调用平A方法
*/
class 坦克{
//成员变量
int legs = 4;//4条腿
//成员方法
public void PingA() {
System.out.println("平A");
}
//父类的静态方法
public static void staticMethod() {
System.out.println("父类静态方法.......");
}
}
class 猪八戒 extends 坦克{
int legs = 2;
public void PingA() {
System.out.println("猪八戒平A");
}
//猪八戒的大招
public void 圈养时刻() {
System.out.println("猪八戒的大招:圈养时刻");
}
//子类的静态方法
public static void staticMethod() {
System.out.println("子类静态方法.......");
}
}
class 程咬金 extends 坦克{
public void PingA() {
System.out.println("程咬金平A");
}
//程咬金大招
public void 正义潜能() {
System.out.println("程咬金大招:正义潜能");
}
}
练习
package day04;
/*
* 练习:
* 编写父类汽车类
* 1.属性:发送机缸数 4
* 2.行为:跑
* 编写子类(奔驰6缸 宝马8缸 奥迪12缸)
* 1.属性:独有的
* 2.行为:独有的跑
* 测试类:
* 多态调用各个子类的属性和行为,输出在控制台
* 多态的好处和弊端:
* 好处
* 1.提高了代码的复用性,简化代码
* 2.多态父类引用可以作为参数,接收任意子类对象
* 弊端
* 1.父类引用不能直接使用子类特有的属性和行为
*/
public class Demo03练习 {
public static void main(String[] args) {
}
}
抽象类
package day04_01;
/*
* 抽象类:
* 概念:
* 就是用abstract关键字修饰的类。更加抽象,完全不懂的类
* 特点:
* 1.抽象类中,可以包含
* 抽象方法
* 成员方法
* 成员变量
* 常量(指final修饰的变量)
* 构造方法(思考抽象类不能实例化为什么要有构造方法)
* 就是用于子类可以访问父类数据的初始化操作
* 2.一个类如果继承了抽象类,必须要重写抽象类中的抽象方法
* 3.抽象类,因为抽象,所以不能实例化
* 4.抽象类的子类:
* 1.要么是抽象类(注意下就可以)
* 2.要么是重写了抽象类中抽象方法的类(常用的)
* 思考一个问题:
* 抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类?对
* 在抽象类中什么时候写抽象方法,什么时候写成员方法?
* 当业务需要子类对共有方法进行重写的时候,用抽象方法
* 当业务需要子类直接继承的方法时候,可以用成员方法
* 5.抽象类强制要求子类做的事情(抽象方法)
* 6.abstract不可以修饰成员变量,没有意义,会报错
*/
public class Demo01抽象类 {
public static void main(String[] args) {
Cat c = new Tom();//Tom是一只猫
c.eat();//Tom吃Jerry
c.sleep();
//抽象类不能实例化
//Cat c1 = new Cat();
}
}
//猫类(父类)
abstract class Cat{//抽象类
final int a=1;//成员变量
// abstract int legs;//报错
//抽象方法(存在的目的就是为了让子类重写)
public abstract void eat();
//成员方法(存在的目的就是为了让子类继承)
public void sleep() {
System.out.println("猫睡觉");
}
public Cat() {//思考:抽象类既然不能实例化,为什么有构造方法?
System.out.println("抽象类Cat的构造方法.....");
}
}
//Tom(子类)
class Tom extends Cat{
@Override
public void eat() {
System.out.println("Tom吃jerry");
}
}
接口
接口定义
package day04_01;
/*
* 接口:(非常常用的)
* 概念:
* 接口就是一个特殊的抽象类
* 包含:
* 抽象方法
* 常量(指final修饰的变量)
* 接口是种强制性的约束
* 语法:
* 定义接口:
* public interface 接口名
* 子类实现接口
* 类名 implements 接口名
* 注意事项:
*/
public class Demo02接口 {
public static void main(String[] args) {
}
}
//父接口
interface Dog{
final int a=10;//常量
int b=10;//常量
//抽象方法
public abstract void eat();
//因为接口里面全是抽象方法,所以可以偷懒,abstract省略
public void sleep();
//思考:接口中是否有构造方法???请问为什么?
// public Dog(){}//1.接口里面全是抽象方法 2.接口里面没有成员变量,只有常量,所以不需要构造来给他初始化
}
class 哮天犬 implements Dog{
@Override
public void eat() {
System.out.println("狗吃饭");
}
@Override
public void sleep() {
System.out.println("狗睡觉");
}
}
接口练习
package day04_01;
/*
* 封装一个增删改查的接口
* 任何业务都离不开增删改查
* 比如 用户管理 车辆管理 部门管理等等
* 那么就考虑是否可以抽取共性的增删改查出来做一个接口,或者抽象类
*/
public class Demo03练习 {
public static void main(String[] args) {
//测试调用
//车辆调用
CarMapper cm = new CarMapper();
findAll(cm,"admin","admin123");
updateById(cm, 123);
deleteById(cm, 123);
}
public static void findAll(BaseMapper bm,String username,String password) {
if(bm instanceof UserMapper) {
if(username!=null) {//当传了账号密码,就走登录
UserMapper u = (UserMapper) bm;
u.login(username,password);
}else {//没传就走查询所有用户信息
bm.findAll();
}
}else {
bm.findAll();
}
}
public static void deleteById(BaseMapper bm,int id) {
bm.updateById(id);
}
public static void updateById(BaseMapper bm,int id) {
bm.deleteById(id);
}
}
//通用增删改查的接口
interface BaseMapper{
//查询所有信息(因为是模拟所以不写返回值)
public void findAll();
//根据id修改信息
public void updateById(int id);
//根据id删除信息
public void deleteById(int id);
}
//对车辆进行查询 修改 删除逻辑
class CarMapper implements BaseMapper{
@Override
public void findAll() {
System.out.println("查询所有车辆的业务逻辑");
}
@Override
public void updateById(int id) {
System.out.println("根据id更新车辆的业务逻辑");
}
@Override
public void deleteById(int id) {
System.out.println("根据id删除车辆的业务逻辑");
}
}
//对用户进行查询 修改 删除逻辑
class UserMapper implements BaseMapper{
@Override
public void findAll() {
System.out.println("查询所有用户的业务逻辑");
}
@Override
public void updateById(int id) {
System.out.println("根据id更新用户的业务逻辑");
}
@Override
public void deleteById(int id) {
System.out.println("根据id删除用户的业务逻辑");
}
public void login(String username,String password) {
System.out.println("登录功能业务逻辑");
}
}
(default关键字)修饰的接口方法
-
在java8以后,接口中可以添加使用default或者static修饰的方法,在这里我们只讨论default方法,default修饰方法只能在接口中使用,在接口中被default标记的方法为普通方法,可以直接写方法体。
-
实现类会继承接口中的default方法
-
如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法
为什么要重写呢?是因为,类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方法。
-
/*如果接口A中有default方法:*/
public interface A {
public default void a(){
System.out.println("这是A");
}
}
//Test类实现接口A:
public class Test implements A{
}
//那么Test类将会继承接口A中的a方法:
public class Test2 {
public static void main(String[] args) {
Test t = new Test();
t.a();
}
}
接口总结
package day04_01;
/*
* 接口的关系总结:
* 类和类之间 类和接口之间 接口和接口之间是什么关系
* 1.类和类之间 extends
* 继承关系:java中只允许单一继承,但是可以多层继承(继承有传递性)
* 2.类和接口之间 implements
* 实现关系:java中允许多实现(一个类允许实现多个接口)
* 语法:实现多个接口,用逗号隔开
* 3.接口和接口之间 extends
* 继承关系
* 思考: java中是否有多继承?
* 1.java中类和类没有多继承,但是接口和接口是有多继承的。
*/
public class Demo04接口的总结 {
public static void main(String[] args) {
}
}
interface InterA{
public void printA();
}
interface InterB{
public void printB();
}
//java中有没有多继承????有
interface InterC extends InterA,InterB{
public void printC();
}
//一个类继承另外一个类,同时可以实现多个接口
class Demo extends Object implements InterA,InterB{
@Override
public void printA() {
}
@Override
public void printB() {
}
}
内部类
package day04_01;
/*
* java内部类:
* 概念:
* 类中类就是内部类,说白了就是在一个类中再定义一个类
* 里面的就是内部类,外面的就是外部类
* 内部类也只是封装的一种体现形式,相对安全
* 种类:(4种)
* 1.成员内部类(类似成员变量,写在类中方法外)
* 2.局部内部类(类似局部变量,写在类中方法里)
* 3.静态内部类 (写在类中用static修饰)
* 4.匿名内部类(没有名字的内部类,常用)
* 语法:看代码
* 访问特点:
* 1.内部类可以访问外部类的所有成员(属性和行为,私有的也可以)
* 2.外部类要访问内部类的成员,必须创建内部类的对象
*
*/
//测试类
public class Demo05内部类 {
public static void main(String[] args) {
Outer out = new Outer();
out.method();//20 10
//在测试类中,是不可以直接创建内部类对象的,必须要通过外部类来创建和访问
// Inner in = new Inner();
}
}
//外部类
class Outer{
//外部类的成员变量
private int num = 20;
//外部类的成员方法
public void method() {
Inner in = new Inner();
in.method();
System.out.println(in.a);
}
//成员内部类 也是隶属与外部的一个成员,所以可以直接用私有
class Inner{
private int a = 10;
//内部类的成员方法
public void method() {
// 1.内部类可以访问外部类的所有成员(属性和行为,私有的也可以)
System.out.println(num);
}
}
}
局部内部类
package day04_01;
/*
* 局部内部类:
* 写在外部类中的成员方法中
*/
public class Demo06局部内部类 {
public static void main(String[] args) {
Outer2 out = new Outer2();
out.method();
}
}
//外部类
class Outer2{
//外部类的成员方法
public void method() {
int num = 20;//局部变量(类中方法内)
class Inner2{//局部内部类:写在外部类中的成员方法中
public void print() {
System.out.println(num);
}
}
//只能在这里创建局部内部类的对象
new Inner2().print();
}
public void method2() {
// new Inner2().print();//报错,作用域的问题,跟局部变量是一样的
}
}
静态内部类
package day04_01;
/*
* 静态内部类
* static修饰的
*/
public class Demo07静态内部类 {
public static void main(String[] args) {
new Outer3.Inner3().method();
//通过外部类直接调用静态内部类的静态方法
Outer3.Inner3.print();
}
}
class Outer3{
static int a;//隶属类的,可以通过直接类名.
//静态内部类
static class Inner3{
public void method() {
System.out.println("静态内部类的成员方法");
}
public static void print() {
System.out.println("静态内部类的静态方法");
}
}
}
匿名内部类
package day04_01;
/*
* 匿名内部类(常用)
* 没有名字的内部类
* 前提:
* 要先存值一个类(可以是普通类,也可以是抽象类或者接口)
*/
public class Demo08匿名内部类 {
public static void main(String[] args) {
Outer4 o = new Outer4();
o.method();
}
}
//要先存在一个类(可以是普通类,也可以是抽象类或者接口)
interface Inter{
public void print();
}
class Outer4{
//定义一个有名字的内部类实现Inter接口并重写它的抽象方法
// class Inner4 implements Inter{
// @Override
// public void print() {
// System.out.println("print打印方法");
// }
// }
//调用print方法如何调用???
public void method() {
//new Inner4().print();
//直接通过匿名内部类取调用print方法
Inter i = new Inter(){//变相的创建Inter接口的实例(对象)
@Override
public void print() {
System.out.println("print方法");
}
//成员方法,编译看左边,运行看右边
public void show() {//废话 没用的代码,人家调不了
System.out.println("Inter接口子类的独有方法");
}
};
i.print();//print方法
//因为Inter子类的对象没有名字,只是一个{},
//所以在多态中,是无法向下造型的
//i.show();//??? 报错
}
}
练习
package day04_01;
//测试类
public class Demo09练习 {
public static void main(String[] args) {
//如何调用CatDemo中的method方法???
CatDemo c = new CatDemo();
c.method(new Cat1() {
@Override
public void eat() {
System.out.println("猫吃饭");
}});
}
}
//抽象类
abstract class Cat1{
//抽象方法
public abstract void eat();
}
class CatDemo{
public void method(Cat1 c) {
c.eat();
}
}
异常
package day04_01;
/*
* 异常:
* 概念:
* 就是java中的错误
* 分类:
* Throwable(所有异常的顶级父类)
* Error(错误)
* 服务器宕机,断电,数据库崩溃等等非人为因素,客观因素
* Exception(异常)
* 编译期异常
* javac xxx.java 编译.java文件的时候就报错了
* 爆红线一般都是编译错误
* 运行期异常
* java xxx.class 运行.class文件的时候报错了
* 这个错误一般都是逻辑错误
* 如何处理:
* jvm如何处理异常:
* 1.main方法遇到了一个异常,可以自己处理
* 就是让程序员去处理
* 2.main方法遇到了一个异常,它自己处理不了,
* 就交还给jvm取处理,jvm就在控制台打印出异常信息,
* 并且停止程序继续向下运行
*/
public class Demo10异常 {
public static void main(String[] args) {
// System.out.println('123');//编译时异常
System.out.println(10/0);//ArithmeticException运行时异常
System.out.println("HelloWorld");
}
}
package day04_01;
/*
* 1.main自己处理,不交还给jvm,而是交给程序员去处理
* 1.try...catch
* 2.throws
*/
public class Demo11异常的处理 {
public static void main(String[] args) {
/*
* try{
* 可能出现异常的代码
* }catch(可能出现的异常类型 e){
* 补救措施,就是异常出现后,写处理异常的业务逻辑
* }finally{
* 一般用于释放资源,关闭连接,关闭流
* }
* 执行流程:
* 1.try里面的代码一旦出现了运行时异常
* 2.就会执行catch里面的代码
* 3.finally里面的代码,是一定会执行的,不管走的try,还是catch
* 有一种情况不会执行,就是jvm退出(概率很低)
*/
try {
System.out.println(10/0);
System.exit(0);//退出虚拟机(一般不这写,概率很低,了解即可)
} catch (Exception e) {
System.out.println("补救措施:0不能做分母");
}finally {
System.out.println("释放资源");
}
}
}
处理异常的第二种方式:throws
package day05;
/*
* 处理异常的第二种方式:throws
* 概念
* 在创建方法时,把可能出现的异常通过throws关键字暴露出来
* 让调用者去处理。
* 如何使用
* 在创建方法的时候,在后面小括号写上throws 异常类型
* 注意事项
* 当一个方法抛出异常后,谁调用这个方法,谁就要处理这个异常
* 思考:
* 什么时候用try catch 什么用throws?
* 你能够预估到这段代码将来出现的错误,并有解决方案的时候
* 用try catch
* 你能够预估到这段代码将来出现的错误,没有解决方案的时候
* 或者你根本预估不到将来出现什么错误,就throws
*/
//测试类
public class Demo01Throws关键字 {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(180);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(p.getAge());
}
}
class Person{
private String name;
private int age;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
//ctrl+shift+s r
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// 在创建方法的时候,在后面小括号写上throws 异常类型
public void setAge(int age)throws Exception{
//编写控制年龄范围的代码
if(age<0||age>130) {//180
System.out.println("年龄超出正常范围.....");
//抛出运行时异常
// throw new Exception();//
// throw new RuntimeException();//防止编译错误
}else {
this.age = age;
}
}
}
集合
package day05;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/*
* 回顾数组:相同数据类型元素的集合(可以存引用和基本),数组长度不可变
* 数组只能扩容,但是扩容是把原数组迁移到一个新数组里面(操作很麻烦)
*集合:可灵活变化长度的数据的集合
*思考:数组和集合的区别是什么?优劣势?
* 数组:
* 可以存基本数据类型,引用数据类型
* 数组的长度不可以变化,一创建就是固定的
* 数据效率更高
* 数组获取长度是通过length属性
* 集合:
* 可以存基本数据类型(存基本数据类型的时候,存的包装类),引用数据类型
* 可以存储不同的数据类型元素
* int Integer 为什么要有包装类,解决基本数据类型面向对象的问题
* 集合长度是可以变化的
* 集合相对数组来说,涉及复杂的算法,效率相对数组会低一些
* 集合获取长度是通过size()方法
* 思考:
* 什么时候使用数组,什么时候使用集合?
* 当数据长度不确定的时候,可以用集合,否则就可以用数组
* 类似mysql char(定长) varchar(变长)
* 语法:
* 关键字:Collection(单例集合顶级接口,接不能实例化)
* 子接口和子类
* List集合(接口)
* ArrayList集合 底层是数组
* LinkedList集合 底层是链表
* Vector集合
* Set集合(接口)
* HashSet集合 底层基于Hash算法
* TreeSet集合 底层基于二叉树算法
*/
public class Demo02集合 {
public static void main(String[] args) {
//父引用指向子类对象(多态向上造型:编译看左边,运行看右边)
Collection c = new ArrayList();
System.out.println(c);//[]
//往集合里面去追加 a b 1 true
c.add('a');
c.add("b");
c.add(1);
c.add(true);
System.out.println(c);//[a, b, 1, true]
System.out.println(c.size());//4
System.out.println(c.isEmpty());//false
boolean isC = c.contains("b");
System.out.println(isC);//true
Object[] o = c.toArray();
System.out.println(Arrays.toString(o));//[a, b, 1, true]
//遍历集合(Collection不能直接通过for循环遍历,需要转成数组)
for(int i=0;i<o.length;i++) {
System.out.println(o[i]+"100");
}
//遍历集合(通过迭代器Iterator)
Iterator it = c.iterator();
while(it.hasNext()) {//判断集合中是否还有元素,有就true,没有false
System.out.println(it.next()+"100");
}
//创建一个学生类,包含姓名年龄(private修饰)
//实例化3个学生对象存入集合,
//再取出来给每个人的年龄加3,并打印+3后的结果
Collection cS = new ArrayList();
cS.add(new Student("张三",23));
cS.add(new Student("李四",24));
cS.add(new Student("王五",25));
System.out.println(cS);
//[Student [name=张三, age=23], Student [name=李四, age=24], Student [name=王五, age=25]]
Iterator its = cS.iterator();
while(its.hasNext()) {//判断集合中是否还有元素,有就true,没有false
Student s = (Student) its.next();//Object是Student的父类向下造型(强转)
System.out.println(s.getAge()+3);
}
//新增一个学生(学习方法),一个老师(教书方法)
//实例化一个学生,一个老师 存入集合中
//遍历集合调用学生的学习方法,老师的教书方法
Collection c2 = new ArrayList();
c2.add(new Teacher());
c2.add(new Student("Andy",23));
Iterator it2 = c2.iterator();
while(it2.hasNext()) {//判断集合中是否还有元素,有就true,没有false
Teacher t = (Teacher) it2.next();
t.teach();
Student s = (Student) it2.next();
s.study();
}
}
}
class Teacher{
public void teach() {
System.out.println("老师教书.....");
}
}
class Student extends Object{
private String name;
private int age;
public void study() {
System.out.println("学生学习.....");
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
Collection常用方法:
add():向集合中添加一个元素,成功添加则返回true
size():返回当前集合的元素个数
isEmpty():判断当前集合是否为空集.当且仅当size=0时返回true
clear():清空集合
contains():判断集合是否包含给定元素
remove():从集合中删除给定元素,成功删除返回true
addAll():将参数给定的集合中的所有元素添加到当前集合中,添加后当前集合发生改变则返回true
containsAll():判断当前集合中是否包含参数集合中的所有元素
retainAll():取交集
removeAll():删交集
public class CollectionDemo1 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one"); //给集合添加元素
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c); //Collection中重写了Object的toString()方法
int size = c.size(); //获取集合的元素个数
System.out.println("size:"+size); //5
boolean isEmpty = c.isEmpty(); //判断集合是否为空集(size为0表示为空集)
System.out.println("是否为空集:"+isEmpty); //false
c.clear(); //清空集合
System.out.println("集合已清空");
System.out.println(c); //[]
System.out.println("size:"+c.size()); //0
System.out.println("是否为空集:"+c.isEmpty()); //true
}
}
public class CollectionDemo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
c.add(new Point(9,0));
c.add(new Point(1,2));
/*
集合重写了Object的toString()方法,格式如下:
[元素1.toString(), 元素2.toString(), 元素3.toString(), ...]
*/
System.out.println(c);
Point p = new Point(1,2);
/*
boolean contains(Object o)
判断当前集合是否包含给定元素,判断依据是给定元素是否与集合元素存在equals比较为true的情况
*/
boolean contains = c.contains(p);
System.out.println("是否包含:"+contains); //true
/*
boolean remove(Object o)
从集合中删除与给定元素equals比较为true的元素,若存在重复元素则只删除一次
*/
c.remove(p);
System.out.println(c);
}
}
class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
/**
* 集合是存放的是元素的引用
*/
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c = new ArrayList();
Point p = new Point(1,2);
c.add(p); //将p添加到集合c中
System.out.println("p:"+p); //(1,2)
System.out.println("c:"+c); //[(1,2)]
p.setX(100);
System.out.println("p:"+p); //(100,2)
System.out.println("c:"+c); //[(100,2)]
}
}
/**
* 集合间操作的演示
*/
public class CollectionDemo2 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c++");
c1.add(".net");
System.out.println("c1:"+c1); //[java, c++, .net]
Collection c2 = new ArrayList();
c2.add("android");
c2.add("ios");
c2.add("java");
System.out.println("c2:"+c2); //[android, ios, java]
/**
* boolean addAll(Collection c)
* 将参数给定的集合中的所有元素添加到当前集合中,添加后当前集合发生改变则返回true
*/
c1.addAll(c2); //将c2添加到c1中
System.out.println("c1:"+c1); //[java, c++, .net, android, ios, java]
System.out.println("c2:"+c2); //[android, ios, java]
Collection c3 = new ArrayList();
c3.add("c++");
c3.add("android");
c3.add("php");
System.out.println("c3:"+c3); //[c++, android, php]
/*
boolean containsAll(Collection c)
判断当前集合中是否包含参数集合中的所有元素
*/
boolean contains = c1.containsAll(c3); //判断c1中是否包含c3
System.out.println("包含所有:"+contains);
//取交集,c1中仅保留c1与c3的共有元素,c3不变
//c1.retainAll(c3);
//System.out.println("c1:"+c1); //[c++, android]
//System.out.println("c3:"+c3); //[c++, android, php]
//删交集,将c1中与c3的共有元素删除,c3不变
c1.removeAll(c3);
System.out.println("c1:"+c1); //[java, .net, ios, java]
System.out.println("c3:"+c3); //[c++, android, php]
}
}
ArrayList
package day05;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/*
* List接口实现类ArrayList
* List接口在继承Collection接口后,又新增了很多自己独有的抽象方法
* 让ArrayList去重写
* 泛型:
* 好处:就可以把运行期的错误,提升到编译期
*/
public class Demo03ArrayList {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
list.add(new Student("Andy", 23));
list.add(new Student("Mike", 24));
list.add(new Student("Bluce", 25));
// list.add(new Teacher());//报错
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
Student stu = (Student) list.get(i);
System.out.println(stu.getAge() + 3);
stu.study();
}
}
}
嵌套集合
package day05;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* 集合可以存基本+引用,集合可以存集合?
* 创建一个小组(Dept)小组编号deptNo,小组名称dName
*存入一个部门的集合里面(一个部门里面有N个小组)
*3个部门每个部门里面3个小组
*把3个部门存入一个公司集合里面
*/
//测试类
public class Demo04嵌套集合 {
public static void main(String[] args) {
//创建3个部门集合
List<Dept> list1 = new ArrayList<Dept>();
list1.add(new Dept(1001,"1001组"));
List<Dept> list2 = new ArrayList<Dept>();
list2.add(new Dept(1001,"1001组"));
List<Dept> list3 = new ArrayList<Dept>();
list3.add(new Dept(1001,"1001组"));
//创建一个公司集合
List<List<Dept>> listC = new ArrayList<List<Dept>>();
listC.add(list1);
listC.add(list2);
listC.add(list3);
// System.out.println(listC);
//给每个部门编号进行+1操作
for (int i = 0; i < listC.size(); i++) {
// System.out.println(listC.get(i));
for(int j=0;j<listC.get(i).size();j++) {
System.out.println(listC.get(i).get(j).getDeptNo()+1);
}
}
}
}
class Dept{
//alt+shift+s o 有参构造
//alt+shift+s c 无参参构造
//alt+shift+s s toString
//alt+shift+s r setget方法
private int deptNo;
private String dName;
public int getDeptNo() {
return deptNo;
}
public void setDeptNo(int deptNo) {
this.deptNo = deptNo;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
@Override
public String toString() {
return "Dept [deptNo=" + deptNo + ", dName=" + dName + "]";
}
public Dept(int deptNo, String dName) {
super();
this.deptNo = deptNo;
this.dName = dName;
}
public Dept() {
super();
}
}
练习(菜单等级)
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class t03 {
public static void main(String[] args) {
List<MenuInfoVo> menuInfoVos = new ArrayList<>();
menuInfoVos.add(new MenuInfoVo(19, "学生信息管理", 1, 12, null));
menuInfoVos.add(new MenuInfoVo(16, "成绩分段", 1, 12, null));
menuInfoVos.add(new MenuInfoVo(15, "成绩管理", 1, 12, null));
menuInfoVos.add(new MenuInfoVo(13, "班级查询", 1, 12, null));
menuInfoVos.add(new MenuInfoVo(12, "班级管理", 0, null, null));
menuInfoVos.add(new MenuInfoVo(17, "考试记录", 1, 8, null));
menuInfoVos.add(new MenuInfoVo(11, "试卷管理", 1, 8, null));
menuInfoVos.add(new MenuInfoVo(10, "科目管理", 1, 8, null));
menuInfoVos.add(new MenuInfoVo(9, "试题管理", 1, 8, null));
menuInfoVos.add(new MenuInfoVo(5, "菜单管理", 1, 1, null));
menuInfoVos.add(new MenuInfoVo(4, "角色菜单管理", 1, 1, null));
menuInfoVos.add(new MenuInfoVo(3, "密码修改", 1, 1, null));
menuInfoVos.add(new MenuInfoVo(2, "用户管理", 1, 1, null));
menuInfoVos.add(new MenuInfoVo(1, "系统管理", 0, null, null));
menuInfoVos.add(new MenuInfoVo(8, "考试管理", 0, null, null));
List<MenuInfoVo> listA = new ArrayList<>();
List<MenuInfoVo> listB = new ArrayList<>();
/*for(MenuInfoVo ma : menuInfoVos){ //普通forecch遍历
if(ma.getMenuDigree().equals(0)){
listA.add(ma);
}
}*/
/*menuInfoVos.forEach(menuInfoVo -> { //lambda表达式
if (menuInfoVo.getMenuDigree().equals(0)) listA.add(menuInfoVo);
if (menuInfoVo.getMenuDigree().equals(1)) listB.add(menuInfoVo);
});*/
//stream流的方式
listA = menuInfoVos.stream().filter(menuInfoVo -> menuInfoVo.getMenuDigree().equals(0)).collect(Collectors.toList());
listB = menuInfoVos.stream().filter(menuInfoVo -> menuInfoVo.getMenuDigree().equals(1)).collect(Collectors.toList());
System.out.println(listA);
// List<MenuInfoVo> listB = new ArrayList<>();
// for (MenuInfoVo mb : menuInfoVos){
// if(mb.getMenuDigree().equals(1)){
// listB.add(mb);
// }
// }
System.out.println(listB);
List<MenuInfoVo> menuEnd = new ArrayList<>();
for(MenuInfoVo la : listA){
List<MenuInfoVo> shus = new ArrayList<>();
for (MenuInfoVo lb : listB){
if (lb.getParentId().equals(la.getMenuId())){
shus.add(lb);
}
}
la.setSubs(shus);
menuEnd.add(la);
}
System.out.println(menuEnd);
}
}
class MenuInfoVo {
private int menuId; //菜单Id
private String title; //菜单名称
private Integer menuDigree; //菜单层级
private Integer parentId; //父Id
private List<MenuInfoVo> subs; //菜单集合
public MenuInfoVo() {
}
public MenuInfoVo(int menuId, String title, Integer menuDigree, Integer parentId, List<MenuInfoVo> subs) {
this.menuId = menuId;
this.title = title;
this.menuDigree = menuDigree;
this.parentId = parentId;
this.subs = subs;
}
public int getMenuId() {
return menuId;
}
public void setMenuId(int menuId) {
this.menuId = menuId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getMenuDigree() {
return menuDigree;
}
public void setMenuDigree(Integer menuDigree) {
this.menuDigree = menuDigree;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public List<MenuInfoVo> getSubs() {
return subs;
}
public void setSubs(List<MenuInfoVo> subs) {
this.subs = subs;
}
@Override
public String toString() {
return "MenuInfoVo{" +
"menuId=" + menuId +
", title='" + title + '/'' +
", menuDigree=" + menuDigree +
", parentId=" + parentId +
", subs=" + subs +
'}';
}
}
泛型
package day05;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* 泛型:
* 概念(好处)
* 1.将运行期的错误提升到了编译期(可以提早的发现逻辑问题)
* 2.省去强转的麻烦
* 如何使用
* <>里面放的必须是引用数据类型
* 注意事项
* 1.前后泛型必须保持一只,后面的泛型可以省略不写
* 一般来说建议前后都写
* 2.泛型最好不要定义成Object,没有意义
* Object是所有class的顶级父类
* 思考:泛型可以写在什么地方?
* 1.泛型类
* 2.泛型接口
* 3.泛型方法
*/
//测试类
public class Demo05泛型 {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("张三",23));
// 把运行期的错误提升到了编译期
// list.add("HelloWorld");
Iterator<Person> it = list.iterator();
while(it.hasNext()) {
//省去强转的麻烦
Person p = it.next();
System.out.println(p.getName());
}
}
}
泛型类
package day05;
/*
* 泛型类:
* 修饰符 class 类名<代表泛型的变量>{}
* class Generic<T>{}
*泛型接口
* 修饰符 interface 接口名<代表泛型的变量>{}
* interface Inter<T>{}
*泛型方法(记住这个格式)
* 修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表){}
* private <T> T show(T a){}
*java中的泛型标记符:
* E element(元素,集合,数组里面存的都叫元素)
* T type(java类)
* K key(键)
* V value(值)
* N number(数值类型)
* ?-不确定的java类型(泛型通配符)
* A-Z都可以
*/
public class Demo06泛型类 {
public static void main(String[] args) {
Generic<Integer> g1 = new Generic<Integer>(123);
Generic<String> g2 = new Generic<String>("helloWorld");
System.out.println(g1.getKey());
System.out.println(g2.getKey());
/*
* 思考:定义的泛型类,一定需要传入泛型类型的实际参数吗?
* 并不是,在使用泛型类的时候,如果传入实际参数,就会根据传入的
* 泛型实际参数,做相应的限制,这个时候,泛型才会起到本应起到的作用
* 如果不传入泛型类型的实际参数,在泛型类中泛型方法或成员变量定义的类型
* 可以为任何类型
*/
Generic g3 = new Generic(123);
Generic g4 = new Generic(true);
Generic g5 = new Generic(1.23);
System.out.println(g3.getKey());
System.out.println(g4.getKey());
System.out.println(g5.getKey());
}
}
/*
* 泛型类:
* 修饰符 class 类名<代表泛型的变量>{}
* 通过泛型可以完成对一组类的操作对外开放相同的接口
* 最典型的就是各种容器类:List Set Map
*/
//这个是一个最普通的泛型类,T可以随便写,但是要有可读性
//注意:实例化泛型类的时候,要给T指定具体的类型,否则会有警告
class Generic<T>{
private T key;
//泛型类的构造方法形式参数key的类型也是T,T由调用者指定
public Generic(T key) {
this.key = key;
}
//泛型的get方法返回值类型也是T,T由调用者指定
public T getKey() {
return key;
}
}
/**
***如何实例化泛型类:
**/
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//这样传入的初始值就只能是Integer的类型了
泛型接口
package day05;
/*
* 泛型接口
* 修饰符 interface 接口名<代表泛型的变量>{}
*/
public class Demo07泛型接口 {
public static void main(String[] args) {
}
}
//泛型接口 和泛型类相似
interface Inter<T>{
public void show(T t);
}
//注意,一个类实现了泛型接口时,如果没有给接口的<>传入实际参数
//要在类名后面也写上泛型
class Demo<T> implements Inter<T>{//这种做法是不妥的,没必要给自定义类强制加泛型
@Override
public void show(T t) {
}
}
class Demo01 implements Inter<String>{//推荐使用
@Override
public void show(String t) {
}
}
泛型方法
package day05;
import java.util.Arrays;
/*
* 泛型方法(记住这个格式)
* 修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表){}
*概念:
* 大多数泛型类中的成员方法都使用了泛型
* 甚至泛型类中包含泛型方法
*注意:
* 1.public与返回值直接的<T>非常重要,有了它才是泛型方法
* 2.<T>表示该方法使用泛型类型T,此时才可以在方法中使用泛型
* 3.与泛型类定义一样,T可以随便写 E K V ?都可以
*泛型方法什么时候使用?
* 1.当你需要传递的参数类型不确定的时候,就可以用泛型方法
*思考:为什么不用Object接收???
* Object本身跟泛型就不是一个概念,Object要强转的
*/
public class Demo08泛型方法 {
public static void main(String[] args) {
//需求:封装一个方法
//给定任意类型的数组,和两个数组元素的下标,替换他们的位置
int[] arr = {1,2,3,4,5};
new Demo123().change(arr, 1, 3);
System.out.println(Arrays.toString(arr));
String[] arrStr = {"1","2","3","4","5"};
new Demo123().change(arrStr, 1, 3);
System.out.println(Arrays.toString(arrStr));
}
}
class Demo123<T>{
private <T> T show(T a){
System.out.println(a);
return null;
}
public void change(int[] arr,int firstIndex,int secondIndex) {
//替换位置
int temp = arr[firstIndex];
arr[firstIndex] = arr[secondIndex];
arr[secondIndex] = temp;
}
public <T> void change(T[] arr,int firstIndex,int secondIndex) {
//替换位置
T temp = arr[firstIndex];
arr[firstIndex] = arr[secondIndex];
arr[secondIndex] = temp;
}
}
泛型通配符<?>、上限、下限
package day05;
/*
* 泛型通配符<?>
* 任意类型,如果没有明确,就可以是Object或者任意java类
* <? super E>
* 表示类型的下界(下限)(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object;
* <? extends E>
* 表示类型的上界(上限),表示参数化类型的可能是T 或是 T的子类;
* 注:带有super超类型限定的通配符可以向泛型对象中写入,带有extends子类型限定的通配符可以向泛型对
* 象读取。
* 先了解(有兴趣可以网查下资料)
*/
public class Demo09泛型通配符 {
public static void main(String[] args) {
}
}
可变参数
语法:参数类型… 参数,表示可以传0~n个参数。…是数组作为方法参数的一种灵活替换的写法。
例如:public void (String … str){}
Lambda表达式
package test03;
/*
* Lambda之前,先回顾java的方法??
* 方法的格式
修饰符 返回值类型 方法名(参数列表....){
方法体语句;
return 返回值;
}
1.修饰符:public static(其他的后期在讲)
2.返回值类型:方法(功能)最终的结果的数据类型 注意:没有结果就写void
3.方法名:满足规范规定即可,方便调用
4.参数类型:参数的数据类型
5.参数列表
6.方法体语句:具体完成功能的代码
7.return:结束方法
8.返回值:就是功能的结果,由return带给调用者
Lambda表达式,简写的格式,只关心参数列表,方法体,也就就是说修饰符,返回值类型,方法名都可以不要
为什么要这么写???先了解下函数式编程(面向过程)
函数式接口:
定义:一个接口,有且只有一个抽象方法,其他什么东西都不能有。
比如Runnable就是一个典型的函数式接口
而TimerTask就不是
Lambda格式:
(参数列表)->表达式
或者
(参数列表)->{代码块}
参数列表:就是方法里面的形式参数列表,可以不写数据类型直接写一个形参,jvm会自动根据值去判断
-> 可以理解为,参数被用于 xxx后面表达式,代码块
*
*/
public class Demo01Lambda {
public static void main(String[] args) {
//java多态的时候讲的吧
// Cat c = new Cat();
// Thread t = new Thread(c);
// t.start();//面试题:启动线程用run方法可以不可以,如果可以和start有什么区别???
//当我们学匿名内部类的时候
/*new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("瞄.......");
//让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();*/
//既然我们用的是匿名内部类,那new Runnable就可以不要了
/*Lambda格式:
(参数列表)->表达式
或者
(参数列表)->{代码块}
参数列表:就是方法里面的形式参数列表,可以不写数据类型直接写一个形参,jvm会自动根据值去判断
-> 可以理解为,参数被用于 xxx后面表达式,代码块*/
new Thread(()->{
while(true) {
System.out.println("瞄.......");
//让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
//用一个子类去实现了一个函数式接口
class Cat implements Runnable{//多线程的形式去执行
@Override
public void run() {//run就是线程要执行的业务
while(true) {
System.out.println("瞄.......");
//让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
函数式接口简化之Lambda
package test03;
/*
* java方法分类:
* 无参无返回值
* 无参有返回值
* 有参无返回值
* 有参有返回值
* 函数式编程接口
* 无参无返回值
* 无参有返回值
* 有参无返回值
* 有参有返回值
*Lambda表达式注意:
* 如果一个接口只有一个抽象方法,那么这个接口是函数式接口,可以用lambda来简化
* 如果我们在某个接口上声明了一个@FunctionalInterface注解,
* 那么编译器就会按照函数式接口的定义,来定义接口,如果你给接口中弄两个抽象方法,
* 那么程序编译就会报错。所以,从某种意义上来说,只要你保证接口中只有一个抽象方法,你可以不加这个注解
* 加上,编译期就会自动检测
* Lambda表达式语法还可以精简:显得非常有逼格,但是可读性就真的很差
* 1.参数类型,可以不写,jvm会自动根据入参匹配,如果多个参数,要么全写要么全不写
* 2.如果参数只有一个 () 可以不写
* 3.如果方法体中是一行代码,{}可以不写
* 4.如果方法体中只有一行代码,并且是返回值,return可以不写
*
*
*/
public class Demo02Lambda {
public static void main(String[] args) {
//无参无返回值
Dog dog = ()->{System.out.println("狗吃饭");};
dog.eat();
//无参有返回值
Person p = ()->{return 23;};
int age = p.getAge();
System.out.println("age="+age);//age=23
//有参无返回值
// Girl g = name->System.out.println("我的名字叫:"+name);//这么写也可以
Girl g = (name)->{
System.out.println("我的名字叫:"+name);
};
g.say("Andy");//我的名字叫:Andy
//有参有返回值
// Sum s = (int a,int b)->a + b;//这样写也是可以的
Sum s = (int a,int b)->{return a + b;};
int result = s.sum(3, 4);
System.out.println("result="+result);//result=7
}
}
//函数式编程接口
//无参无返回值
interface Dog{
void eat();//接口中默认都是public修饰抽象方法,所以public可以省略
}
//无参有返回值
@FunctionalInterface
interface Person{
int getAge();
// String getName();//打开就Person接口就报错
}
//有参无返回值
@FunctionalInterface
interface Girl{
void say(String name);
}
//有参有返回值
@FunctionalInterface
interface Sum{
int sum(int x,int y);
}
Lambda的变量捕获
package test03;
/*
* Lambda的变量捕获
*/
public class Demo03Lambda {
public static void main(String[] args) {
// Outer outer = new Outer();
// outer.method();
int age = 23;
Man man = ()->{
// age = 18;
return age;
};
int ManAge = man.getAge();
System.out.println("age="+ManAge);//age=23
}
}
/*
* 创建一个匿名内部类,重写并调用匿名内部类里面的print方法
* 打印一下变量a,这个a是外部类的局部变量,值为10
*/
interface Inter{
public void print();
}
class Outer{//外部类
public void method() {//成员方法
int a = 20;
// a = 20; //匿名内部类中,要求程序在执行过程中,如果使用变量是不允许改变的,类似常量概念
Inter i = new Inter() {
//a = 20;//匿名内部类中,要求程序在执行过程中,如果使用变量是不允许改变的,类似常量概念
@Override
public void print() {
System.out.println("a="+a);
}
};
i.print();
}
}
interface Man{
int getAge();
}
Lambda对集合的操作
package test03;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.logging.Logger;
/*
* Lambda对集合的操作
*/
public class Demo04Lambda {
public static void main(String[] args) {
//小tip:使用日志框架(slf4j log4j等等)
//jdk自带的日志工具
Logger log = Logger.getGlobal();
// 需求:创建一个集合,泛型为String,往里面存5个学生的名字
List<String> list = new ArrayList<String>();
list.add("Andy");
list.add("Mike");
list.add("Bluce");
list.add("Joy");
list.add("Eson");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
log.info(list+"");
log.info(String.valueOf(list));
}
System.out.println("==============华丽的分割线=====================");
for (String string : list) {
System.out.println(string);
}
System.out.println("==============华丽的分割线=====================");
list.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
System.out.println("==============Lambda华丽的分割线=====================");
list.forEach(a -> System.out.println(a));
System.out.println("==============ListSort华丽的分割线=====================");
listSort(list);
/*
* Map<Key,Value> key不能重复 value可以重复 根据key拿value 通过put往map集合里面去追加元素
*/
System.out.println("==============Map华丽的分割线=====================");
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "Andy");
map.put(2, "Mike");
map.put(3, "Bluce");
map.put(4, "Joy");
map.put(5, "Eson");
// 获取map里面的每个员工的名字
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer k, String v) {
System.out.println(k+"="+v);//禁止出现在源码中的,被扫描到了,扣钱
}
});
System.out.println("==============lambdaMap华丽的分割线=====================");
map.forEach((k,v)->System.out.println(k+"="+v));
}
// 集合排序的方法
private static void listSort(List<String> list) {
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
System.out.println(list);
System.out.println("==============lambda表达式写法华丽的分割线=================");
list.sort((a1, a2) -> a1.compareTo(a2));
System.out.println(list);
}
}
练习
import java.lang.reflect.Array;
public class t01 {
public static void main(String[] args) {
// Cat cat = new Cat();
// Thread t = new Thread(()->{
// while(true) {
// System.out.println("瞄.......");
// //让线程休眠
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// });
// t.start();
// C c = new c();
B b = new B(()->{
int i = 10;
for (;i>0;i--)System.out.println("B*********************");
});
b.start();
}
}
//用一个子类去实现了一个函数式接口
class Cat implements Runnable{ //多线程的形式去执行
@Override
public void run() {//run就是线程要执行的业务
while(true) {
System.out.println("瞄.......");
//让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 模拟(Runnable)函数式接口
*/
interface A {
void run ();
// void sho(); //函数式接口有且只能有一个抽象方法!!!,如果注释去掉,上面的Lambda表达式立马报错!
}
/**
* 模拟(Thread)线程类
*/
class B {
private A aa;
public B() {
super();
}
public B(A a) {
aa = a;
}
public void start(){ //模拟线程启动方法
if (aa == null) return;
aa.run();
}
}
/**
* 模拟继承Runnable接口
*/
class C implements A {
@Override
public void run() {
int i = 10;
for (;i>0;i--)System.out.println("B*********************");
}
}
方法和构造函数引用(Method and Constructor References)
- Java 8允许您通过
::
关键字传递方法或构造函数的引用。
构造引用
// Supplier<Student> s = () -> new Student();
Supplier<Student> s = Student::new;
对象::实例方法 Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应
// set.forEach(t -> System.out.println(t));
set.forEach(System.out::println);
类名::静态方法
// Stream<Double> stream = Stream.generate(() -> Math.random());
Stream<Double> stream = Stream.generate(Math::random);
类名::实例方法
// TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
/* 这里如果使用第一句话,编译器会有提示: Can be replaced with Comparator.naturalOrder,这句话告诉我们
String已经重写了compareTo()方法,在这里写是多此一举,这里为什么这么写,是因为为了体现下面
这句编译器的提示: Lambda can be replaced with method reference。好了,下面的这句就是改写成方法引用之后:
*/
TreeSet<String> set = new TreeSet<>(String::compareTo);
Stream (流)流式编程
package day08;
import java.util.ArrayList;
import java.util.List;
import day08.bean.User;
/*
* Stream java8新特性(流)
* 和javaString InputStream和OutputStream是完全不同的概念。
* java8中Stream对Collection(集合)对象的增强操作
*
*/
public class Demo02Stream集合 {
/*
* Collection(单列集合的顶级父接口)
* List接口
* ArrayList实现类
* LinkedList实现类
* Set接口
* HashSet实现类
*/
public static void main(String[] args) {
//创建一个学生集合,往里面存3个学生的信息,然后找出年龄小于25的学生,存入新的集合
List<User> list = new ArrayList<User>();
list.add(new User(1001,"Andy",23,80));
list.add(new User(1002,"Mike",24,90));
list.add(new User(1003,"Bluce",25,100));
List<User> newList = new ArrayList<User>();
//找出年龄小于25的学生,存入新的集合
for (User user : list) {
if(user.getAge()<25) {
// list.add(user);//ConcurrentModificationException,并发修改异常
// foreach底层是迭代器(迭代的过程中不允许对集合进行增删改操作)
//所以增强for循环只能用来查询,而普通for循环是可以用来操作集合的
newList.add(user);
}
}
// for (int i = 0; i < list.size(); i++) {
// if(list.get(i).getAge()<25) {
// list.add(list.get(i));//ConcurrentModificationException,并发修改异常
// }
// }
// [User [id=1001, name=Andy, age=23, score=80], User [id=1002, name=Mike, age=24, score=90]]
System.out.println(newList);
//找出成绩score大于等于90的学生信息
List<User> newList2 = new ArrayList<User>();
for (User user : list) {
if(user.getScore()>=90) {
newList2.add(user);
}
}
System.out.println(newList2);
//[User [id=1002, name=Mike, age=24, score=90], User [id=1003, name=Bluce, age=25, score=100]]
}
}
如何使用Stream
package day08;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import day08.bean.User;
/*
* Stream java8新特性(流)
* 和javaString InputStream和OutputStream是完全不同的概念。
* java8中Stream对Collection(集合)对象的增强操作
* 如何使用Stream
* 生成一个Stream流
* list.stream()
* 中间方法
* list.stream().filter(接口) 来对集合做处理
* 终结方法
* 收集处理后的结果
* list.stream().filter(接口).collect() 来对集合做处理
* 好处:
* 代码简洁,对集合操作搭配lambda表达式非常便利,可以并发处理集合数据,提高程序执行效率
* 集合和流的对比:
* 集合主要关心的是有效的管理和访问他们的元素
* 流不会直接操作元素的,只是提供了一种手段,不关心声明的描述,不关心元素的来源,只做计算
*/
public class Demo03Stream集合 {
public static void main(String[] args) {
//创建一个学生集合,往里面存3个学生的信息,然后找出年龄小于25的学生,存入新的集合
List<User> list = new ArrayList<User>();
list.add(new User(1001,"Andy",23,80));
list.add(new User(1002,"Mike",24,90));
list.add(new User(1003,"Bluce",25,100));
/*
* Predicate接口(函数式接口)
* java自带的,支持lambda表达式的API,里面有一些比较的方法
*/
// Predicate<User> p1 = (User user) -> {return user.getAge()<25;};//lambda表达式基本用法
Predicate<User> p2 = user->user.getAge()<25;//lambda表达式简写
Predicate<User> p3 = user->user.getScore()>=90;//lambda表达式简写
//年龄小于25的学生并且成绩大于等于90的学生信息
/*
* filter找出满足条件的对象,将期返回给最后的列表
* collect 是收集结果集的方法
* Collectors是一个操作集合的工具类
*/
List<User> newList = list.stream()
.filter(p2.and(p3))
// .filter(p3)
.collect(Collectors.toList());
System.out.println("新的List集合为:"+newList);
//[User [id=1002, name=Mike, age=24, score=90]]
List<String> strList = Arrays.asList("aaa","bbb","ccc");
strList.forEach((String str)->{
System.out.println(str);
});
//下面的写法是上面写法的简写。。。
strList.forEach(System.out::println);
}
}
正则表达式
正则表达式:
-
用于描述字符串的内容格式,使用它通常用于匹配一个字符串是否符合格式要求
-
正则表达式的语法:——————-了解、不用纠结、不用深入研究
1.[]:表示一个字符,该字符可以是[]中指定的内容 例如: [abc]:这个字符可以是a或b或c [a-z]:表示任意一个小写字母 [a-zA-Z]:表示任意一个字母 [a-zA-Z0-9]:表示任意一个字母数字 [a-zA-Z0-9_]:表示任意一个数字字母下划线 [^abc]:该字符只要不是a或b或c 2.预定义字符: .:表示任意一个字符,没有范围限制 /d:表示任意一个数字,等同于[0-9] /w:表示任意一个单词字符,等同于[a-zA-Z0-9_]----单词字符指:字母、数字和_ /s:表示任意一个空白字符 /D:不是数字 /W:不是单词字符 /S:不是空白字符 3.量词: ?:表示前面的内容出现0-1次 例如: [abc]? 可以匹配:a 或 b 或 c 或什么也不写 但是不能匹配:m或aaa +:表示前面的内容最少出现1次 例如: [abc]+ 可以匹配:b或aaaaaaaaaa...或abcabcbabcbabcba.... 但是不能匹配:什么都不写 或 abcfdfsbbaqbb34bbwer... *:表示前面的内容出现任意次(0-多次)---匹配内容与+一致,只是可以一次都不写 例如: [abc]* 可以匹配:b或aaaaaaaaaa...或abcabcba....或什么都不写 但是不能匹配:abcfdfsbbaqbb34bbwer... {n}:表示前面的内容出现n次 例如: [abc]{3} 可以匹配:aaa 或 bbb 或 aab 或abc 或bbc 但是不能匹配: aaaa 或 aad {n,m}:表示前面的内容出现最少n次最多m次 例如: [abc]{3,5} 可以匹配:aaa 或 abcab 或者 abcc 但是不能匹配:aaaaaa 或 aabbd {n,}:表示前面的内容出现n次以上(含n次) 例如: [abc]{3,} 可以匹配:aaa 或 aaaaa.... 或 abcbabbcbabcba.... 但是不能匹配:aa 或 abbdaw... 4.()用于分组,是将小括号里面的内容看做是一个整体 例如: (abc){3} 表示abc整体出现3次. 可以匹配abcabcabc 但是不能匹配aaa 或abcabc (abc|def){3}表示abc或def整体出现3次. 可以匹配: abcabcabc 或 defdefdef 或 abcdefabc 但是不能匹配abcdef 或abcdfbdef
String支持与正则表达式相关的方法:
-
matches():使用给定的正则表达式(regex)验证当前字符串的格式是否符合要求,若符合则返回true,否则返回false
public class MatchesDemo { public static void main(String[] args) { /* 邮箱正则表达式: [a-zA-Z0-9_]+@[a-zA-Z0-9]+(/.[a-zA-Z]+)+ 注意: /.中的这个/是正则表达式中的转义符 //.中的第1个/,是在转义正则表达式中的/ */ String email = "wangkj@tedu.cn"; String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(//.[a-zA-Z]+)+"; //使用regex匹配email是否符合格式要求 boolean match = email.matches(regex); if(match){ System.out.println("是正确的邮箱格式"); }else{ System.out.println("不是正确的邮箱格式"); } } }
-
replaceAll():将当前字符串中满足正则表达式(regex)的部分给替换为给定的字符串(s)
public class ReplaceAllDemo { public static void main(String[] args) { String line = "abc123def456ghi78"; line = line.replaceAll("[0-9]+","#NUMBER#"); System.out.println(line); } }
-
split():将当前字符串按照满足正则表达式(regex)的部分进行拆分,将拆分出的以String[]形式来返回
public class SplitDemo { public static void main(String[] args) { String line = "abc123def456ghi"; String[] data = line.split("[0-9]+"); //按数字拆分(数字就拆没了) System.out.println(Arrays.toString(data)); //将data数组转换为字符串并输出 line = "123.456.78"; data = line.split("//."); //按.拆(.就拆没了) System.out.println(Arrays.toString(data)); //最开始就是可拆分项(.),那么数组第1个元素为空字符串------------"" //如果连续两个(两个以上)可拆分项,那么中间也会拆出一个空字符串----"" //如果末尾连续多个可拆分项目,那么拆出的空字符串被忽略 line = ".123.456..78........"; data = line.split("//."); //按.拆(.就拆没了) System.out.println(Arrays.toString(data)); } }
JAVA IO
File类
File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径)
使用File可以做到:
- 1:访问其表示的文件或目录的属性信息,例如:名字,大小,修改时间等等
- 2:创建和删除文件或目录
- 3:访问一个目录中的子项
但是File不能访问文件数据.
public class FileDemo {
public static void main(String[] args) {
//使用File访问当前项目目录下的demo.txt文件
/*
创建File时要指定路径,而路径通常使用相对路径。
相对路径的好处在于有良好的跨平台性。
"./"是相对路径中使用最多的,表示"当前目录",而当前目录是哪里
取决于程序运行环境而定,在idea中运行java程序时,这里指定的
当前目录就是当前程序所在的项目目录。
*/
// File file = new File("c:/xxx/xxx/xx/xxx.txt");
File file = new File("./demo1.2.3.423.txt");
//获取名字
String name = file.getName();
System.out.println(name);
//获取文件大小(单位是字节)
long len = file.length();
System.out.println(len+"字节");
//是否可读可写
boolean cr = file.canRead();
boolean cw = file.canWrite();
System.out.println("是否可读:"+cr);
System.out.println("是否可写:"+cw);
//是否隐藏
boolean ih = file.isHidden();
System.out.println("是否隐藏:"+ih);
}
}
创建一个新文件
createNewFile()方法,可以创建一个新文件
package file;
import java.io.File;
import java.io.IOException;
/**
* 使用File创建一个新文件
*/
public class CreateNewFileDemo {
public static void main(String[] args) throws IOException {
//在当前目录下新建一个文件:test.txt
File file = new File("./test.txt");
//boolean exists()判断当前File表示的位置是否已经实际存在该文件或目录
if(file.exists()){
System.out.println("该文件已存在!");
}else{
file.createNewFile();//将File表示的文件创建出来
System.out.println("文件已创建!");
}
}
}
删除一个文件
delete()方法可以将File表示的文件删除
package file;
import java.io.File;
/**
* 使用File删除一个文件
*/
public class DeleteFileDemo {
public static void main(String[] args) {
//将当前目录下的test.txt文件删除
/*
相对路径中"./"可以忽略不写,默认就是从当前目录开始的。
*/
File file = new File("test.txt");
if(file.exists()){
file.delete();
System.out.println("文件已删除!");
}else{
System.out.println("文件不存在!");
}
}
}
创建目录
mkDir():创建当前File表示的目录
mkDirs():创建当前File表示的目录,同时将所有不存在的父目录一同创建
package file;
import java.io.File;
/**
* 使用File创建目录
*/
public class MkDirDemo {
public static void main(String[] args) {
//在当前目录下新建一个目录:demo
// File dir = new File("demo");
File dir = new File("./a/b/c/d/e/f");
if(dir.exists()){
System.out.println("该目录已存在!");
}else{
// dir.mkdir();//创建目录时要求所在的目录必须存在
dir.mkdirs();//创建目录时会将路径上所有不存在的目录一同创建
System.out.println("目录已创建!");
}
}
}
删除目录
delete()方法可以删除一个目录,但是只能删除空目录。
package file;
import java.io.File;
/**
* 删除一个目录
*/
public class DeleteDirDemo {
public static void main(String[] args) {
//将当前目录下的demo目录删除
File dir = new File("demo");
// File dir = new File("a");
if(dir.exists()){
dir.delete();//delete方法删除目录时只能删除空目录
System.out.println("目录已删除!");
}else{
System.out.println("目录不存在!");
}
}
}
访问一个目录中的所有子项
listFiles方法可以访问一个目录中的所有子项
package file;
import java.io.File;
/**
* 访问一个目录中的所有子项
*/
public class ListFilesDemo1 {
public static void main(String[] args) {
//获取当前目录中的所有子项
File dir = new File(".");
/*
boolean isFile()
判断当前File表示的是否为一个文件
boolean isDirectory()
判断当前File表示的是否为一个目录
*/
if(dir.isDirectory()){
/*
File[] listFiles()
将当前目录中的所有子项返回。返回的数组中每个File实例表示其中的一个子项
*/
File[] subs = dir.listFiles();
System.out.println("当前目录包含"+subs.length+"个子项");
for(int i=0;i<subs.length;i++){
File sub = subs[i];
System.out.println(sub.getName());
}
}
}
}
获取目录中符合特定条件的子项
重载的listFiles方法:File[] listFiles(FileFilter)
该方法要求传入一个文件过滤器,并仅将满足该过滤器要求的子项返回。
package file;
import java.io.File;
import java.io.FileFilter;
/**
* 重载的listFiles方法,允许我们传入一个文件过滤器从而可以有条件的获取一个目录
* 中的子项。
*/
public class ListFilesDemo2 {
public static void main(String[] args) {
/*
需求:获取当前目录中所有名字以"."开始的子项
*/
File dir = new File(".");
if(dir.isDirectory()){
// FileFilter filter = new FileFilter(){//匿名内部类创建过滤器
// public boolean accept(File file) {
// String name = file.getName();
// boolean starts = name.startsWith(".");//名字是否以"."开始
// System.out.println("过滤器过滤:"+name+",是否符合要求:"+starts);
// return starts;
// }
// };
// File[] subs = dir.listFiles(filter);//方法内部会调用accept方法
File[] subs = dir.listFiles(new FileFilter(){
public boolean accept(File file) {
return file.getName().startsWith(".");
}
});
System.out.println(subs.length);
}
}
}
Lambda表达式
JDK8之后,java支持了lambda表达式这个特性.
- lambda可以用更精简的代码创建匿名内部类.但是该匿名内部类实现的接口只能有一个抽象方法,否则无法使用!
- lambda表达式是编译器认可的,最终会将其改为内部类编译到class文件中
package lambda;
import java.io.File;
import java.io.FileFilter;
/**
* JDK8之后java支持了lambda表达式这个特性
* lambda表达式可以用更精简的语法创建匿名内部类,但是实现的接口只能有一个抽象
* 方法,否则无法使用。
* lambda表达式是编译器认可的,最终会被改为内部类形式编译到class文件中。
*
* 语法:
* (参数列表)->{
* 方法体
* }
*/
public class LambdaDemo {
public static void main(String[] args) {
//匿名内部类形式创建FileFilter
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
return file.getName().startsWith(".");
}
};
FileFilter filter2 = (File file)->{
return file.getName().startsWith(".");
};
//lambda表达式中参数的类型可以忽略不写
FileFilter filter3 = (file)->{
return file.getName().startsWith(".");
};
/*
lambda表达式方法体中若只有一句代码,则{}可以省略
如果这句话有return关键字,那么return也要一并省略!
*/
FileFilter filter4 = (file)->file.getName().startsWith(".");
}
}
总结:
File类
File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径)
使用File可以做到:
- 1:访问其表示的文件或目录的属性信息,例如:名字,大小,修改时间等等
- 2:创建和删除文件或目录
- 3:访问一个目录中的子项
常用构造器:
- File(String pathname)
- File(File parent,String name)可参考文档了解
常用方法:
- length():返回一个long值,表示占用的磁盘空间,单位为字节。
- canRead():File表示的文件或目录是否可读
- canWrite():File表示的文件或目录是否可写
- isHidden():File表示的文件或目录是否为隐藏的
- createNewFile():创建一个新文件,如果指定的文件所在的目录不存在会抛出异常java.io.FileNotFoundException
- mkdir:创建一个目录
- mkdirs:创建一个目录,并且会将所有不存在的父目录一同创建出来,推荐使用。
- delete():删除当前文件或目录,如果目录不是空的则删除失败。
- exists():判断File表示的文件或目录是否真实存在。true:存在 false:不存在
- isFile():判断当前File表示的是否为一个文件。
- isDirectory():判断当前File表示的是否为一个目录
- listFiles():获取File表示的目录中的所有子项
- listFiles(FileFilter filter):获取File表示的目录中满足filter过滤器要求的所有子项
文件输入流
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 使用文件输入流读取文件中的数据
*/
public class FISDemo {
public static void main(String[] args) throws IOException {
//将fos.dat文件中的字节读取回来
/*
fos.dat文件中的数据:
00000001 00000010
*/
FileInputStream fis = new FileInputStream("fos.dat");
/*
java.io.InputStream(所有字节输入流的超类)定义着读取字节的相关方法
int read()
读取1个字节并以int型整数返回读取到的字节内容,返回的int值中对应的2进制的"低八位"
就是读取到的数据。如果返回的int值为整数-1(这是一个特殊值,32位2进制全都是1)表达的
是流读取到了末尾了。
int read(byte[] data)
文件输入流重写了上述两个方法用来从文件中读取对应的字节。
*/
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第一次读取的字节
当我们第一次调用:
int d = fis.read();//读取的是文件中第一个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000001
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是1.
*/
int d = fis.read();//读取到的就是整数1
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第二次读取的字节
当我们第二次调用:
d = fis.read();//读取的是文件中第二个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000010
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是2.
*/
d = fis.read();//2
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010 文件末尾
^^^^^^^^
没有第三个字节
当我们第三次调用:
d = fis.read();//读取到文件末尾了!
该int值d对应的2进制:
11111111 11111111 11111111 11111111
该数字是正常读取1个字节永远表达不了的值。并且-1的2进制格式好记。因此用它表达读取
到了末尾。
*/
d = fis.read();//-1
System.out.println(d);
fis.close();
}
}
文件输出流
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 利用文件输入流与输出流实现文件的复制操作
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
//用文件输入流读取待复制的文件
// FileInputStream fis = new FileInputStream("image.jpg");
FileInputStream fis = new FileInputStream("01.rmvb");
//用文件输出流向复制文件中写入复制的数据
// FileOutputStream fos = new FileOutputStream("image_cp.jpg");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
/*
原文件image.jpg中的数据
10100011 00111100 00001111 11110000....
^^^^^^^^
读取该字节
第一次调用:
int d = fis.read();
d的2进制:00000000 00000000 00000000 10100011
读到的字节
fos向复制的文件image_cp.jpg中写入字节
第一次调用:
fos.write(d);
作用:将给定的int值d的2进制的"低八位"写入到文件中
d的2进制:00000000 00000000 00000000 10100011
写出字节
调用后image_cp.jpg文件数据:
10100011
*/
/*
循环条件是只要文件没有读到末尾就应该复制
如何直到读取到末尾了呢?
前提是:要先尝试读取一个字节,如果返回值是-1就说明读到末尾了
如果返回值不是-1,则说明读取到的是一个字节的内容,就要将他写入到复制文件中
*/
int d;//先定义一个变量,用于记录每次读取到的数据
long start = System.currentTimeMillis();//获取当前系统时间
while ((d = fis.read()) != -1) {
fos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}
块读写的文件复制操作
int read(byte[] data) 一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。 返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。
块写操作 void write(byte[] data) 一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len) 一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 通过提高每次读写的数据,减少读写次数可以提高读写效率。
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("01.rmvb");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
/*
块读:一次性读取一组字节
块写:一次性将写出一组字节
java.io.InputStream上定义了块读字节的方法:
int read(byte[] data)
一次性读取给定字节数组length个字节并从头开始装入到数组中。返回值为实际读取到的字节量
如果返回值为-1则表示流读取到了末尾。
文件流重写了该方法,作用是块读文件里的数据。
java.io.OutputStream上定义了块写字节的方法:
void write(byte[] data)
一次性将给定的字节数组中所有的字节写出。
void write(byte[] data,int offset,int len)
一次性将给定的字节数组data中从下标offset处开始的连续len个字节写出。
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010
byte[] buf = new byte[4];//创建一个长度为4的字节数组
buf默认的样子(每个元素若以2进制表现):{00000000,00000000,00000000,00000000}
int len;//记录每次实际读取的字节数
当第一次调用:
len = fis.read(buf);
由于字节数组buf的长度为4.因此可以一次性最多从文件中读取4个字节并装入到buf数组中
返回值len表示的整数是这次实际读取到了几个字节。
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
第一次读取的4个字节
buf:{11110000,00001111,01010101,11111111}
len:4 表示本次读取到了4个字节
第二次调用:
len = fis.read(buf);
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010 文件末尾了
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
本次实际只能读取到2个字节
buf:{00000000,10101010,01010101,11111111}
|本次新读的2字节数据| |---上次的旧数据---|
len:2表示本次实际只读取到了2个字节。它的意义就是告诉你buf数组中前几个字节是本次真实
读取到的数据
第三次调用:
len = fis.read(buf);
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010 文件末尾了
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
buf:{00000000,10101010,01010101,11111111} 没有任何变化!
len:-1 表示本次读取时已经是文件末尾了!!
*/
/*
00000000 8位2进制 1byte 1字节
1024byte = 1kb
1024kb = 1mb
1024mb = 1gb
1024gb = 1tb
*/
/*
编译完该句代码:byte[] buf = new byte[10240];
在实际开发中,有时候用一个计算表达式更能表现这个值的含义时,我们不妨使用计算表达式
long t = 864000000;
long t = 60 * 60 * 24 * 1000;
*/
byte[] buf = new byte[1024 * 10];//10kb
int len;//记录每次实际读取到的字节数
long start = System.currentTimeMillis();
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕,耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}
写文本数据
String提供方法: byte[] getBytes(String charsetName) 将当前字符串转换为一组字节
参数为字符集的名字,常用的是UTF-8。 其中中文字3字节表示1个,英文1字节表示1个。
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输出流
2:将写出的文字先转换为2进制(一组字节)
3:关闭流
文件流有两种创建方式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后
在陆续将通过该流写出的内容保存到文件中。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "让我再看你一遍,从南到北。";
/*
String提供了将内容转换为一组字节的方法:getBytes()
java.nio.charset.StandardCharsets
*/
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "像是北五环路蒙住的双眼。";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
文件输出流-追加模式
重载的构造方法可以将文件输出流创建为追加模式
- FileOutputStream(String path,boolean append)
- FileOutputStream(File file,boolean append)
当第二个参数传入true时,文件流为追加模式,即:指定的文件若存在,则原有数据保留,新写入的数据会被顺序的追加到文件中
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输出流
2:将写出的文字先转换为2进制(一组字节)
3:关闭流
文件流有两种创建方式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后
在陆续将通过该流写出的内容保存到文件中。
2:追加模式,对应的构造器
FileOutputStream(String filename,boolean append)
FileOutputStream(File file,boolean append)
当第二个参数为true时,那么就是追加模式。
所谓追加模式:文件流在创建时若发现该文件已存在,则原内容都保留。通过当前流陆续写出
的内容都会被陆续追加到文件末尾。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "斯国一!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "奥里给!";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
读取文本数据
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 从文件中读取文本数据
*/
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输入流
2:从文件中将字节都读取回来
3:将读取到的字节转换回字符串
*/
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1024];//1kb
int len = fis.read(data);//块读操作,返回值表达实际读取到了多少字节
System.out.println("实际读取了:"+len+"个字节");
/*
String提供了构造方法可以将一个字节数组还原为字符串
String(byte[] data,Charset charset)
将给定的字节数组data中所有字节按照给定的字符集转换为字符串。
String(byte[] data,int offset,int len,Charset charset)
将给定的字节数组data从下标offset处开始的连续len个字节按照指定的字符集转换为字符串
*/
String line = new String(data,0,len,StandardCharsets.UTF_8);
System.out.println(line.length());//输出字符串长度
System.out.println(line);
fis.close();
}
}
字节流
InputStream
文件输入流FileInputStream
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 使用文件输入流读取文件中的数据
*/
public class FISDemo {
public static void main(String[] args) throws IOException {
//将fos.dat文件中的字节读取回来
/*
fos.dat文件中的数据:
00000001 00000010
*/
FileInputStream fis = new FileInputStream("fos.dat");
/*
java.io.InputStream(所有字节输入流的超类)定义着读取字节的相关方法
int read()
读取1个字节并以int型整数返回读取到的字节内容,返回的int值中对应的2进制的"低八位"
就是读取到的数据。如果返回的int值为整数-1(这是一个特殊值,32位2进制全都是1)表达的
是流读取到了末尾了。
int read(byte[] data)
文件输入流重写了上述两个方法用来从文件中读取对应的字节。
*/
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第一次读取的字节
当我们第一次调用:
int d = fis.read();//读取的是文件中第一个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000001
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是1.
*/
int d = fis.read();//读取到的就是整数1
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第二次读取的字节
当我们第二次调用:
d = fis.read();//读取的是文件中第二个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000010
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是2.
*/
d = fis.read();//2
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010 文件末尾
^^^^^^^^
没有第三个字节
当我们第三次调用:
d = fis.read();//读取到文件末尾了!
该int值d对应的2进制:
11111111 11111111 11111111 11111111
该数字是正常读取1个字节永远表达不了的值。并且-1的2进制格式好记。因此用它表达读取
到了末尾。
*/
d = fis.read();//-1
System.out.println(d);
fis.close();
}
}
缓冲流:BufferedInputStream
缓冲流是一对高级流,在流链接中链接它的目的是加快读写效率。缓冲流内部默认缓冲区为8kb,缓冲流总是块读写数据来提高读写效率。
缓冲字节输出流:BufferedInputStream,继承自java.io.InputStream
常用构造器
- BufferedInputStream(InputStream in):创建一个默认8kb大小缓冲区的缓冲字节输入流,并连接到参数指定的字节输入流上。
- BufferedInputStream(InputStream in,int size):创建一个size指定大小(单位是字节)缓冲区的缓冲字节输入流,并连接到参数指定的字节输入流上。
常用方法
缓冲流的读取方法功能与InputStream上一致,需要知道的时read方法调用后缓冲流会一次性读取缓冲区大小的字节数据并存入缓冲区,然后再根据我们调用read方法读取的字节数进行返回,直到缓冲区所有数据都已经通过read方法返回后会再次读取一组数据进缓冲区。即:块读取操作
OutPutStream
文件输出流:FileOutputStream
package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* JAVA IO
* IO:Input,Output 即:输入与输出
*
* JAVA IO用于我们程序可以和外界交换数据。用于与外界的数据进行读写操作的。
*
* java中将输入与输出比喻为"流":stream
* 如何理解流:讲流想象为一个连接我们程序和另一端的"管道",在其中按照同一方向顺序移动的数据。
* 有点像"水管"中向着统一方向流动的水。
* 输入流:从外界向我们的程序中移动的方向,因此是用来获取数据的流,作用就是:读取操作
* 输出流:写出操作
* 注意:流是单向的,输入永远用来读,输出永远用来写。将来我们在实际开发中希望与程序交互的另一端
* 互相发送数据时,我们只需要创建一个可以连接另一端的"流",进行读写操作完成。
*
* java定义了两个超类,来规范所有的字节流
* java.io.InputStream:所有字节输入流的超类(抽象类),里面定义了读取字节的相关方法。
* 所有字节输入流都继承自它
* java.io.OutputStream:所有字节输出流的超类(抽象类),里面定义了写出字节的相关方法。
* 所有的字节输出流都继承自它
*
*
* 文件流
* java.io.FileInputStream和java.io.FileOutputStream
* 作用是真实连接我们程序和文件之间的"管道"。其中文件输入流用于从文件中读取字节。而文件输出流则
* 用于向文件中写入字节。
*
* 文件流是节点流
* JAVA IO将流划分为两类:节点流和处理流
* 节点流:俗称"低级流",特点:真实连接我们程序和另一端的"管道",负责实际读写数据的流
* 文件流就是典型的节点流,真实连接我们程序与文件的"管道",可以读写文件数据了。
* 处理流:俗称"高级流"
* 特点:
* 1:不能独立存在(单独实例化进行读写操作不可以)
* 2:必须连接在其他流上,目的是当数据"流经"当前流时,可以对其做某种加工操作,简化我们的工作、
* 流的连接:实际开发中经常会串联一组高级流最终到某个低级流上,对数据进行流水线式的加工读写。
*
*/
public class FOSDemo {
public static void main(String[] args) throws IOException {
//需求:向当前目录的文件fos.dat中写入数据
/*
在创建文件输出流时,文件输出流常见的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
上述两种构造器都会在创建时将该文件创建出来(如果该文件不存在才会这样做),自动创建
该文件的前提是该文件所在的目录必须存在,否则会抛出异常。
*/
// File file = new File("./fos.dat");
// FileOutputStream fos = new FileOutputStream(file);
/*
一个小技巧:在指定相对路径时,如果是从"当前目录"(./)开始的,那么"./"是可以忽略不写的
因为在相对路径中,默认就是从"./"开始
*/
// FileOutputStream fos = new FileOutputStream("./fos.dat");
FileOutputStream fos = new FileOutputStream("fos.dat");//与上面一句位置相同
/*
OutputStream(所有字节输出流的超类)中定义了写出字节的方法:
其中:
void write(int d)
写出一个字节,将给定的参数int值对应的2进制的"低八位"写出。
文件输出流继承OutputStream后就重写了该方法,作用是将该字节写入到文件中。
*/
/*
向文件中写入1个字节
fow.write(1)
将int值的1对应的2进制的"低八位"写如到文件第一个字节位置上
1个int值占4个字节,每个字节是一个8为2进制
int 1的2进制样子:
00000000 00000000 00000000 00000001
^^^^^^^^
写出的字节
write方法调用后,fos.dat文件中就有了1个字节,内容为:
00000001
再次调用:
fos.write(5)
int 5的2进制样子:
00000000 00000000 00000000 00000101
^^^^^^^^
写出的字节
write方法调用后,fos.dat文件中就有了2个字节,内容为:
00000001 00000101
上次写的 本次写的
*/
fos.write(1);
fos.write(5);
System.out.println("写出完毕!");
//注意!流使用完毕后要关闭,来释放底层资源
fos.close();
}
}
缓冲流:BufferedOutputStream
缓冲流是一对高级流,在流链接中链接它的目的是加快读写效率。缓冲流内部默认缓冲区为8kb,缓冲流总是块读写数据来提高读写效率。
缓冲字节输出流:BufferedOutputStream,继承自java.io.OutputStream
常用构造器
- BufferedOutputStream(OutputStream out):创建一个默认8kb大小缓冲区的缓冲字节输出流,并连接到参数指定的字节输出流上。
- BufferedOutputStream(OutputStream out,int size):创建一个size指定大小(单位是字节)缓冲区的缓冲字节输出流,并连接到参数指定的字节输出流上。
常用方法
flush():强制将缓冲区中已经缓存的数据一次性写出
缓冲流的写出方法功能与OutputStream上一致,需要知道的时write方法调用后并非实际写出,而是先将数据存入缓冲区(内部的字节数组中),当缓冲区满了时会自动写出一次。
字符流
java将流按照读写单位划分为字节与字符流。字节流以字节为单位读写,字符流以字符为单位读写。
字符流的两大
转换流java.io.InputStreamReader和OutputStreamWriter
功能无需掌握,了解其核心意义:
1:衔接其它字节与字符流
2:将字符与字节进行转换
相当于是现实中的”转换器”
缓冲字符输出流
缓冲字符输出流需要记住的是PrintWriter和BufferedReader
作用:
1:块写或块读文本数据加速
2:可以按行写或读字符串
java.io.PrintWriter 具有自动行刷新的缓冲字符输出流
常用构造器
PrintWriter(String filename) :可以直接对给定路径的文件进行写操作
PrintWriter(File file):可以直接对File表示的文件进行写操作
上述两种构造器内部会自动完成流连接操作。
PrintWriter(OutputStream out):将PW链接在给定的字节流上(构造方法内部会自行完成转换流等流连接)
PrintWriter(Writer writer):将PW链接在其它字符流上
PrintWriter(OutputStream out,boolean autoflush)
PrintWriter(Writer writer,boolean autoflush)
上述两个构造器可以在链接到流上的同时传入第二个参数,如果该值为true则开启了自动行刷新功能。
常用方法
void println(String line):按行写出一行字符串
特点
自动行刷新,当打开了该功能后,每当使用println方法写出一行字符串后就会自动flush一次
总结
java将流分为两类:节点流与处理流:
-
节点流:也称为低级流.
节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的.
-
处理流:也称为高级流.
处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.
实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在.
日常随笔
为什么要用枚举类型实现单例模式呢,为什么枚举可以防止序列化和反射呢?
1. 反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。
Java的类什么时候被加载?
1.被new关键字实例化的时候
2.通过反射实例化对象时也可以实现类加载,例如:Class.forName()方法可以实现类的加载
3.被使用的时候(通过类.静态方法的方式使用)
注:User user; 这只是定义了一个User类变量,new User; 才是对User类的实例化。
static关键字
1.static修饰的成员变量和方法,从属于类
2.普通变量和方法从属于对象
3.静态方法不能调用非静态成员,编译会报错
反射:
反射功能非常强大,主要表现为:
1. 可以通过类名实例化对象,并对对象进行操作。
2. 可以突破访问修饰符的限制
3. 可以突破泛型的限制
注意:Class就像是设计图纸,有了图纸就可以产生模型class(比如:被子的设计图纸)
class就是通过设计图纸产生的模型(比如:各种杯子的模型)
对象就是通过模型生产出的真实实体(比如:通过模型生产的杯子实体)
注解:
Java有内置的7大注解:
Java用于给注解定义注解的元注解4个:元注解的作用就是负责注解其他注解。 是Java定义了4个标准
@Target
@Retention
@Documented
@Inherited
注意:只是定义注解是没有什么实际意义的,只有通过反射对注解进行解析才能实现很多丰富的作用!!
IDEA查看二进制文件的插件:BinEd library 可用于二进制的编辑和查看,很强大!
IDEA自动添加get、set方法的插件:Lombok(小辣椒)
IDEA查看分析.class字节码文件的插件:jclasslib
IDEA中文插件:Chinese Language Pack
IDEA查看方法之间的调用关系。
1. ctrl+alt+h的功能的英文名意思是"调用层次",alt+f7的功能的英文名意思是"找到使用的地方"。其实都有"找到使用的地方"的功能,区别
2. alt+f7的结果是由大到小的层次,结果的树形结构是模块->包->类->方法->行。
3. ctrl+alt+h的搜索结果的结构是目标方法->调用目标方法的方法a->调用a的方法b……这样的结构
finally 和 return,到底谁先执行?
总结:finally 在 return 之后时,先执行 finally 后,再执行该 return;finally 内含有 return 时,直接执行其 return 后结束;finally 在 return 前,执行完 finally 后再执行 return。
Java 中 final、finally、finalize 的区别与用法
1. final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。即如果一个类被声明为 final,意味着它不能作为父类被继承,因此一个类不能同时被声明为 abstract 的,又被声明为 final 的。变量或方法被声明为 final,可以保证它们在使用中不被修改。被声明为 final 的变量必须在声明时给赋予初值,而在以后的引用中只能读取,不可修改。被声明为 final 的方法也同样只能使用,不能重载。
2. finally 是异常处理语句结构的一部分,总是执行,常见的场景:释放一些资源,例如前面所说的 redis、db 等。在异常处理时提供 finally 块来执行任何清除操作,即在执行 catch 后会执行 finally 代码块。
3. finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
Java 代码中数字中间带下划线是几个意思?
简单来说就是从 Java SE 7 的版本开始,程序中的数字可以使用下划线来进行分割(_)以便于为程序提供更好的可读性。
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。
线程安全的指令重排问题
当一个变量定义为 volatile 之后,将具备两种特性:
1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
缓存行(cache line)
定义:Cache Line可以简单的理解为CPU Cache中的最小缓存单位。目前主流的CPU Cache的Cache Line大小都是64Bytes。
通过缓存行优化性能:通过填充缓存行达到提高系统运行效率,也就是通过牺牲内存换速度。
Java中的引用类型
引用分类:
1.强引用:当对象没有被引用时会被gc回收
2.软引用:当内存空间不够用时软引用会被强制回收(用途:适合做缓存,防止内存泄漏问题)
3.弱引用:遇到gc就会被回收(为了防止内存泄漏问题)
4.虚引用:用于对堆外内存进行回收
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/java/288454.html