chap01-Java入门
学习方法
- 笔记 课上简单记一下笔记、详细记录问题;课下整理一份属于自己的笔记,推荐手写笔记
- 问题 先独立思考0~5分钟;再问同学、问老师;最后总结问题,确保自己真正会了
- 代码 多写!多写!多写! 提高手速、熟悉代码
Java
Java发展史
- 1995年 Sun公司发布Java
- 1996年 发布了JDK1.0
- 1999年 发布JDK1.2 逐渐流行 J2EE(企业版) J2SE(标准版) J2ME(移动版)
- 2004年 发布了JDK1.5
- 2005年 改名为JDK5.0 JavaEE JavaSE JavaME
- 2006年 正式开源,走向伟大
- 2009年 Sun公司被Oracle收购
- 2011年 发布了JDK7.0
- 2014年 发布JDK8.0 增加了许多实用的新特性
- 2018年 JavaEE正式由Eclipse基金会(开源组织)负责,JakartaEE(雅加达)
Java的优势(特点)
- 简单性 比C简单
- 跨平台
- 纯面向对象
- 开源、丰富的编程资源
Java的运行机制
-
编译型 c c++ c# uc oc
- 源文件----编译器—》机器码文件----》运行机器码文件
- 优点: 效率高 编译一次,运行多次
- 缺点:不能跨平台
-
解释型 JS 脚本语言
- 源文件----解释器—》逐行将源文件中的代码解释成机器码,逐行运行
- 优点:跨平台
- 缺点:效率低 每次执行都需要重新解释运行
-
Java的运行机制: 先编译,后解释
- 源文件----编译器—》先编译成平台中立的字节码文件(.class文件)—解释器----》逐行解释.class文件,逐行运行
- 特点: 既可以跨平台,效率又高
环境搭建
第一个Java代码
基础语法
-
创建一个Java源文件(.java文件) -
class 类 盛放代码的容器、Java程序的基本组成单元 一个源文件中可以写多个类 -
类名 区分不同的类 -
{} 划分类的范围 -
public static void main(String[] args){
}
主函数,程序运行的入口,只有写在主函数中的代码才可以运行到
-
System.out.println();
换行打印输出语句:可以将()中的数据输出到控制台或黑窗口中
System.out.print(); 不换行打印输出语句
-
编译命令: javac 源文件名.java 注意: 当编译成功后,源文件中的每个类都会对应生成一个.class文件 -
解释命令:java 类名
补充:
打开黑窗口的方式:
- win键+r键,再出现的窗口中输入cmd
- 通过notepad++
常用的DOS命令:
命令 | 作用 |
---|
盘符名: | 切换盘符 | cd 文件夹名 | 进入该文件夹 | cd … | 退出当前文件夹 | cd \ | 回到盘符 | dir | 查看当前目录下的所有文件 | cls | 清屏 |
公开类
public class 公开类 公共的类,即可以随意访问的类
public 公开的、公共的
使用要求:
- 公开类类名必须和源文件名一致
- 每个源文件中最多只能有一个公开类,可以没有
Package
概念: 包 类似于文件夹
作用1: 帮助管理.class文件 (前期)
作用2: 划分项目结构,相同相似作用的源文件放在同一包下 (后期)
语法: package 包名1.包名2.包名3;
注意:
当使用了package后,原有的编译命令也可以使用,原有的解释命令不能使用
推荐(强制)使用
带包编译命令:javac -d . 源文件名.java
带包解释命令:java 包名.类名
使用要求:
- package语句,必须写在源文件中有效代码的第一个行
- 每个源文件中最多只能有一个package语句,可以没有
- 包名可以写多个,用.连接,代表多层包结构
编码规范
书写规范
- 每行只写一行代码
- 层级之间要有缩进 使用Tab缩进,不要使用空格
标识符命名规范
- 语法:硬性规定,必须遵守
- 标识符只能由数字、字母、_、$组成,数字不能开头
- 严格区分大小写
- 没有长度限制
- 不能使用关键字、保留字(goto,const)、特殊单词(true、false、null)命名
- 规范:不是硬性规定,可以不遵守,但是强烈建议遵守
- 望名生义
- 类名中的所有首字母大写
- 变量名、函数名首单词首字母小写,其他单词首字母大写
- 包名全小写
- 常量名全大写,单词之间使用_连接
注释
概念:描述性的文字
作用:可以对下面的代码做一些解释说明;注释内容不会参与代码的编译过程,自然也不会运行;常常将一些不需要运行但也不想删除的代码注释掉;
-
单行注释 // 注释内容
-
多行注释 /*
注释内容
注释内容
*/
/*
class Test{
public static void main(String[] args){
//System.out.print("Hello");
System.out.print("World");
}
}
*/
-
文档注释 作用:生成一套网页形式的代码说明文档 特殊编译命令: javadoc -d 文件名 源文件名.java 注意:只能写在公开类、主函数上面 语法: /**
这是一个公开类,public 代表公开公共的
First是公开类类名,必须和源文件名保持一致
*/
public class First{
/**
这是一个主函数,是程序运行的入口
*/
public static void main(String[] args){
System.out.println("我的第一个公开类");
}
}
chap02-数据类型与运算符
变量
概念: 是内存中的一块儿存储空间,用来存储数据,是存储数据的基本单元
变量的组成: 数据类型 变量名 值(数据)
变量(虚拟的盒子) 实体盒子
数据类型 类型、材质
变量名 名字、标号
值(数据) 具体物品
语法:
1. 声明并赋值 最常用
数据类型 变量名 = 值; 例如: int a = 10;
2. 先声明,再赋值
声明:数据类型 变量名; 例如: int a;
赋值:变量名 = 值; 例如: a = 10;
3. 同时声明多个相同类型的变量并赋值
数据类型 变量名1 = 值1,变量名2 = 值2,变量名3 = 值3;
例如:int a = 1,b,c = 3;
等价于:
int a = 1;
int b;
int c = 3;
注意: Java是强类型语言,变量所存储的值的类型必须与变量的数据类型一致!
数据类型
- 基本数据类型
- 4类8种
- 整数类型:byte short int long
- 浮点数类型: float double
- 字符类型: char
- 布尔类型: boolean
- 引用数据类型(对象类型) 无数种!
基本数据类型
-
整数类型
数据类型 | 字节长度 | 取值范围 | 取值范围(十进制) | 举例 |
---|
byte | 1B | -27~27-1 | -128~127 | byte b = 10; | short | 2B | -215~215-1 | -32768~32767 | short s = 10; | int | 4B | -231~231-1 | -2147483648~2147483647 | int i = 10; | long | 8B | -263~263-1 | -9223372036854775808~9223372036854775807 | long l = 10L; |
注意:Java中默认的整数类型为int,最常用的也是int
补充:
? 计算机就是一系列的电路开关,每个开关都有两种状态,开或者关,开即1,关即0;
? 每个1或者0,都存为一个位(bit,比特),最小的存储单元
? 每8个位存储为一个字节(Byte),字节是最小的存储单位
? 通常使用b代表位,B代表字节,即1B=8b;
-
浮点数类型-小数类型
数据类型 | 字节长度 | 取值范围 | 举例 |
---|
float(单精度浮点数) | 4B | -3.4E38~3.4E38 | float f = 1.25F; | double(双精度浮点数) | 8B | -1.79E308~1.79E308 | double d = 2.5; |
注意: Java中默认的浮点数类型为double,最常用的也是double -
字符类型-char
数据类型 | 字节长度 | 取值范围 | 字符集 |
---|
char | 2B | 0~65535 | Unicode(16进制)字符集 |
注意:Unicode兼容ASCII字符集
char类型的三种赋值方式:
- 使用’‘引起来的单个字符 char c1 = ‘A’; char c2 = ‘中’;
- 整数形式 char c3 = 65;
- 使用16进制的Unicode编码 char c4 = ‘\u0041’;
补充:
? 转义字符 \ 在Java中某些具有特殊含义的符号不能之间打印出来,需要使用转义字符
单个单引号 \'
单个双引号 \"
单个\ \\
水平跳格 \t
换行 \n
-
布尔类型-boolean
数据类型 | 取值 | 应用场景 |
---|
boolean | true(真) false(假) | 条件判断 |
注意: boolean类型不参与数据运算
引用数据类型
- String 字符串 由一串字符组成的串,必须有“”引起来
- 字符串拼接: 任何数据类型通过+与字符串拼接到一起,整体都会变成字符串
数据类型转换
除了直接给一个变量赋一个确切的值之外,还可以通过一个变量给另一个变量赋值
int a = 10;
int b = a;
short s = a; // 错误,a 中的值可能会超出s的范围
-
自动类型转换 当小类型变量赋值为大类型变量时,可以直接赋值 例如: int a = 10; long l = a; 转换规则: ? byte -> short -> int -> long -> float -> double ? char -> int -> long -> float -> double class TestDataTypeChange{
public static void main(String[] args){
// byte b = 10;
// short s = b;
char c = 'A';
int i = c;
long l = i;
float f = l;
double d = f;
System.out.println(d);
}
}
-
强制类型转换 - 强转 (不常用) 当大类型变量赋值为小类型变量时,不可以直接赋值,需要强转 语法: 小数据类型 变量名 = (小数据类型)大数据类型变量名; 例如: int i = 10000; byte b = (byte) i; 强转规则:
-
大类型整数强转为小类型整数,若不超出范围,数据完整 例如: long l = 100; int i = l; i = 100; -
大类型整数强转为小类型整数,超出范围,直接舍去高位字节, 数据正负可能发生变化 例如: int i = 10000; byte b = (byte) i; b = 16 -
小数类型转为整数类型,直接舍去小数部分,没有四舍五入 例如: double d = 12.99; int i = (int)d; i = 12; class TestDataTypeChange1{
public static void main(String[] args){
double d1 = 0.1;
double d2 = -0.1;
int i1 = (int)d1;
int i2 = (int)d2;
System.out.println(i1+" "+i2); 0 0
}
}
自动类型提升
当进行算数运算时
若有double参与,最终结果提升为double类型;
若没有double,有float参与,最终结果提升为float;
若没有double、float,有long参与,最终结果提升为long;
除此之外,统统提升为int类型
class TestDataTypeChange2{
public static void main(String[] args){
byte b1 = 1;
byte b2 = 2;
int b3 = b1+b2;
System.out.println(b3);
int i1 = 10;
double i2 = 3;
System.out.println(i1/i2);
}
}
运算符
表达式
概念:通过运算符将变量或值连接起来的式子,并最终会得到一个结果,即表达式
1+2 是
int a = 10; a+10; 是
int a = 10;int b = 20; a+b; 是
int a = 10;int b = 20; a>b; 布尔表达式,结果为true或false
运算符
-
算数运算符 + - *(乘,求积)/(除,求商) %(模,求余)
class TestOperator{
public static void main(String[] args){
int a = 10;
int b = 3;
System.out.println(a/b);
System.out.println(a%b);
}
}
-
一元运算符
class TestOperator1{
public static void main(String[] args){
int a = 5;
int b = a++;
System.out.println(a);
System.out.println(b);
int c = ++a;
System.out.println(a);
System.out.println(c);
int n = 5;
int m = n--;
System.out.println(n);
System.out.println(m);
int m1 = --n;
System.out.println(n);
System.out.println(m1);
int x = 5;
int y = (x++)+(++x)+(x--)+(--x);
System.out.println(x);
System.out.println(y);
}
}
-
赋值运算符
运算符 | 作用 |
---|
= | 将=右边赋值给=左边 | += | 先求和再赋值,不会发生自动类型提升 | -= | 先求差再赋值,不会发生自动类型提升 | *= | 先求积再赋值,不会发生自动类型提升 | /= | 先求商再赋值,不会发生自动类型提升 | %= | 先求余再赋值,不会发生自动类型提升 |
class TestOperator2{
public static void main(String[] args){
int a = 5;
a += 1;
System.out.println(a);
byte b = 5;
b += 1;
System.out.println(b);
}
}
-
比较运算符 判断符号两边的值或变量的关系,为布尔表达式,结果为true或false
运算符 | 作用 |
---|
== | 判断两边的值是否相等 | > | 大于 | < | 小于 | >= | 大于等于 | <= | 小于等于 | != | 不等 |
class TestOperator3{
public static void main(String[] args){
int a = 5;
int b = 10;
System.out.println(a==b);
System.out.println(a>b);
System.out.println(a<b);
System.out.println(a>=5);
System.out.println(b<=100);
System.out.println(a!=b);
}
}
-
逻辑运算符 符号两边均为布尔表达式或布尔值,总结果依然为true或false
运算符 | 作用 |
---|
&&(与) | 若两边布尔表达式均为true,则总结果为true;有一个为false,总结果为false | ||(或) | 若两边布尔表达式均为false,则总结果为false;有一个为true,总结果为true | !(反) | 获取相反的结果,对true取反则为false;对false取反则为true |
class TestOperator4{
public static void main(String[] args){
int a = 5;
int b = 10;
System.out.println(a==b||a!=b);
System.out.println(a>b&&a>3);
System.out.println(a<b&&b==10);
System.out.println(!(a>=5));
}
}
补充:
? && 短路逻辑求与运算符 例如: a>b&&a>c,当a>b为false时,由于整体结果肯定为false,所以并不会判断a>c的结果
? || 短路逻辑求或运算符 例如: a>b||a>c,当a>b为true时,由于整体结果肯定为true,所以并不会判断a>c的结果
? & 非短路逻辑求与运算符 例如: a>b&a>c, 无论a>b的结果为什么,都会进行a>c的判断
? | 非短路逻辑求与运算符 例如: a>b|a>c, 无论a>b的结果为什么,都会进行a>c的判断
class TestOperator4{
public static void main(String[] args){
int n = 10;
int m = 20;
System.out.println(n++>=100&m++>=0);
System.out.println(n);
System.out.println(m);
}
}
-
三元运算符
运算符 | 作用 |
---|
布尔表达式?值1:值2; | 对布尔表达式进行判断,若结果为true,则获得值1;若结果为false,则获得值2 |
class TestOperator5{
public static void main(String[] args){
int weekDay = 6;
String result = (weekDay>=1&&weekDay<=5?"上课":"休息");
System.out.println(result);
}
}
class TestOperator6{
public static void main(String[] args){
int n = 9;
String result = (n%2==0?"偶数":"奇数");
System.out.println(result);
}
}
扫描器
Scanner : 扫描器 类库中具有接受数据的功能类
创建语法:
java.util.Scanner sc = new java.util.Scanner(System.in);
常用方法:
sc.next(); 接受字符串
sc.nextInt(); 接收int
sc.nextDouble(); 接收double
sc.nextBoolean(); 接收boolean
import
概念: 导包,将类库中或其他地方的类,导入到当前源文件中
语法: import 包名.类名;
位置: package 语句下面,第一个类上面
注意:
1. * 代表当前包下的所有的类,不包括子包中的类
例如:import java.util.*; 意味着将util包下的所有类都导入进来
2. * 不能连用
例如: import java.*.*; 错误!!
3. java.lang是默认导入的!就等价于,在每个源文件中都有一个 import java.lang.*;
4. 当多个包中具有同名的类,需要指定具体路径(在类中写全 包名.类名)
import java.util.*;
import java.sql.*;
class TestImport{
public static void main(String[] args){
java.util.Date d = null;
}
}
学习方法
整理笔记—> 抄写案例代码并且加注释 —> 写作业
chap03-流程控制
选择结构
概念: 先对某一条件做判断,根据判断结果,执行相应的流程
如果(我有钱){
为所欲为
}
努力挣钱
基本if选择结构
语法:
if(布尔表达式){
}
执行流程:
- 判断布尔表达式,若结果为true,则执行逻辑代码,并结束整个选择,执行后续代码;
- 若结果为false,则直接结束整个选择结构,执行后续代码
class TestIf{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if(n%2==0){
System.out.println(n+"为偶数");
}
System.out.println(n);
}
}
if else选择结构
语法:
if(布尔表达式){
}else{
}
执行流程:
- 先对布尔表达式做判断,若结果为true,则执行逻辑代码1,并结束整个选择结构,执行后续代码;
- 若结果为false,则执行逻辑代码2,结束整个选择结构,执行后续代码
class TestIfElse{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if(n%2==0){
System.out.println(n+"为偶数");
}else{
System.out.println(n+"为奇数");
}
System.out.println(n);
}
}
class TestIfElse_a{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if(n%2==0)
System.out.println(n+"为偶数");
else
System.out.println(n+"为奇数");
System.out.println(n);
}
}
多重if选择结构
语法:
if(布尔表达式1){
}else if(布尔表达式2){
}else if(布尔表达式3){
}else if(布尔表达式4){
}else{
}
执行流程:
- 先对布尔表达式1进行判断,若结果为true,则执行逻辑代码1,并结束整个选择结构,执行后续代码;
- 若布尔表达式1结果为false,则进行布尔表达式2的判断,若结果为true,则执行逻辑代码2,并结束整个选择结构,执行后续代码;
- 。。。
- 若布尔表达式1、2、3、4结果统统为false,则执行else中的逻辑代码5;、
注意: 多个if互相排斥,从上到下,依次判断,当判断成功一个,其他的则不再判断
应用场景:区间判断
class TestIfElseIf{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的预算:");
int money = sc.nextInt();
if(money>30000){
System.out.println("败家之眼、外星人、顶配MacBookPro");
}else if(money>15000){
System.out.println("Dell G7");
}else if(money>8000){
System.out.println("惠普、华为");
}else if(money>4000){
System.out.println("小米、神舟");
}else{
System.out.println("步步高点读机");
}
}
}
class TestIfElseIf_a{
public static void main(String[] args){
int n = 5;
if(n++>10){
System.out.println("if");
}else if(n++>5){
System.out.println("else if1");
}else if(n++>4){
System.out.println("else if2");
}
System.out.println(n);
}
}
嵌套if选择结构
语法:
if(外层布尔表达式){
if(内层布尔表达式1){
}
}else{
if(内层布尔表达式2){
}else{
}
}
注意: 只要结果完整,可以随意嵌套
执行流程:
- 先判断外层布尔表达式,若结果为true,则判断内层布尔表达式1,。。。。
- 若结果为false,则判断内层布尔表达式2,。。。。
class Test27{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入总条数:");
int n = sc.nextInt();
System.out.println("请输入每页显示条数:");
int m = sc.nextInt();
System.out.println("请输入查询页码:");
int i = sc.nextInt();
int count = (n%m==0?n/m:(n/m)+1);
if(i>=1&&i<=count){
if(i==count){
System.out.println("显示第"+((i-1)*m+1)+"条到第"+n+"条");
}else{
System.out.println("显示第"+((i-1)*m+1)+"条到第"+i*m+"条");
}
}else{
System.out.println("youwu !");
}
}
}
分支结构
语法:
switch(变量|表达式){
case 值1:逻辑代码1;break;
case 值2:逻辑代码1;break;
case 值3:逻辑代码1;break;
case 值4:逻辑代码1;break;
case 值5:逻辑代码1;break;
default:默认代码;
}
补充: break 关键字,可以结束当前分支或循环结构
变量的类型:byte short int char String(JDK7.0+)
执行流程:
- 若变量的值为值1,则执行逻辑代码1,然后再通过break关键字结束当前分支结构
- 若变量的值为值2,则执行逻辑代码2,然后再通过break关键字结束当前分支结构
- 若变量的值为值3,则执行逻辑代码3,然后再通过break关键字结束当前分支结构
- …
- 若变量的值为值n,则执行逻辑代码n,然后再通过break关键字结束当前分支结构
- 若所有case的值都不匹配,则执行default中的默认代码
注意:
- 所有能够使用分支结果完成的代码,都可以使用多重if完成
- case中的值不能重复
- 若某一个case匹配成功,下面所有的case都不会再进行判断匹配;所以若没有及时的通过break结束分支结构,可能会执行到其他case中的逻辑代码
class TestSwitch{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = sc.nextInt();
switch(n){
case 1:System.out.println("星期一");break;
case 2:System.out.println("星期二");break;
case 3:System.out.println("星期三");break;
case 4:System.out.println("星期四");break;
case 5:System.out.println("星期五");break;
case 6:System.out.println("星期六");break;
case 7:System.out.println("星期日");break;
default:System.out.println("输入有误!");
}
}
}
class TestSwitch{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = sc.nextInt();
switch(n){
case 1:System.out.println("星期一");break;
case 2:System.out.println("星期二");break;
case 3:System.out.println("星期三");break;
case 4:System.out.println("星期四");break;
case 5:System.out.println("星期五");break;
case 6://System.out.println("星期天");break;
case 7:System.out.println("星期天");break;
default:System.out.println("输入有误!");
}
}
}
class TestSwitch{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = sc.nextInt();
switch(n){
default:System.out.println("输入有误!");break;
case 1:System.out.println("星期一");break;
case 2:System.out.println("星期二");break;
case 3:System.out.println("星期三");break;
case 4:System.out.println("星期四");break;
case 5:System.out.println("星期五");break;
case 6://System.out.println("星期天");break;
case 7:System.out.println("星期天");break;
}
}
}
class TestSwitch1{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个名字:");
String name = sc.next();
switch(name){
case "孙悟空":System.out.println("齐天大圣");break;
case "猪罡鬣":System.out.println("天蓬元帅");break;
case "沙和尚":System.out.println("卷帘大将");break;
case "乔碧罗":System.out.println("幻影坦克");break;
default:System.out.println("路人甲");
}
}
}
局部变量
概念:定义在函数(方法)内部的变量
使用要求:
- 先赋值,再使用
- 作用范围:从定义的那一行开始,到定义所在的代码块的结束
- 作用范围重合内,变量名不能重复
class Test{
public static void main(String[] args){
int a = 10;
if(a>0){
int b = 20;
}
System.out.println(a+" "+b);
}
}
循环结构
概念:满足某个条件,即可重复执行的一段代码
核心思路图:
while循环
语法:
while(布尔表达式){
}
执行流程:
- 判断布尔表达式(循环条件),若结果为true,则执行循环体
- 当循环体执行结束后,再次进行布尔表达式的判断,若结果为true,依然执行循环体
- 直至布尔表达式结果为false时,结束循环,执行后续代码
循环的组成部分: 循环变量、循环条件、循环体、循环迭代语句
while循环的特点: 先判断,再执行;不确定性循环,适用于循环次数不明确的场景
class TestWhile{
public static void main(String[] args){
int i = 1;
int j = 1;
while(i<=10){
System.out.println("Hello World "+i);
i++;
j++;
}
}
}
死循环:
死循环原因:缺少循环迭代语句
class TestWhile1{
public static void main(String[] args){
int i = 1;
while(i<=10){
System.out.println("Hello World "+i);
}
}
}
死循环原因:循环条件永远为true
class TestWhile2{
public static void main(String[] args){
int i = 1;
while(true){
System.out.println("Hello World "+i);
i++;
}
}
}
随堂小练习:
1. 通过循环,计算1~100的和
提示:需要定义额外变量,接受1~100之间的每个数
class TestWhile3{
public static void main(String[] args){
int i = 1;
int sum = 0;
while(i<=100){
sum += i;
i++;
}
System.out.println(sum);
}
}
2. 通过循环,计算1~100的偶数和
class TestWhile4{
public static void main(String[] args){
int i = 1;
int sum = 0;
while(i<=100){
if(i%2==0){
sum += i;
}
i++;
}
System.out.println(sum);
}
}
3. 猜大小
思路:
先获取到一个随机整数n,然后通过黑窗口输入整数m,若m==n,则打印,猜对了,并且游戏结束;
若m<n,则打印猜小了,请重新输入;
若m>n,则打印猜大了,请重新输入;
class TestGame{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
Random r = new Random();
int n = r.nextInt(101);
System.out.println("欢迎来到猜大小游戏!!");
System.out.println("请输入一个0~100之间的整数:");
while(true){
int m = sc.nextInt();
if(m==n){
System.out.println("恭喜你,猜对了!!");
break;
}else if(m<n){
System.out.println("猜小了,请重新输入:");
}else if(m>n){
System.out.println("猜大了,请重新输入:");
}
}
}
}
do while循环
语法:
do{
}while(布尔表达式);
执行流程:
- 先执行一次循环体
- 再进行布尔表达式(循环条件)的判断,若结果为true,则再次执行循环体
- 直至结果为false,则结束循环
do while循环的特点:先执行,再判断;不确定性循环,适用于循环次数不明确的场景
案例:
同学写作业,老师批改作业,给出评价(true、false);若为true,则作业完成;若为false,则需要再写一次作业
class TestDoWhile{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
boolean bo;
do{
System.out.println("500行代码~~~~");
System.out.println("请老师批改:");
bo = sc.nextBoolean();
}while(!bo);
}
}
for循环
语法:
for(循环变量;循环条件;循环迭代语句){
}
执行流程:
- 初始化循环变量(只执行一次)
- 判断循环条件,若结果为true,则执行循环体;
- 当循环体执行结束后,执行循环迭代语句
- 再次判断循环条件,若结果true,则执行循环体
- …
- 直至结果为false,结束循环
for循环的特点:先判断,再执行;确定性循环,适用于循环次数明确的场景
class TestFor{
public static void main(String[] args){
for(int i=1;i<=10;i++){
System.out.println("HelloWorld "+i);
}
}
}
class TestFor1{
public static void main(String[] args){
int sum = 0;
for(int i=1;i<=100;i++){
sum += i;
}
System.out.println(sum);
}
}
注意:
- 循环变量可以省略(或写在上面),;不能少
- 循环条件可以省略,可能造成死循环
- 循环迭代语句可以省略,可能造成死循环
总结
- while\for都是先判断,再执行;do while是先执行,再判断
- for循环为确定性循环,适用于循环次数明确的场景
- while和do while为不确定性循环,适用于循环次数不明确的场景
- 若无论如何都要执行一次,使用do while
流程控制
-
break 跳出或结束当前循环、分支结构 -
continue 跳过本次循环,继续下次循环 class TestBreak{
public static void main(String[] args){
for(int i=1;i<=10;i++){
if(i==5){
break;
}
System.out.println(i);
}
}
}
class TestContinue{
public static void main(String[] args){
for(int i=1;i<=10;i++){
if(i==5){
continue;
}
System.out.println(i);
}
}
}
循环嵌套
概念: 在一个循环内部再定义一个循环
注意:
-
只要语法结构完整,可以无限嵌套;但是一般推荐,只嵌套一层 -
内层循环体循环次数 = 外层循环次数*内层循环次数 -
break及continue只能作用于直接包含它们的循环结构 -
循环的标号:在循环的前面给循环起一个名字,该名字即可代表该循环 class TestForFor{
public static void main(String[] args){
outer:for(int i=1;i<=10;i++){
inner:for(int j=1;j<=10;j++){
if(j==5)break outer;
System.out.println("i="+i+" j="+j);
}
}
}
}
-
应用场景:图形打印、百钱买百鸡、打印乘法口诀表
class Test{
public static void main(String[] args){
for(int i=0;i<=33;i++){
int max = (100-i*3)/2;
for(int j=0;j<=max;j++){
int k = 100-i-j;
if(k%3==0&&3*i+2*j+k/3==100){
System.out.println("公鸡个数:"+i+" 母鸡个数:"+j+" 小鸡个数:"+k);
}
}
}
}
}
chap04-函数
基本语法
概念:一段具有特定功能的代码,可以重复调用
class TestFunction{
public static void main(String[] args){
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
System.out.println("文能挂机喷队友");
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
System.out.println("武能越塔送人头");
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
System.out.println("进可孤身一挑五");
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
System.out.println("退可坐等十五投");
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
}
}
以上代码存在问题:
1. 代码冗余 重复的代码写了太多遍
2. 可维护性差
3. 可重用性差
解决办法:将打印分割线的功能写成一个函数,在需要分割线的地方调用函数即可
语法:
public static void 函数名(){
}
函数声明: public static void 函数名() -- 声明一个函数。一般的,通过声明就可以明白函数的作用
函数实现: { 功能代码 } -- 函数功能的具体实现代码
例如: 打印分割线函数
public static void printLine(){
for(int i=1;i<=15;i++){
System.out.print("*");
}
System.out.println();
}
位置: 类的内部,其他函数的外部
class Test{
public static void main(String[] args){
}
}
注意:只有位置2与位置4可以定义函数
调用函数: 通过 函数名(); 即可
注意:
- 函数定义完成后,必须进行调用,才会实现相应的功能
- () 不能省略!!!
- 当程序执行时,遇到函数调用语句,会先暂时离开当前函数,进入到被调用函数中执行代码;当被调用函数中的代码执行完毕后,程序会回到函数调用处,继续向下执行
参数
大多数情况下,函数的顺利执行需要从外界获取数据。在函数声明中,需要告诉外界此函数需要接收什么样的数据;在函数被调用时,函数调用者,就需要根据声明传入相应的数据;
形参:形式参数,定义在函数声明中(),作用: 告诉外界此函数需要什么样的数据
实参:实际参数,写在函数调用中(),作用:根据形参传入相应的实际数据
语法:
public static void 函数名(形参列表){
}
0个形参:
public static void 函数名(){
}
1个形参
public static void 函数名(参数类型 参数名){
}
例如:
public static void method(int n){}
多个参数:参数之间使用,隔开
public static void 函数名(参数类型1 参数名1,参数类型2 参数名2,...,参数类型n 参数名n){
}
调用函数: 函数名(实参列表);
注意:当函数具有形参时,调用该函数时必须根据形参,传入相应的实参(类型、个数、顺序)
经验:形参相当于声明了一个作用范围为整个函数内部的局部变量;实参相当于为形参赋值
单个参数
class TestFunction{
public static void main(String[] args){
printLine1(10);
System.out.println("文能挂机喷队友");
printLine1(15);
System.out.println("武能越塔送人头");
printLine1(20);
System.out.println("进可孤身一挑五");
printLine1(25);
System.out.println("退可坐等十五投");
printLine1(30);
}
public static void printLine1(int num){
for(int i=1;i<=num;i++){
System.out.print("*");
}
System.out.println();
}
}
多个参数
class TestFunction{
public static void main(String[] args){
printLine2(10,"*");
System.out.println("文能挂机喷队友");
printLine2(15,"~");
System.out.println("武能越塔送人头");
printLine2(20,"#");
System.out.println("进可孤身一挑五");
printLine2(25,"-");
System.out.println("退可坐等十五投");
printLine2(30,"=");
}
public static void printLine2(int num,String line){
for(int i=1;i<=num;i++){
System.out.print(line);
}
System.out.println();
}
}
返回值
很多时候,函数执行结束后都需要返回一个结果,该结果即返回值!
语法:
public static 返回值类型 函数名(形参列表){
}
注意:
1. 返回值类型:8种基本数据类型、引用类型、 void(无返回值类型,表示函数没有返回值)
2. 当返回值类型为8种基本数据类型或引用类型时,在函数实现中,必须书写return语句
return语句: return 返回值;
作用:表示结束函数,并将返回值返回到函数调用处
3. 当返回值类型为void时,函数实现中不需要书写return语句!但是,也可以使用return关键字
return; 表示结束函数!!
4. return后面的返回值的类型必须和函数声明中定义的返回值类型一致!!
调用函数:
两种方式:
1. 若需要使用返回值,可以使用变量接收返回值
返回值类型 变量名 = 函数名(实参列表);
2. 若不需要使用返回值,或者只是简单的使用一次、打印,可以像之前直接调用
函数名(实参列表);
class TestFunction2{
public static void main(String[] args){
System.out.println(add(1,2));
add1(2,3);
}
public static int add(int a,int b){
return a+b;
}
public static void add1(int a,int b){
System.out.println(a+b);
}
}
补充: return的用法
用法:
- 用在具有返回值类型(8种基本数据类型、引用类型)的函数中,必须通过return语句返回相应的值
return 返回值; 表示结束函数,并将返回值返回到函数调用处
- 用在没有返回值类型(void)的函数中,可以不写return语句;但也可以使用return
return ; 仅仅表示结束函数
class Test{
public static void main(String[] args){
Prime(11);
}
public static void Prime(int n){
for(int i=2;i<n;i++){
if(n%i==0){
System.out.println(n+"不是质数");
return;
}
}
System.out.println(n+"是质数");
}
}
思考:
1. if后面为什么不写else
2. return在本代码中的作用
return的使用要求
- return 语句后面,不能写其他代码
- 一个代码中,同一时间只能有一个有效的return语句
- return后面只能跟一个值
- 若在具有返回值类型的函数中,存在选择、循环结构,必须保证任何情况下都有return语句!
class TestReturn{
public static void main(String[] args){
method();
}
public static int method(){
if(true){
return 0;
}else{
return 0;
}
}
public static int method1(){
int a = 10;
while(a>1){
return 0;
}
return 0;
}
}
总结
- 不能在一个函数内部,再定义其他函数
- 函数三要素:函数名、形参列表、返回值类型
- 一般的,一个函数只负责一个功能!
- 函数的优点
- 减少代码冗余
- 提高代码可读性
- 提高代码可维护性
- 提高代码可复用性
函数高级
函数嵌套调用
概念: 在一个函数中调用其他函数
class TestFunction3{
public static void main(String[] args){
System.out.println("main start");
ma();
System.out.println("main end");
}
public static void ma(){
System.out.println("ma start");
mb();
System.out.println("ma end");
}
public static void mb(){
System.out.println("mb start");
mc();
System.out.println("mb end");
}
public static void mc(){
System.out.println("mc start");
System.out.println("mc end");
}
}
总结:
- 嵌套层次最深的函数,最先执行结束
- 调用函数的形参,可以作为被调用函数的实参
JVM的内存结构(了解)
栈: 是一种只能在顶部进行操作的线性表;即入栈(压栈)和出栈(弹栈)操作都只能对栈顶元素执行
栈的特点: 先进后出,后进先出
栈帧:栈的基本组成元素;每当执行一个函数时,都会自动生成一个栈帧,进入到栈空间中;该栈帧中保存着该函数的所有信息(局部变量表、操作数栈,动态链接、返回地址…);当函数代码执行结束,会执行出栈操作
递归(了解)
概念: 在函数内部调用自身函数(俗称:自己调自己)
作用: 可以高效的解决某些特定场景下的问题
注意:所有能够使用递归的场景,都可以通过循环代替
何时才能使用递归:
class TestDiGui{
public static void main(String[] args){
System.out.println("main start");
ma();
System.out.println("main end");
}
public static void ma(){
System.out.println("ma start");
ma();
System.out.println("ma end");
}
}
存在问题:无穷递归,导致栈溢出,程序运行终止
解决问题:设定正确的出口条件(回归条件)
class TestJieCheng{
public static void main(String[] args){
System.out.println(ma(3));
}
public static int ma(int n){
int result = 1;
for(int i=n;i>=1;i--){
result *= i;
}
return result;
}
}
class TestJieCheng1{
public static void main(String[] args){
System.out.println(ma(3));
}
public static int ma(int n){
if(n==0||n==1) return 1;
return n*ma(n-1);
}
}
chap05-数组
概念: 是内存中一块连续的存储空间。可以存储多个相同类型的值,相当于声明了多个相同类型的变量
注意: 数组的长度是固定的、不可变的
创建数组
-
第一种方式 先声明: 数据类型[] 数组名; 例如: int[] arr;
再分配空间: 数组名 = new 数据类型[长度]; 例如:arr = new int[5];
-
第二种方式 推荐使用 声明的同时分配空间:数据类型[] 数组名 = new 数据类型[长度];
例如: int[] arr = new int[5];
-
第三种方式 推荐使用 声明并初始化: 数据类型[] 数组名 = {值1,值2,值3,....};
例如: int[] arr = {1,2,3,4,5};
注意: 声明与初始化不能分开!!!
int[] arr;
arr = {1,2,3,4,5};
-
第四种方式 声明并初始化: 数据类型[] 数组名 = new 数据类型[]{值1,值2,值3,....};
例如: int[] arr = new int[]{1,2,3,4,5};
注意:数组长度与初始化不能同时出现!!
int[] arr = new int[5]{1,2,3,4,5};
补充: 数组数据类型的写法
1. 数据类型[] 数组名;
2. 数据类型 []数组名;
3. 数据类型 数组名[];
推荐使用第一种!!
使用数组
-
数组长度: 数组的长度是固定的,通过 数组名.length 可以获取到 -
数组下标: 数组中的每个存储空间都有一个默认的标号,即下标; 下标范围: 0~length-1 -
数组元素: 数组中的每个存储空间都为元素! -
元素访问: 通过数组下标,可以访问到数组中的每个元素; ? 取值: 数组名[下标]; 赋值: 数组名[下标] = 值;
class TestArray{
public static void main(String[] args){
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
System.out.println(arr[4]);
System.out.println(arr.length);
}
}
-
数组具有默认值,即数组的数据类型的默认值
数据类型 | 默认值 |
---|
整数类型 | 0 | 浮点数类型 | 0.0 | 字符类型 | 0或‘\u0000’所对应的字符–空字符 | 布尔类型 | false | 引用类型 | null |
-
遍历数组: 通过下标逐个访问数组中的每个元素;需要借助for循环,获取每个数组下标,此时循环变量不仅仅控制循环次数,还要充当数组下标 class TestArray{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
深入数组底层
- 数组属于引用类型
- 数组引用(数组名)中存储的是数组首元素地址(首地址)
- 寻址公式: 首元素地址+下标*数组数据类型字节长度
- 数组下标为什么从0开始?答: 为了提高寻址效率!!
- 数组变量之间相互赋值,传递的是地址(首元素地址)
class TestArray1{
public static void main(String[] args){
int a = 10;
int b = a;
System.out.println(b);
b++;
System.out.println(a);
int[] arr = {1,2,3,4,5};
int[] brr = arr;
System.out.println(brr[0]);
brr[0]++;
System.out.println(arr[0]);
}
}
class TestArray2{
public static void main(String[] args){
int a = 10;
int[] arr = {1,2,3,4,5};
ma(a,arr);
System.out.println(a);
System.out.println(arr[0]);
}
public static void ma(int b,int[] brr){
b++;
brr[0]++;
}
}
数组“扩容”
原理:
- 新建一个更大的数组
- 将旧数组中的数据一一复制到新数组中
- 将新数组的地址赋值给旧数组的引用
具体实现方式
-
使用for循环 class TestArray3{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
int[] brr = new int[arr.length*2];
for(int i=0;i<arr.length;i++){
brr[i] = arr[i];
}
arr = brr;
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
-
使用System类中的数组扩容方法arraycopy(…); class TestArray4{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
int[] brr = new int[arr.length*2];
System.arraycopy(arr,0,brr,0,arr.length);
arr = brr;
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
-
借助数组工具类Arrays实现扩容 class TestArray5{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
arr = java.util.Arrays.copyOf(arr,arr.length*2);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
思考:如何实现缩容??
答:只需要将新建的大数组改为小数组即可!
可变长参数(了解)
概念:定义形参时,参数的个数可以不确定;当调用函数时,由传递的实参个数决定
语法:
public static 返回值类型 函数名(数据类型... 参数名){
}
注意:每个函数最多只能定义一个可变长参数,并且必须要定义在形参列表末尾
使用可变长参数:可以将可变长参数当做数组来使用
class Test{
public static void main(String[] args){
print(1,2,3,4);
System.out.println("===========");
print(11,12,13,14,15);
}
public static void print(int... a){
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
二维数组
概念:一维数组的元素还是一维数组
创建语法
1. 先声明: 数据类型[][] 数组名; 例如: int[][] arr;
后分配空间: 数组名 = new 数据类型[高维数组长度][低维数组长度]; 例如:arr = new int[4][2];
2. 声明并分配空间:数据类型[][] 数组名 = new 数据类型[高维数组长度][低维数组长度];
例如: int[][] arr = new int[4][2];
3. 声明并初始化: 数据类型[][] 数组名 = {{值1,值2,值3},{值1,值2},{值1},....{}};
例如: int[][] arr = {{1,2,3},{4,5},{6,7,8,9,0}};
4. 创建不规则二维数组:
数据类型[][] 数组名 = new 数据类型[高维数组长度][];
数组名[高维数组下标] = new 数据类型[低维数组长度];
数组名[高维数组下标] = new 数据类型[低维数组长度];
...
例如:
int[][] arr = new int[4][];
arr[0] = new int[2]; arr[0] = {1,2};
arr[1] = new int[3]; arr[1] = new int[]{3,4,5};
arr[2] = new int[1];
arr[3] = new int[5];
使用数组
通过 数组名[高维数组下标] 可以获取到相应的低维数组
通过 数组名[高维数组下标][低维数组下标] 可以获取到相应的低维数组中相应的下标所对应的元素
数组遍历:需要使用双重for循环
class TestArray1{
public static void main(String[] args){
int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
for(int i=0;i<arr.length;i++){
for(int j =0;j<arr[i].length;j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}
class TestArray2{
public static void main(String[] args){
int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
int max = arr[0][0];
int min = arr[0][0];
for(int i=0;i<arr.length;i++){
for(int j =0;j<arr[i].length;j++){
if(arr[i][j]>max) max = arr[i][j];
if(arr[i][j]<min) min = arr[i][j];
}
}
System.out.println(min+" "+max);
}
}
class TestArray3{
public static void main(String[] args){
int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
int sum = 0;
for(int i=0;i<arr.length;i++){
for(int j =0;j<arr[i].length;j++){
sum += arr[i][j];
}
}
System.out.println(sum);
}
}
数组排序
冒泡排序
概念: 使数组中相邻的两个元素比较大小,根据需求决定是否交换位置
class TestArraySort{
public static void main(String[] args){
int[] arr = {9,6,3,7,2};
for(int i=1;i<arr.length;i++){
for(int j=0;j<arr.length-i;j++){
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
选择排序
概念: 每次选择,都固定一个元素,逐个与其他所有元素比较大小,根据需求决定是否交换位置
class TestArraySort2{
public static void main(String[] args){
int[] arr = {9,6,3,7,2};
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
JDK排序
概念:借助数组工具类Arrays中的排序方法sort完成排序
注意:只能做升序排序!!
class TestArraySort3{
public static void main(String[] args){
int[] arr = {9,6,3,7,2};
java.util.Arrays.sort(arr);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
chap06-面向对象
编程思想
概念:建立解决问题的思路,即编程思想
常见的编程思想
- 面向过程: 自顶向下,逐步求精,注重解决问题的过程,将解决问题分为一个一个的步骤,只要按照步骤执行就一定能解决问题
- 面向对象: 分析解决问题需要的哪些功能,而哪些对象具有这种功能;注重对象之间的配合调用
问题: 回家
面向过程: 拿着地图,分析回家需要经过哪些路口,每条路上走多远,遇到路口往哪儿拐,一步一步就走到家了
面向对象:只需要调用出租车(对象)的载客功能,由出租车送我们回家
注意:面向对象与面向过程并不是完全对立!面向对象是在面向过程的基础上实现的
面向对象
认识对象
什么是对象?
Java眼中的对象: 一切客观存在的事物都是对象!世界就是由对象组成的!—万物皆对象
? 例如:水杯、桌子、电脑、空气、错误等等都是对象
程序中的对象: 内存中的一块存储空间,用来存储对现实对象的关注的部分,从而代表现实中的对象
重新认识类
类与对象的关系
- 类是对象的模板
- 类是人们对一系列相同相似事物(现实中的对象)的认识,抽取出共性定义在类中
- 对象就是根据类创建出来的实例,类似于根据模板生产物品
- 类由属性与方法组成
例如: Computer类 ------》电脑对象
类的组成
-
属性: 有什么 成员变量\实例变量 位置: 类的内部,方法的外部 语法: 1. 声明属性: 数据类型 属性名; 例如:String name;
2. 声明并初始化: 数据类型 属性名 = 值; 例如: int age = 18;
错误语法: 先声明,再赋值
例如: double score;
score = 100;
class Student{
String name;
int age = 18;
double score;
}
属性具有默认值:即属性的数据类型的默认值! 属性的作用范围:至少为整个类的内部 注意:当属性与局部变量命名冲突,局部变量优先!
补充:局部变量与属性的区别
| 局部变量 | 属性 |
---|
位置 | 定义在函数(方法)内部 | 类的内部,方法的外部 | 默认值 | 没有默认值 | 具有默认值 | 作用范围 | 从定义的那一行开始,到定义所在代码块的结束 | 至少整个类的内部 | 命名冲突 | 作用范围重合内,不允许命名重复 | 当属性与局部变量命名冲突,局部变量优先 |
-
方法: 干什么 成员方法 对象的具体的行为:吃喝拉撒睡、工作、学习、玩耍、等等 位置: 类的内部,其他方法的外部 语法: 修饰符 返回值类型 方法名(形参列表){
}
注意:函数是特殊的方法!函数声明中需要写上static修饰符;而方法一般不写static
class Student{
String name = "小郭";
int age = 18;
double score;
public void study(){
System.out.println(name+"沉迷学习无法自拔");
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
}
-
构造方法: Java规定的具有特殊功能的方法 作用: 可以用来配合new关键字创建对象,并且可以在创建对象的过程中选择是否为对象属性赋值! 分类:
-
无参构造方法 没有形参列表,在创建对象时无法为对象属性赋值;可以在对象创建成功后,通过其他方式赋值 语法: public 类名(){} -
有参构造方法 具有形参列表,在创建对象时可以将形参的值赋值给属性 语法: public 类名(形参列表){ //赋值语句 } 注意:
- 构造方法的方法名必须和类名相同!
- 构造方法没有返回值类型,注意连void都没有
- 构造方法无法手动调用,只有在创建对象时被调用一次
- 若代码中没有定义任何构造方法,默认提供一个公开无参构造方法
经验:
- 一般的,若类中具有属性,需要提供有参、无参构造方法
- 可以定义多个形参个数不同的有参构造方法
class Student{
String name = "小红";
int age = 18;
double score;
public void study(){
System.out.println(name+"沉迷学习无法自拔");
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
public Student(){}
public Student(String n,double s){
name = n;
score = s;
}
public Student(String n,int a,double s){
name = n;
age = a;
score = s;
}
}
思考:普通方法与构造方法的区别?
答: 普通方法是人为定义的一些具体功能、行为方法,例如吃喝拉撒睡、工作学习等等
而构造方法它的作用是Java实先规定好的,是用来创建对象的;它们的关系就好像主函数与普通函数的关系
创建对象
创建对象的方式:
-
通过无参构造方法创建对象 语法: 类名 引用 = new 类名(); 例如:Student s1 = new Student(); -
通过有参构造方法创建对象 语法: 类名 引用 = new 类名(实参列表); 例如:Student s2 = new Student(“小郭”,20,80);
使用对象:
- 使用对象的属性:
- 通过 引用.属性名; 获取该对象的该属性
- 通过 引用.属性名 = 值; 完成对该对象的该属性的再次赋值
- 使用对象的方法:
- 通过 引用.方法名(实参列表) 调用该对象的该方法
class TestObject{
public static void main(String[] args){
Student s1 = new Student();
System.out.println(s1.name+" "+s1.age+" "+s1.score);
s1.study();
s1.score = 100;
System.out.println(s1.name+" "+s1.age+" "+s1.score);
Student s2 = new Student("小郭",20,80);
System.out.println(s2.name+" "+s2.age+" "+s2.score);
s2.study();
}
}
class Student{
String name = "小红";
int age = 18;
double score;
public void study(){
System.out.println(name+"沉迷学习无法自拔");
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
public Student(){}
public Student(String n,double s){
name = n;
score = s;
}
public Student(String n,int a,double s){
name = n;
age = a;
score = s;
}
}
方法重载(OverLoad)
概念:允许一个类中定义多个同名的方法,比如,构造方法
语法要求:
- 方法名必须相同
- 形参列表必须不同(个数、类型、顺序不同,仅仅形参名不同,不算重载)
- 修饰符、返回值类型、异常不作要求
使用重载方法:编译器在编译时,会根据实参列表选择调用相应的重载方法,采用就近向上匹配原则
方法重载的好处:对于方法调用者,屏蔽了多个功能相同相似,只是形参不同的方法的差异;方便调用者调用方法
class TestCounter{
public static void main(String[] args){
Counter1 c1 = new Counter1();
System.out.println(c1.addInt(1,2));
System.out.println(c1.addDouble(1.5,2.5));
System.out.println(c1.addIntAndDouble(1,2.5));
System.out.println(c1.addDoubleAndInt(1.5,2));
Counter2 c2 = new Counter2();
System.out.println(c2.add(1,2));
System.out.println(c2.add(1.5,2.5));
System.out.println(c2.add(1,2.5));
System.out.println(c2.add(1.5,2));
}
}
class Counter1{
public int addInt(int a,int b){
return a+b;
}
public double addDouble(double d1,double d2){
return d1+d2;
}
public double addIntAndDouble(int a,double d){
return a+d;
}
public double addDoubleAndInt(double d,int a){
return a+d;
}
}
class Counter2{
public int add(int a,int b){
return a+b;
}
public double add(double d1,double d2){
return d1+d2;
}
public double add(int a,double d){
return a+d;
}
public double add(double d,int a){
return a+d;
}
}
就近向上匹配原则:
class TestJavaKid{
public static void main(String[] args){
int a = 10;
JavaKid jk = new JavaKid();
int n = 1;
jk.study(n);
jk.study(1,2);
}
}
class JavaKid{
public void study(byte n){
System.out.println("byte");
}
public void study(short n){
System.out.println("short");
}
public void study(float n){
System.out.println("float");
}
public void study(double n){
System.out.println("double");
}
public void study(int i,double n){
System.out.println("int -- double");
}
public void study(double n,int i){
System.out.println("double -- int");
}
}
重点:
- 程序中的对象与现实中的对象的的关系
- 类与对象的关系
- 类的组成,每个组成部分的作用及其语法、使用、注意事项
- 普通方法与构造方法的区别
- 无参构造方法与有参构造方法的区别,以及根据二者创建出来的对象的区别
- 创建对象的语法,对象的属性与方法的调用
- 明白int a = 10; 与 JavaKid jk = new JavaKid()的异同
- 基本数据类型变量与引用数据类型变量的异同
- 方法重载的语法及要求
this关键字
public Worker(String n,int a,double s){
name = n;
age = a;
salary = s;
}
存在问题: 不满足标识符命名规范,无法望名知意!
修改后:
public Worker(String name,int age,double salary){
name = name;
age = age;
salary = salary;
}
存在问题: 形参也属于局部变量,此时形参名与属性名重复,局部变量优先,即形参优先!那么此时 name = name中的两个name都是形参name,所以无法完成对属性赋值
需要使用this将局部变量形参与属性区别开!!!
this的用法:
-
用在构造方法或普通方法中,代表当前实例对象
- this.属性名; 代表当前对象的属性
- this.方法名(实参); 代表调用当前对象的方法
public Worker(String name,int age,double salary){
this.name = name;
this.age = age;
this.salary = salary;
}
public void method(){
int age = 20;
System.out.println("局部变量:age"+age);
System.out.println("属性:age"+this.age);
}
-
必须要用在构造方法中,表示调用本类的其他构造方法
- this(实参) 代表调用本类中具有相应形参的构造方法
- 注意: 该语句,必须写在构造方法中的第一行
public Student(String name,int age){
this.name = name;
this.age = age;
}
public Student(String name,int age,double score){
this(name,age);
this.score = score;
}
class TestThis{
public static void main(String[] args){
Student s = new Student("zhangs",18,100);
s.study();
s.method();
}
}
class Student{
String name ;
int age;
double score;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public Student(String name,int age,double score){
this(name,age);
this.score = score;
}
public void study(){
System.out.println(name+"沉迷学习无法自拔");
}
public void method(){
int age = 20;
System.out.println("局部变量:age"+age);
System.out.println("属性:age"+this.age);
}
}
对象的创建过程
- 分配空间(属性),给属性赋默认值
- 属性初始化,给属性赋初始值
- 执行构造方法,给属性赋构造参数值
class ClassA{
public ClassA(){
System.out.println("ClassA");
}
}
class ClassB{
public ClassB(){
System.out.println("ClassB");
}
}
class ClassC{
ClassA ca = new ClassA();
ClassB cb;
public ClassC(){
System.out.println("ClassC");
cb = new ClassB();
}
}
class TestClassC{
public static void main(String[] args){
ClassC cc = new ClassC();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRneiXlT-1628609627989)(CoreJava笔记.assets/image-20210304151600391.png)]
对象的内存结构
Student s1 = new Student();
Student s2 = s1;//只会在栈帧中开辟空间存放s1中的地址,此时s1与s2中保存的地址相同,指向堆中的同一对象
引用
现实生活中的引用:指使用现成的文章或者诗句
Java中的引用:即对象名,通过自身保存现成对象的地址,可以用来代表一个对象
数据类型的分类:
- 基本数据类型:4类8种
- 引用数据类型:String\数组\自定义类名… 无数种
变量的分类:
- 基本数据类型变量: 数据类型 变量名 = 值; 变量名中存值!
- 引用数据类型变量: 类名 引用 = new 类名(); 引用中存地址!
变量之间相互赋值:
-
基本数据类型变量之间传递的是值! int a = 10;
int b = a;
-
引用数据类型变量之间传递的是地址! Student s1 = new Student();
Student s2 = s1;
class Test{
public static void main(String[] args){
int a = 10;
int b = a;
b++;
System.out.println(a);
System.out.println(b);
Student s1 = new Student();
Student s2 = s1;
s2.age = 18;
System.out.println(s1.age);
System.out.println(s2.age);
}
}
chap07-三大特性
封装
问题:目前,可以随意的调用、修改一个对象的属性,这种行为是不合理的!!!
概念:是一个边界,是一个屏障,可以保护对象内部的数据不被外界随意访问
实现封装:借助访问(权限)修饰符–private 私有的
访问修饰符 | 作用范围 |
---|
public 公共的、公开的 | 外界可以随意访问 | private 私有的 | 只能在本类中使用 |
-
属性封装
- 语法:private 数据类型 属性名;
- 注意:一般的,当将属性私有化后,需要提供公开的get\set方法,供外界使用,从而去获取及修改属性
注意:
1. 每个私有属性都必须有自己独立的get(获取属性)\set(修改属性)方法
2. get\set命名规范:get\set+属性名(首字母大写)
3. 若属性的数据类型为boolean类型,get方法可以命名为:is+属性名(首字母大写);set方法不变
4. 可以根据需求,选择是否提供get\set方法
class TestDog{
public static void main(String[] args){
Dog d = new Dog();
d.setName("旺财");
d.setAge(2);
System.out.println(d.getName()+" "+d.getAge());
}
}
class Dog{
private String name;
private int age;
private boolean sex;
public Dog(){}
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public Dog(String name,int age,boolean sex){
this(name,age);
this.sex = sex;
}
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 boolean isSex(){
return sex;
}
public void setSex(boolean sex){
this.sex = sex;
}
}
思考:为什么将属性封装后,还提供get\set方法?
答: 封装只是起到保护的作用,而不是完全隔绝!
-
方法封装 将一些只用于本类,并不希望被外界调用的方法私有化,禁止外界调用!
重点:
- this.与this(实参)的区别及使用注意事项
- 对象的创建过程,明白属性的三个赋值时机
- 明白引用中存储的内容,及对象的存储地方
- 基本数据类型变量与引用数据类型变量的区别:存储、传递
- private与public的区别,作用范围
- get\set方法的作用及语法及注意事项
继承
生活中的继承:一般的,指儿子继承爸爸的一些物品、财产
Java中的继承:指一个类(子类)可以继承(使用)另一个类(父类)的某些属性和方法
概念: 父类是子类共性的抽取!在父类中只能定义所有子类的共性部分;父类—子类是中“一般–特殊”的关系
语法:class 子类类名 extends 父类类名{}
继承的特点:当继承关系建立后,子类可以直接使用父类中可以被子类继承的方法与属性;子类中也可以定义自己独有的属性与方法
继承的好处:减少代码冗余,提高代码的可复用性
注意:
- 一般的,只有满足 is a的关系,才能建立继承关系
- 构造方法不能被继承
- Java是单继承—一个子类只能有一个直接父类!但是可以多级继承,可以被继承的属性或方法也是叠加的
补充: 访问修饰符
访问修饰符 | 本类 | 同包 | 其他包子类 | 不同包不子类 | 继承性 |
---|
public公开的 | √ | √ | √ | √ | 可以被继承 | protected受保护的 | √ | √ | √ | | 可以被继承 | default默认的 | √ | √ | | | 同包子类可继承 | private私有的 | √ | | | | 不能被继承 |
从宽到严: public -> protected -> default -> private
注意:
- 以上四个访问修饰符都可以修饰 属性、方法、构造方法
- 只有个public与default可以修饰类
- 以上四个均不可以修饰局部变量
package d10;
public class ClassA{
public int a = 10;
protected int b = 20;
int c = 30;
private int d = 40;
}
======================================
package d10;
public class ClassB extends ClassA{
public ClassB(int a,int b,int c,int d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
}
=======================================
package p10;
import d10.ClassA;
public class ClassC extends ClassA{
public ClassC(int a,int b,int c,int d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
}
=======================================
package p10;
import d10.ClassA;
public class ClassD{
public static void main(String[] args){
ClassA ca = new ClassA();
System.out.println(ca.a);
System.out.println(ca.b);
System.out.println(ca.c);
System.out.println(ca.d);
}
}
class TestAnimal{
public static void main(String[] args){
Dog d = new Dog("旺财",2,"红色");
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name+" "+d.age+" "+d.color);
}
}
class Animal{
String name;
int age;
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
String color;
public void lookDoor(){
System.out.println("看门门");
}
public Dog(){}
public Dog(String name,int age,String color){
this.name = name;
this.age = age;
this.color = color;
}
}
class Bird extends Animal{
String pinZhong;
public void fly(){
System.out.println("飞高高");
}
public Bird(){}
public Bird(String name,int age,String pinZhong){
this.name = name;
this.age = age;
this.pinZhong = pinZhong;
}
}
class Fish extends Animal{
double size;
public void swim(){
System.out.println("划水水");
}
public Fish(){}
public Fish(String name,int age,double size){
this.name = name;
this.age = age;
this.size = size;
}
}
方法覆盖
概念:OverRide,又称为方法重写,即在子类中可以将从父类继承到的方法进行重新实现
语法要求:
- 方法三要素(返回值类型、方法名、形参列表)必须完全相同
- 访问修饰符相同或更宽
注意:
- 子类中覆盖后的方法优先执行
- 若父类中有一个方法使用private修饰,此时子类中有一个方法三要素完全与之相同、但访问修饰符为public的方法,此时不构成方法覆盖,因为子类继承不到该方法
class TestAnimal{
public static void main(String[] args){
Dog d = new Dog("旺财",2,"红色");
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name+" "+d.age+" "+d.color);
}
}
class Animal{
String name;
int age;
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
String color;
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
public Dog(){}
public Dog(String name,int age,String color){
this.name = name;
this.age = age;
this.color = color;
}
}
对象的创建过程
- 无父类的创建过程
- 分配空间,为属性赋默认值
- 属性初始化,为属性赋初始值
- 执行构造方法,完成对象的创建,为属性赋构造参数值
- 有父类的创建过程
- 分配空间(父类+子类),为父子类属性赋默认值
- 初始化父类属性,为父类属性赋初始值
- 执行父类构造方法,完成父类对象的创建,为父类属性赋构造参数值
- 初始化子类属性,为子类属性赋初始值
- 执行子类构造方法,完成子类对象的创建,为子类属性赋构造参数值
- 总结:
- 创建子类对象时,必须先创建父类对象
- 子类对象由父类对象加上子类独有内容共同组成
class TestAnimal{
public static void main(String[] args){
Animal a = new Animal("动物",1);
System.out.println(a.getName());
Dog d1 = new Dog("狗1",2,"黑色");
System.out.println(d1.getName());
Dog d2 = new Dog();
System.out.println(d2.getName());
}
}
super关键字
当子类中定义了与父类中同名的属性时(属性遮蔽)或发生了方法覆盖时,需要加以区分,才能专项访问
super的两种用法:
-
用在构造方法、普通方法中,代表父类对象
- super.属性名; 获取父类属性
- super.方法名(); 调用父类方法
注意:该用法中的属性与方法必须是子类可以继承到的
class Dog extends Animal{
String color;
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
public Dog(){}
public Dog(String name,int age,String color){
super.name = name;
super.age = age;
this.color = color;
}
}
=========================================================
class TestMyClass{
public static void main(String[] args){
MyClassB mb = new MyClassB();
mb.method();
mb.print();
}
}
class MyClassA{
int value = 10;
public void print(){
System.out.println(value);
}
}
class MyClassB extends MyClassA{
int value = 20;
public void print(){
super.print();
System.out.println(value);
}
public void method(){
int value = 30;
System.out.println("局部变量:"+value);
System.out.println("子类属性:"+this.value);
System.out.println("父类属性:"+super.value);
}
}
-
必须用在子类构造方法中, 用来制定创建父类对象时,使用父类的哪个构造方法
- super(); 调用父类无参构造方法
- super(实参); 调用父类有参构造方法
注意:
- super(…)必须写在子类构造方法中的第一行
- this(…)与super(…)不能同时出现
- 若子类构造方法第一行不是this(…)也不是super(…),则默认为super();
class Dog extends Animal{
String color;
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
public Dog(){}
public Dog(String name,int age,String color){
super(name,age);
this.color = color;
}
}
面向对象代码规范写法:
class Animal{
private String name;
private int 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;
}
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
}
class Dog extends Animal{
private String color;
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
public Dog(){}
public Dog(String name,int age,String color){
super(name,age);
this.color = color;
}
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
}
多态
一个动物------> 狗
一个动物------> 猫
Dog d = new Dog(); 狗 是狗
Animal a = new Dog(); 狗 是动物
概念:父类的引用可以指向子类的对象
语法:父类类名 引用 = new 子类类名(实参);
特点:
- 父类的引用指向子类对象,并不会改变对象的实际类型
- 当使用多态时,只能调用引用类型中具有的方法
- 当使用多态时,会优先执行子类中覆盖后的方法
总结:方法调用看引用类型;方法的执行结果看对象的实际类型(子类类型)
注意:方法具有多态,属性没有多态!调用属性的引用是什么类型,就会获取什么类型中的属性。
class TestAnimal{
public static void main(String[] args){
Animal a = new Animal("a");
a.eat();
a.sleep();
System.out.println(a.name);
Dog d = new Dog("d");
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name);
Animal d1 = d;
System.out.println(d1.name);
Animal dog = new Dog("dog");
dog.eat();
dog.sleep();
System.out.println(dog.name);
}
}
class Animal{
String name;
public Animal(){}
public Animal(String name){
this.name = name;
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
}
class Dog extends Animal{
public Dog(){}
public Dog(String name){
super(name);
}
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NAzKCIhX-1628609627992)(CoreJava笔记.assets/image-20210308113522836.png)]
引用类型的类型转换
-
子类类型引用可以直接赋值给父类类型引用 Dog d = new Dog();
Animal a = d;
-
在多态场景下,父类类型引用赋值给子类类型引用,需要强转 Animal a = new Dog();
Dog d = (Dog)a;
========================================================================
Animal a = new Cat();
Dog d = (Dog)a;
========================================================================
Animal a = new Animal();
Dog d = (Dog)a;
========================================================================
Cat c = new Cat();
Dog d3 = (Dog)c;
-
没有继承关系的两种类型,不允许相互转换 Person p = new Person();
Dog d = (Dog)p;
instanceof关键字
语法: 引用 instanceof 类名 注意:这是一个boolean表达式,结果要么为true,要么为false
作用:判断引用指向的对象的实际类型与关键字后面的类名类型是否兼容,若兼容,则返回true,表示可以强转;若不兼容,则返回false,表示不能强转;
例如:
if(a instanceof Dog){
Dog d = (Dog)a;
}else{
Cat c = (Cat)a;
}
====================================================================================
class TestInstanceof{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
Animal[] as = new Animal[4];
as[0] = new Dog();
as[1] = new Cat();
as[2] = new Dog();
as[3] = new Cat();
for(int i=0;i<as.length;i++){
if(as[i] instanceof Dog){
Dog d = (Dog)as[i];
d.lookDoor();
}else{
Cat c = (Cat)as[i];
c.catchMouse();
}
}
}
}
多态的应用场景
-
应用在数组上,定义父类类型数组,可以存储子类类型对象; Animal[] as = {
new Animal(),
new Dog(),
new Cat()
}
-
应用在方法形参上,方法形参定义为父类类型,调用方法时,传入实参的类型可以为父类对象或其子类对象 -
应用在返回值类型上,方法返回值类型定义为父类类型,方法执行结束时,可以返回父类对象或其子类对象 class Test{
public static void main(String[] args){
Dog d = new Dog();
Cat c = new Cat();
Wolf w = new Wolf();
Tiger t = new Tiger();
feed(d);
feed(c);
Animal a = sell(2000000);
System.out.println(a);
}
public static void feed(Animal a){
a.eat();
}
public static Animal sell(double money){
if(money>1000000){
return new Tiger();
}else if(money>10000){
return new Wolf();
}else{
return new Dog();
}
}
}
class Animal{
public void eat(){
System.out.println("吃饭饭");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
}
class Wolf extends Animal{
public void eat(){
System.out.println("狼吃喜洋洋");
}
}
class Tiger extends Animal{
public void eat(){
System.out.println("老虎吃人");
}
}
多态的好处
- 减少代码冗余,提高代码的可重用性
- 解耦合(依赖倒转原则)
重点:
- 多态的概念及语法,三个特点
- 理解:方法具有多态,属性没有多态
- instanceof的真正用法,及其判断的内容
- 多态的三个应用场景
chap08-三大修饰符
abstract
概念:抽象的,似是而非的,像却不是的
用法:
-
修饰类,抽象类,不完整的类 语法: abstract class 类名 特点:
- 不能实例化,即不能创建对象
- 可以被子类继承,作为子类对象的引用,强行使用多态
- 有构造方法,供子类使用
class TestAbstract{
public static void main(String[] args){
Dog d = new Dog("旺财");
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name);
Animal dog = new Dog("来福");
dog.eat();
dog.sleep();
System.out.println(dog.name);
}
}
abstract class Animal{
String name;
public Animal(){}
public Animal(String name){
this.name = name;
}
public void eat(){
System.out.println("吃饭饭");
}
public void sleep(){
System.out.println("睡觉觉");
}
}
class Dog extends Animal{
public Dog(){}
public Dog(String name){
super(name);
}
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
}
-
修饰方法,即抽象方法,不完整的方法—没有方法实现 语法: 访问修饰符 abstract 返回值类型 方法名(形参列表);
或
abstract 访问修饰符 返回值类型 方法名(形参列表);
注意:修饰符之间没有顺序要求
特点:
- 抽象方法没有实现,即{}使用;代表,表示结束
- 抽象方法只有方法声明,作为标准,供子类去实现,统一所有子类中该方法的声明定义
abstract class Animal{
String name;
public Animal(){}
public Animal(String name){
this.name = name;
}
public abstract void eat();
public void sleep(){
System.out.println("睡觉觉");
}
}
class Dog extends Animal{
public Dog(){}
public Dog(String name){
super(name);
}
public void eat(){
System.out.println("狗吃骨头");
}
public void lookDoor(){
System.out.println("看门门");
}
}
补充:抽象类与抽象方法的关系
- 抽象类中可以有抽象方法,也可以没有抽象方法
- 抽象类中可以有普通方法
- 有抽象方法的类,一定是抽象类
- 若子类继承了一个抽象父类,必须实现父类中的所有抽象方法,否则自身也将成为抽象类
抽象的好处:
- 更加的贴近现实,符合客观规律
- 更加符合父类抽取子类共性的特点
- 将标准分离—方法声明定义在父类中,而方法实现由子类完成
抽象与多态的好处---------解耦合!!
class TestLamp{
public static void main(String[] args){
Lamp l = new Lamp();
l.setBulb(new RedBulb());
l.on();
l.setBulb(new GreenBulb());
l.on();
l.setBulb(new YellowBulb());
l.on();
}
}
class Lamp{
Bulb bulb;
public void setBulb(Bulb bulb){
this.bulb = bulb;
}
public void on(){
bulb.shine();
}
}
abstract class Bulb{
public abstract void shine();
}
class RedBulb extends Bulb{
public void shine(){
System.out.println("发红光");
}
}
class GreenBulb extends Bulb{
public void shine(){
System.out.println("发绿光");
}
}
class YellowBulb extends Bulb{
public void shine(){
System.out.println("发黄光");
}
}class TestLamp{
public static void main(String[] args){
Lamp l = new Lamp();
l.setBulb(new RedBulb());
l.on();
l.setBulb(new GreenBulb());
l.on();
l.setBulb(new YellowBulb());
l.on();
}
}
class Lamp{
Bulb bulb;
public void setBulb(Bulb bulb){
this.bulb = bulb;
}
public void on(){
bulb.shine();
}
}
abstract class Bulb{
public abstract void shine();
}
class RedBulb extends Bulb{
public void shine(){
System.out.println("发红光");
}
}
class GreenBulb extends Bulb{
public void shine(){
System.out.println("发绿光");
}
}
class YellowBulb extends Bulb{
public void shine(){
System.out.println("发黄光");
}
}
static
概念: 静态的
用法:
-
修饰属性,即静态属性,又称为静态变量、全局变量、类变量 语法: 访问修饰符 static 数据类型 属性名;
或
static 访问修饰符 数据类型 属性名;
特点:
- 静态属性是全类共有----根据该类创建出的所有对象,共享该属性,不独属于某个对象
- 静态属性可以通过类名直接调用,不依赖对象引用----->类名.静态属性名;
class TestStatic{
public static void main(String[] args){
System.out.println(MyClass.b);
MyClass mc1 = new MyClass();
mc1.a = 10;
mc1.b = 100;
MyClass mc2 = new MyClass();
mc2.a = 20;
mc2.b = 200;
System.out.println(mc1.a+" "+mc1.b);
System.out.println(mc2.a+" "+mc2.b);
mc1.b = 300;
System.out.println(mc1.a+" "+MyClass.b);
System.out.println(mc2.a+" "+MyClass.b);
}
}
class MyClass{
int a;
static int b;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4umCfcbv-1628609627993)(CoreJava笔记.assets/image-20210309152430505.png)]
补充:
- 静态属性是什么时候分配空间并赋予默认值的?
答:类加载的时候,分配空间并赋予默认值
- 什么是类加载?
答:当JVM第一次使用某个类时,会根据ClassPath(类路径)找到该类的字节码文件(存放着该类的全部信息),读取(加载)字节文件,将类的全部信息加载到JVM中的过程,就是类加载;一般的,类加载只会进行一次
- 类加载的时机?
答:
- 第一次创建对象时
- 第一次调用类中的静态成员时
- 创建子类对象会引发父类类加载
- 使用Class.forName(“类的权限定名”);
- 只声明引用,不会引发类加载
-
修饰方法,即静态方法,也称为函数 语法: 访问修饰符 static 返回值类型 方法名(形参列表){
}
特点:
-
静态方法可以通过类名直接调用---->类名.静态方法名(实参); -
静态方法中不能直接访问本类的非静态成员(静态属性、静态方法) 思考:为什么函数都需要使用static修饰? 答:因为主函数是静态的 -
静态方法中不能使用this和super -
静态方法只能被静态方法覆盖,但是没有多态—>调用该方法的引用是什么类型,就执行什么类型中的静态方法
思考:
问题1:为什么静态方法中不能直接访问本类的非静态成员?
答:因为静态方法只需要使用类名就可以调用,所以执行静态方法时,对象可能还没有创建出来,而非静态成员(即普通方法、普通属性)都是必须依赖对象才能调用的,所以静态方法中不能直接访问本类的非静态成员
问题2:为什么静态方法中不能使用this和super?
答:因为静态方法只需要使用类名就可以调用,所以执行静态方法时,对象可能还没有创建出来,而this代表当前对象,super代表父类对象
class TestClass{
public static void main(String[] args){
ClassA c = new ClassB();
c.m1();
c.m2();
ClassB cb = (ClassB)c;
cb.m1();
cb.m2();
}
}
class ClassA{
int a = 10;
static int b = 20;
public void m1(){
System.out.println(a);
System.out.println(b);
}
public static void m2(){
System.out.println(b);
ClassA ca = new ClassA();
System.out.println(ca.a);
}
}
class ClassB extends ClassA{
public void m1(){
System.out.println("ClassB--m1");
}
public static void m2(){
System.out.println("ClassB--m2");
}
}
class ClassC{
int c = 30;
}
-
修饰初始代码块,即静态代码块
- 初始代码块,又称为动态代码块
- 位置: 类的内部,方法的外部
- 语法: { //代码 }
- 特点: 每次创建对象时,都会自动执行一次,无法手动调用
- 作用: 一般用于初始化属性,由于属性可以直接初始化,所以初始代码块不常用!
- 静态代码块
- 位置:类的内部,方法的外部
- 语法:static{ //代码 }
- 特点:只会在类加载时自动执行一次,无法手动调用
- 作用:一般用于后期读取配置文件
class TestClassD{
public static void main(String[] args){
new ClassD();
new ClassD();
}
}
class ClassD{
int a = 10;
{
a = 20;
System.out.println("初始代码块1:"+a);
}
{
a = 30;
System.out.println("初始代码块2:"+a);
}
static{
System.out.println("静态代码块");
}
public ClassD(){
System.out.println("无参构造方法");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvTUgCOm-1628609627993)(CoreJava笔记.assets/image-20210309164453611.png)]
final
概念: 最终的、最后的、不可变的
用法:
-
修饰变量(局部变量、属性),也称为常量 语法: final 数据类型 变量名; 特点:一经赋值,不可改变 分类:
- 基本数据类型常量 值不能改
- 引用数据类型常量 地址不能改,但是地址指向的对象内容可变
class TestFinal{
public static void main(String[] args){
final int n = 10;
System.out.println(n);
final MyClass mc = new MyClass();
mc.a++;
System.out.println(mc.a);
}
}
class MyClass{
int a = 100;
}
补充:被final修饰的属性没有默认值!!!
注意:
- 若final修饰普通属性,必须在属性初始化或初始代码块中或构造方法中有且必须把握一次赋值机会,给该属性完成赋值;若选择使用构造方法赋值,必须保证所有构造方法中都有该属性的赋值语句,保证无论使用哪个构造方法创建对象,该属性都是有值的!!
class TestClass{
public static void main(String[] args){
ClassA ca1 = new ClassA();
ClassA ca2 = new ClassA(20);
ClassA ca3 = new ClassA(30);
System.out.println(ca1.a);
System.out.println(ca2.a);
System.out.println(ca3.a);
}
}
class ClassA{
final int a;
public ClassA(){
a = 10;
}
public ClassA(int a){
this.a = a;
}
}
- 若final修饰静态属性,必须在属性初始化或静态代码块中有且必须把握一次赋值机会
class ClassB{
final static int b;
static{
b = 20;
}
}
-
修饰方法,即最终的方法,只能被继承,不能被覆盖 class TestSuper{
public static void main(String[] args){
Sub sb = new Sub();
sb.m1();
}
}
class Super{
public final void m1(){
System.out.println("Super m1");
}
}
class Sub extends Super{
}
-
修饰类,即最终的类(断子绝孙类),不能被继承 final class ClassE{}
class ClassF extends ClassE{}
总结
- private不能与abstract连用;
- static不能与abstract连用;
- final不能与abstract连用;
- private\static\final是可以随意组合的;
重点:
- 抽象类与抽象方法的特点
- 抽象类与抽象方法的关系
- 静态属性与静态方法都可以通过类名直接调用—》不依赖对象
- 静态方法的使用要求—》为什么不能直接访问本类的非静态成员?为什么不能使用this和super
- 静态代码块的特点
- 什么是类加载?类加载的时机
- 基本数据类型常量与引用数据类型常量的区别
- 当final修饰普通属性和静态属性时,属性的赋值时机
chap09-接口
概念:
- 从语法角度上,接口是特殊的抽象类
- 是一种功能,实现接口往往意味着为实现类扩展了一种功能
- 是一个标准,是接口定义者和接口调用者都需要遵守的标准
定义语法: interface 接口名{}
特点:
- 接口中的属性,都是公开静态常量:public static final 数据类型 属性名;
- 接口中的方法,都是公开抽象方法:public abstract 返回值类型 方法名(形参列表);
- 接口不能独立创建对象,但可以作为引用指向实现类对象
- 接口中没有构造方法,不参与对象的创建过程
- 接口编译后,依然生成.class文件
abstract class ClassA{
public static final int M = 10;
public static final int N = 20;
public abstract void m1();
public abstract void m2();
}
interface ClassA{
public static final int M = 10;
public static final int N = 20;
public abstract void m1();
public abstract void m2();
}
interface IA{
int M = 10;
int N = 20;
void m1();
void m2();
}
实现接口:class 实现类类名 implements 接口名{}
注意:实现类若不想成为抽象类,必须实现接口中的所有抽象方法
class TestInterface{
public static void main(String[] args){
IAImpl ia1 = new IAImpl();
ia1.m1();
ia1.m2();
IA ia2 = new IAImpl();
ia2.m1();
ia2.m2();
}
}
interface IA{
int M = 10;
int N = 20;
void m1();
void m2();
}
class IAImpl implements IA{
public void m1(){
System.out.println("IAImpl -- m1");
}
public void m2(){
System.out.println("IAImpl -- m2");
}
}
继承性:
- 类与类之间是单继承: 一个子类只能有一个直接父类
- class 子类类名 extends 父类类名{}
- 接口与接口之间是多继承: 一个子接口可以有多个父接口
- interface 子接口 extends 父接口1,父接口2,…{}
类与接口的关系:
- 实现关系:一个类实现一个接口
- class 实现类 implements 接口{}
- 先继承,再实现
- class 子类 extends 父类 implements 接口{}
- 先继承,再多实现
- class 子类 extends 父类 implements 接口1,接口2,…{}
思考:父类与接口的区别?–亲爹与干爹的区别?
手机
? 主要共性:打电话 父类–电话
? 次要共性:玩游戏、听音乐、拍照、上网等 接口
答: 主要共性放在父类中,次要共性放在接口中;当父类无法满足子类的需求时,可以通过实现接口,扩展子类的功能
随堂小练习:
定义电话类(Phone),具有打电话方法(call)
定义游戏接口(Game),具有玩游戏方法(playGame)
定义音乐接口(Music),具有听音乐方法(listenToMusic)
定义手机类(MobilePhone),继承电话类并覆盖打电话方法,实现游戏、音乐接口并实现玩游戏和听音乐方法
abstract class Phone{
public abstract void call();
}
interface Game{
void playGame();
}
interface Music{
void listenToMusic();
}
class MobilePhone extends Phone implements Game,Music{
public void call(){
System.out.println("手机打电话");
}
public void playGame(){
System.out.println("手机玩游戏");
}
public void listenToMusic(){
System.out.println("手机听音乐");
}
}
接口中的多态
-
和父类一样,接口可以作为引用,指向实现类对象—> 接口 引用 = new 实现类类名(实参); -
当使用多态时,通过引用只能调用引用类型中具有的方法 -
实现类对象可以有多个不同类型的引用,表示看待该对象的角度不同 class TestPhone{
public static void main(String[] args){
MobilePhone mp = new MobilePhone();
mp.call();
mp.playGame();
mp.listenToMusic();
Phone p = mp;
p.call();
Game g = mp;
g.playGame();
Music m = mp;
m.listenToMusic();
}
}
-
接口多态的应用场景:和父类多态的应用场景一样;相比起父类,接口更加适合多态的特性,此时不需要再关注对象的类型(父类),只需要关注对象是否具有某个功能(接口)即可 class TestStudent{
public static void main(String[] args){
Student s = new Student();
s.relax(new MobilePhone());
s.relax(new Computer());
}
}
class Computer implements Game{
public void playGame(){
System.out.println("电脑玩游戏");
}
}
class Student{
public void relax(Game g){
g.playGame();
}
}
引用类型的类型转换
回顾昨日:
- 子类引用可以直接赋值给父类引用
- 多态场景下,父类引用赋值给子类引用需要强转
- 没有继承关系的类型,不允许相互转换
放眼当下:
- 任意类型都可以通过强转,赋值给接口类型的引用!编译通过,但是运行可能报错,可以在转换之前,通过instanceof关键字判断一下,是否兼容
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
interface Person{}
class Snoopy extends Dog implements Person{}
class TestAnimal{
public static void main(String[] args){
Animal a = new Dog();
Dog d = (Dog)a;
System.out.println((a instanceof Person));
Person p1 = (Person)a;
}
}
接口回调
概念:先有接口的使用者,再有接口的实现者
class TestStudent{
public static void main(String[] args){
int[] arr = {3,6,1,9,2};
Arrays.sort(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");
}
System.out.println();
String[] ss = {"d","B","s","A"};
Arrays.sort(ss);
for(int i=0;i<ss.length;i++){
System.out.print(ss[i]+"\t");
}
System.out.println();
Student[] stus = {
new Student("张三",17,90),
new Student("李四",19,80),
new Student("王五",18,100),
new Student("赵六",20,70)
};
Arrays.sort(stus);
for(int i=0;i<stus.length;i++){
System.out.println(stus[i].name+" "+stus[i].age+" "+stus[i].score);
}
}
}
class Student implements Comparable<Student>{
String name;
int age;
double score;
public Student(){}
public Student(String name,int age,double score){
this.name = name;
this.age = age;
this.score = score;
}
public int compareTo(Student s){
if(this.score>s.score) return -1;
if(this.score<s.score) return 1;
return 0;
}
}
高版本语法补充
JDK8.0
-
默认方法 default 返回值类型 方法名(形参列表){
}
注意:该方法修饰符依然为public
-
静态方法 static 返回值类型 方法名(形参列表){
}
注意:该方法修饰符依然为public
JDK9.0
-
私有方法 private 返回值类型 方法名(形参列表){
}
补充: 接口与抽象类的区别
| 抽象类 | 接口 |
---|
关键字 | abstract class\extends | interface\implements | 属性 | 不做要求 | 必须是公开静态常量 | 方法 | 可以有、也可以没有抽象方法 | JDK8.0之前,必须是公开抽象方法 JDK8.0之后,可以定义具有方法实现的默认、私有、静态方法 | 构造方法 | 有构造方法 | 没有构造方法 | 继承性 | 单继承 | 多继承 |
接口分类
- 常量接口 只定义属性,不定义方法
- 标记接口 不定义属性,也不定义方法,只是作为一个标记;例如:Serializable 序列化接口
- 函数(式)接口 只定义了一个抽象方法的接口
- 普通接口 至少定义两个抽象方法
接口的好处
- 多继承 将共性分为主要共性与次要共性,主要共性可以放在父类中,次要共性放在接口
- 解耦合,降低程序各个模块之间的耦合性
- 方便程序设计、框架的搭建
重点:
- 接口的基本语法(定义、实现)
- 接口的多态,接口可以作为引用指向实现类对象,通过接口引用只能调用引用类型中的方法
- 先通过instanceof判断是否兼容,再决定是否强转
- Comparable接口及compareTo()
- 抽象类与接口的区别
- 接口分类
chap10-内部类
概念:在类的内部再定义一个类,即内部类
特点:
- 可以直接访问外部类的私有成员,不破坏封装
- 可以为外部类提供必要的功能组件
- 编译生成独立的.class文件
分类:
- 成员内部类 了解即可
- 静态内部类 了解即可
- 局部内部类 掌握
- 匿名内部类 熟练
成员内部类
概念:定义在类的内部,方法的外部,类似于成员变量
特点:
-
成员内部类对象的创建,需要依赖外部类对象 外部类类名.内部类类名 引用 = 外部类对象名.new 内部类类名(); -
若外部类属性与内部类属性同名:可以通过this.属性名代码内部类属性;通过外部类类名.this.属性名代表外部类属性 -
成员内部类中不允许定义静态成员
class Test{
public static void main(String[] args){
Outer out = new Outer();
Outer.Inner inn = out.new Inner();
inn.method();
}
}
class Outer{
private String str = "外部类私有属性";
static String sta_str = "外部类静态属性";
class Inner{
private String str = "内部类私有属性";
public void method(){
String str = "内部类局部变量";
System.out.println(str);
System.out.println(this.str);
System.out.println(Outer.this.str);
}
}
}
静态内部类
概念: 定义在类的内部,方法的外部,使用static修饰的类,类似于静态变量
特点:
-
静态内部类对象的创建需要依赖外部类类名 外部类类名.内部类类名 引用 = new 外部类类名.内部类类名(); -
静态内部类的静态成员,可以通过 外部类类名.内部类类名.静态属性|静态方法 -
静态内部类只能访问外部类的静态成员
class Test{
public static void main(String[] args){
Outer.Inner inn = new Outer.Inner();
inn.inn_method();
System.out.println(Outer.Inner.innSta_str);
}
}
class Outer{
private String str = "外部类私有属性";
static String outSta_str = "外部类静态属性";
static class Inner{
private String innStr = "内部类私有属性";
static String innSta_str = "内部类静态属性";
public void inn_method(){
System.out.println(innStr);
System.out.println(innSta_str);
System.out.println(outSta_str);
}
}
}
局部内部类
概念:定义在方法内部的类,类似于局部变量
特点:
-
局部内部类的作用范围:从定义的那一行开始,到定义所在代码块的结束 -
局部内部类中不允许定义静态成员 -
局部内部类可以访问外部类的局部变量,但是该局部变量必须是常量! JDK8.0之前,该局部变量前必须显示加上final修饰符 JDK8.0之后,只要该局部变量是一个事实上的常量(即值没有改变过),可以不加final
class Test{
public static void main(String[] args){
Outer out = new Outer();
out.out_method();
}
}
class Outer{
private String str = "外部类私有属性";
static String outSta_str = "外部类静态属性";
public void out_method(){
final int a = 10;
int b = 20;
class Inner{
private String innStr = "局部内部类私有属性";
public void inn_method(){
System.out.println(str);
System.out.println(outSta_str);
System.out.println(innStr);
System.out.println(a);
System.out.println(b);
}
}
Inner inn = new Inner();
inn.inn_method();
}
}
==================================================================
interface JavaTeacher{
void teach();
}
class School{
public static JavaTeacher getTeacher(int i){
class JavaTeacherA implements JavaTeacher{
public void teach(){
System.out.println("JavaTeacherA teach Java");
}
}
class JavaTeacherB implements JavaTeacher{
public void teach(){
System.out.println("JavaTeacherB teach Java");
}
}
if(i==0)return new JavaTeacherA();
else return new JavaTeacherB();
}
}
class TestSchool{
public static void main(String[] args){
JavaTeacher jt = School.getTeacher(0);
jt.teach();
}
}
匿名内部类
概念: 特殊的局部内部类,将类的定义、方法的实现、对象的创建三合一
语法:接口名|类名 引用 = new 接口名|类名(){ //代码 };
特点:
- 具有局部内部类所有的特点
- 只能用来实现一个接口或继承一个类,一般的都用来实现接口
- 只能创建一个对象
- 不允许定义构造方法,只有一个默认的公开无参构造方法
- 定义匿名内部类,本质上是得到了一个接口实现类对象或子类对象
class Test{
public static void main(String[] args){
IA ia1 = new IAImpl();
ia1.ma();
class ClassA implements IA{
public void ma(){
System.out.println("局部内部类");
}
}
IA ia2 = new ClassA();
ia2.ma();
IA ia3 = new IA(){
public void ma(){
System.out.println("匿名内部类");
}
};
ia3.ma();
}
}
interface IA{
void ma();
}
class IAImpl implements IA{
public void ma(){
System.out.println("接口外部实现类IAImpl");
}
}
==============================================================
interface JavaTeacher{
void teach();
}
class School{
public static JavaTeacher getTeacher(int i){
if(i==0){
JavaTeacher jt = new JavaTeacher(){
public void teach(){
System.out.println("JavaTeacherA teach Java");
}
};
return jt;
}else{
return new JavaTeacher(){
public void teach(){
System.out.println("JavaTeacherB teach Java");
}
};
}
}
}
class TestSchool{
public static void main(String[] args){
JavaTeacher jt = School.getTeacher(10);
jt.teach();
}
}
Lambda表达式
概念:匿名内部类的简化,只能实现函数式接口,三合一
语法:接口名 引用 = (形参列表)->{ //方法实现 };
? -> 左边:Lambda表达式的参数部分,即函数式接口中抽象方法的形参列表
? -> 右边:Lambda表达式的执行代码部分,即对函数式接口中抽象方法的实现
class TestLambda{
public static void main(String[] args){
IA ia1 = new IA(){
public void ma(){
System.out.println("匿名内部类");
}
};
ia1.ma();
IA ia2 = ()->{System.out.println("Lambda表达式");};
ia2.ma();
}
}
interface IA{
void ma();
}
注意:
- Lambda只能用来实现函数式接口
- Lambda表达式本质上是一个接口实现类对象
Lambda表达式细节优化
-
参数部分的数据类型可以省略 IB ib1 = (int n)->{System.out.println(n);};
IB ib2 = (n)->{System.out.println(n);};
ib2.mb(10);
IC ic1 = (int n,int m)->{System.out.println(n+m);};
IC ic2 = (n,m)->{System.out.println(n+m);};
ic2.mc(2,3);
-
若只有一个参数,小括号可以省略 IB ib1 = (int n)->{System.out.println(n);};
IB ib2 = (n)->{System.out.println(n);};
IB ib3 = n->{System.out.println(n);};
ib3.mb(10);
-
若Lambda表达式的代码部分,只有一行代码,大括号可以省略 IA ia2 = ()->{System.out.println("Lambda表达式");};
IA ia3 = ()->System.out.println("Lambda表达式");
ia3.ma();
IB ib1 = (int n)->{System.out.println(n);};
IB ib2 = (n)->{System.out.println(n);};
IB ib3 = n->{System.out.println(n);};
IB ib4 = n->System.out.println(n);
ib4.mb(10);
-
若Lambda表达式的代码部分,只有一行return语句,return和大括号都可以省略 ID id1 = (int n,int m)->{return n+m;};
ID id2 = (n,m)-> n+m;
=============================================================
ID id1 = (int n,int m)->{return n+m;};
ID id2 = (n,m)-> return n+m;
chap11-常用类
Object类
概念: 又称为根类、超类、基类;Java中所有类的父类(直接或间接)
特点:
- Object类中定义了所有类都适用的一些方法,可以供其他类去继承;也可以根据需求,将这些方法进行覆盖
- Object类型的引用可以指向任意类型的对象
- 若一个类没有显示继承一个父类,则默认继承Object类
常用方法:
-
getClass() 获取对象的实际类型,可以用于判断两个对象的类型是否相同 class TestObject{
public static void main(String[] args){
Animal a1 = new Dog();
Animal a2 = new Cat();
Animal a3 = new Dog();
System.out.println(a1.getClass());
System.out.println(a2.getClass());
System.out.println(a1.getClass()==a2.getClass());
System.out.println(a1.getClass()==a3.getClass());
}
}
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
-
boolean equals(Object o) 判断对象内容是否相等,方法内部默认比较的还是地址,所以需要进行覆盖,使之比较对象内容 注意:从此以后,若两个对象的内容(属性)完全相同,即认为是同一对象; 注意:一般情况下,重写equals()方法时,也要重写hashCode()方法,保证相同对象必须返回相同哈希码,不同对象尽量返回不同哈希码! class TestStudent{
public static void main(String[] args){
int a = 10;
int b = 10;
System.out.println(a==b);
Student s1 = new Student("张三",18);
Student s2 = new Student("张三",18);
Student s3 = s1;
Student s4 = new Student("张三",19);
System.out.println(s1==s2);
System.out.println(s1==s3);
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s1.equals(s4));
}
}
class Student{
String name;
int age;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object o){
if(this==o) return true;
if(o==null) return false;
if(this.getClass()!=o.getClass()) return false;
Student s = (Student)o;
if(this.name.equals(s.name)&&this.age==s.age)return true;
return false;
}
}
-
int hashCode() 可以返回对象的哈希码(默认将对象的地址转换为哈希码) 要求:相同对象必须返回相同哈希码,不同对象尽量返回不同哈希码! class TestStudent{
public static void main(String[] args){
Student s1 = new Student("张三",18);
Student s2 = new Student("张三",18);
Student s3 = new Student("李四",19);
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
}
}
class Student{
String name;
int age;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public int hashCode(){
return name.hashCode()+age;
}
}
-
String toString() 返回对象的字符串形式 class TestStudent{
public static void main(String[] args){
Student s1 = new Student("张三",18);
Student s2 = new Student("张三",18);
Student s3 = new Student("李四",19);
System.out.println(s1.toString());
System.out.println(s2);
System.out.println(s3);
}
}
class Student{
String name;
int age;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "学生名字:"+name+", 学生年龄:"+age;
}
}
-
finalize() 用于垃圾对象的回收,释放内存空间
- 垃圾对象: 没有任何引用指向的对象,即垃圾对象(零引用算法)
- 垃圾回收:销毁垃圾对象,释放内存空间
- 垃圾回收时机:
- 自动垃圾回收:当内存被占用满,没有空间用来创建新对象时,垃圾回收器(GC)会自动调用所有垃圾对象的finalize()方法,销毁垃圾对象,释放内存空间
- 手动垃圾回收: 通过 System.gc();可以手动通知GC回收垃圾,若GC空闲会立马回收垃圾对象,若是GC繁忙会推迟回收
总结:equals、hashCode、toString方法一般情况下都是要重写的
重点:
- Object在Java中的意义
- 前四个方法的作用,及重写语法
包装类
class Test{
public static void main(String[] args){
Object o = 12;
}
}
问题:以上代码是否存在语法错误?Object引用是否能够指向基本数据类型的值
答:木有问题!
概念: 所有基本数据类型都有对应的引用类型,即包装类
基本数据类型 | 包装类型 |
---|
byte | Byte | short | Short | int | Integer | long | Long | float | Float | double | Double | char | Character | boolean | Boolean |
作用:
- Object可以统一管理到所有的数据类型
- 区分了0和null
经验:开发中,基本所有的属性数据类型都应该定义为包装类型
基本数据类型与包装类型的转换
-
基本数据类型------->包装类型
- 通过构造方法: int i =10; Integer i1 = new Integer(i);
- 通过静态方法valueOf() : int i = 10; Integer i1 = Integer.valueOf(i);
-
包装类型-------->基本数据类型
- 通过xxxValue()方法: Integer i1 = new Integer(10); int i = i1.intValue();
-
JDK5.0以后,可以实现自动装箱、自动拆箱
- 自动装箱:自动将基本数据类型转为包装类型
- 自动拆箱:自动将包装类型转为基本数据类型
class Test{
public static void main(String[] args){
Integer i1 = 10;
int i2 = i1;
System.out.println(i1);
System.out.println(i2);
}
}
String类型与基本数据类型的转换
-
基本数据类型------->String类型
- +"" 例如: int i = 10; String s = i+""; 推荐使用!
- 调用静态方法valueOf() 例如: int i = 10; String s = String.valueOf(i);
-
String类型------->基本数据类型 重点!!!
-
调用相应包装类的静态方法 包装类名.parseXxx(); 例如: String s = “12”; int i = Integer.parseInt(s); 注意:字符串中存放的必须是相应的基本数据类型的值,否则会报运行时异常:java.lang.NumberFormatException 数据格式异常 例如: String s = “12a”; int i = Integer.parseInt(s);//报异常
String类型与包装类型的转换
-
包装类型------->String类型
- +“” Integer i1 = 10; String s = i1+""; 推荐使用!
- 调用toString()方法 Integer i1 = 10; String s = i1.toString();
-
String-------->包装类型
- 通过构造方法 String s = “10”; Integer i1 = new Integer(s);
- 调用静态方法valueOf String s = “10” ; Integer i1 = Integer.valueOf(s);
注意:字符串中存放的必须是相应的包装类型的值,否则会报运行时异常:java.lang.NumberFormatException 数据格式异常
总结:
- 基本数据类型与包装类型转换:自动装箱、自动拆箱
- 基本属性类型转为String类型: +""
- String类型转为基本数据类型: 包装类名.parseXxx();
整数缓冲区
class Test{
public static void main(String[] args){
int i1 = 12;
int i2 = 12;
System.out.println(i1==i2);
Integer i3 = 12;
Integer i4 = 12;
System.out.println(i3==i4);
Integer i5 = 200;
Integer i6 = 200;
System.out.println(i5==i6);
Integer i7 = new Integer(12);
Integer i8 = new Integer(12);
System.out.println(i7==i8);
}
}
String类
概念:字符串,由一系列字符组成的串
特点:常量,不可变长字符串
注意:字符串属于常量,内容不可变;所谓的字符串拼接,并不会在原有字符串的基础上拼接,而是创建了一个新的字符串对象,引用指向新字符串对象
String对象的创建方式:
1. String str = "Hello";
2. String str = new String("Hello");
若采用第一种方式创建String对象,会先在串池(字符串常量池)中寻找是否存在该字符串,若不存在,则在串池中开辟空间创建该字符串对象,引用指向串池中该对象;若存在,引用直接指向该对象;
若采用第二种方式创键String对象,会先在串池(字符串常量池)中寻找是否存在该字符串,若不存在,则在串池以及堆空间中年同时开辟空间创建该字符串对象,引用指向堆空间中的该字符串对象;若存在,则只在堆空间中开辟空间创建该字符串对象,引用指向堆空间中的该字符串对象;
===============================================================
class TestString{
public static void main(String[] args){
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1==s2);
String s3 = new String("World");
String s4 = new String("World");
System.out.println(s3==s4);
String s5 = "HelloWorld";
String s6 = new String("HelloWorld");
System.out.println(s5==s6);
String s7 = s6.intern();
System.out.println(s5==s7);
}
}
String的不可变:
class TestString{
public static void main(String[] args){
Student s1 = new Student("小明");
Student s2 = new Student("小郭");
System.out.println(s1==s2);
s2 = s1;
System.out.println(s1==s2);
System.out.println(s1.name);
System.out.println(s2.name);
s2.name = "小红";
System.out.println(s1==s2);
System.out.println(s1.name);
System.out.println(s2.name);
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2);
str2 = str1;
System.out.println(str1==str2);
System.out.println(str1);
System.out.println(str2);
str2 += "d";
System.out.println(str1==str2);
System.out.println(str1);
System.out.println(str2);
}
}
class Student{
String name;
public Student(){}
public Student(String name){
this.name = name;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRCtqKI4-1628609627994)(CoreJava笔记.assets/image-20210312172212361.png)]
可变长字符串:
- StringBuffer JDK1.0 线程安全 效率低
- StringBuilder JDK5.0 线程不安全 效率高
class TestStringBuilder{
public static void main(String[] args){
StringBuilder sb1 = new StringBuilder("abc");
StringBuilder sb2 = new StringBuilder("abc");
System.out.println(sb1==sb2);
sb2 = sb1;
System.out.println(sb1==sb2);
sb2.append("d");
System.out.println(sb1==sb2);
System.out.println(sb1);
System.out.println(sb2);
}
}
String类中的常用方法:
class TestString1{
public static void main(String[] args){
String str = "Hello";
System.out.println(str.length());
System.out.println(str.charAt(1));
System.out.println(str.contains("el"));
System.out.println(str.contains("he"));
System.out.println(str.contains("eo"));
System.out.println(str.startsWith("H"));
System.out.println(str.startsWith("He"));
System.out.println(str.startsWith("he"));
System.out.println(str.startsWith("Hello"));
System.out.println(str.endsWith("o"));
System.out.println(str.equals("hello"));
System.out.println(str.equalsIgnoreCase("hello"));
System.out.println(str.indexOf("l"));
System.out.println(str.indexOf("lo"));
System.out.println(str.lastIndexOf("l"));
System.out.println(str.isEmpty());
String s1 = "";
System.out.println(s1.length());
System.out.println(s1.isEmpty());
System.out.println(str.replace("l","L"));
String[] ss = str.split("");
for(String string:ss){
System.out.println(string);
}
System.out.println(str.substring(1,3));
char[] cs = str.toCharArray();
for(char c:cs){
System.out.println(c);
}
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
String s2 = " a b c ";
System.out.println(s2.trim());
}
}
chap12-集合
一.概念
集合:一种便利操作对象元素的容器,通常情况下可以代替数组.
都在java.util包下
学习方法:
- 学习集合接口及特点
- 学习接口中的方法
- 学习接口的实现类
- 遍历方式
二.根接口Collection
三.List
-
特点:有序有下标,元素可重复 -
方法: add(int index,Object o):向集合中index位置处插入元素o
remove(int index):把集合中下标index处的元素删除
set(int index,Object o):把集合中index处的元素替换为o
get(int index):获取下标index处的元素o
-
实现类
-
遍历方式
-
下标遍历 for(int i=0;i<集合名.size();i++) {
}
-
foreach遍历 for(元素类型 变量名:目标集合) {
}
-
iterator遍历
Iterator i = list.iterator();
while(i.hasNext()) {
}
-
自遍历-forEach()
Consumer ca=t->System.out.println(t);
list.forEach(ca);
四.泛型集合
五.Collections
-
倒序:Collections.reverse(list); -
随机:Collections.shuffle(list); -
排序:Collections.sort(list); 使用sort方法,集合中的数据类型必实现Comparable接口
重写其中方法
public int compareTo(泛型类型){
}
-
排序:集合名.sort(): 使用外置比较器,需要写一个类实现Comparator接口
重写其中方法
public int compare(泛型类型 o1,泛型类型 o2){
}
使用Lambda表达式写法:
list.sort((o1,o2)->{
});
六.Set
-
特点:无序无下标,元素不可重复 -
方法:无 -
实现类
-
遍历方式
- foreach遍历
- 迭代器遍历
- 自遍历–forEach
-
结构图
七.根接口Map
-
特点 键值对存储,键不能重复,值可以重复 -
方法 put(Object k,Object v):向集合中添加键值对
remove(Object k):通过键删除键值对,返回删除值
clear():清空集合
get(Object k):通过键查询值
size():返回有效元素的个数
containsKey(Object k):判断集合中是否含有k键
containsValue(Object v):判断集合中是否含有v值
isEmpty():判断集合是否为null
keySet():返回所有键组成的Set集合
values():返回所有值组成的Collection集合
entrySet():返回所有键值对对象(Map.Entry)组成的Set集合
-
实现类
-
遍历方式
-
键遍历–keySet():返回所有键组成的Set集合 Set<Integer> set=map.keySet();
for(Integer i:set) {
System.out.println(i+"="+map.get(i));
}
-
值遍历–values():返回所有值组成的Collection集合 Collection<String> coll=map.values();
coll.forEach(v->System.out.println(v));
-
键值对遍历–entrySet():返回所有键值对对象(Map.Entry)组成的Set集合 Set<Map.Entry<Integer,String>> set2=map.entrySet();
for(Map.Entry<Integer, String> e:set2) {
System.out.println(e.getKey()+"="+e.getValue());
}
map.forEach((t,u)->System.out.println(t+"="+u));
-
自遍历 map.forEach((t,u)->System.out.println(t+"="+u));
-
图解 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l9z2ldnB-1628609627996)(CoreJava笔记.assets/HashMap.png)]
重点:
- 集合的作用
- List\Set\Map的特点
- ArrayList的创建方式,基本的存值取值及遍历方式(下标遍历\forEach遍历)
- HashSet的创建方式,基本的存值及遍历方式(forEach遍历)
- HashSet集合的底层原理(面试问题)
- HashMap的创建方式,基本的存值\取值及遍历方式(键遍历)
- HashMap与HashTable的区别
chap13-异常
一.概念
- 异常:程序在运行时发生的不正常情况
- 处理异常:预先写好一段代码,当异常发生时执行这段代码,避免程序异常给用户带来的不必要的麻烦或这损失.
二.异常的分类
三.异常的产生
- 自动产生异常:程序运行过程中遇到错误的代码,程序因为异常产生而终止
- 手动产生异常:
- 语法:throw 异常对象;(定义在方法内部)
- 作用:手动产生异常相当于return,中止程序运行
四.异常的传递
- 异常具有传递性,按照调用链(main–>m1–>m2–>m3–>m4)的反向(m4–>m3–>m2–>m1–>main)进行传递,导致程序因为异常中止,异常最终交由JVM,(JVM的处理方式,打印异常的追踪信息)
五.异常的处理
-
消极处理
-
语法:在访问声明的尾部throws 异常类型 修饰符 返回值类型 方法名(参数表)throws 异常类型
-
结果:可以保证程序编译通过 -
作用:消极处理异常的方式是一种不负责任的异常处理,将异常向上声明,谁调用谁处理,程序还是会因为异常的产生而中止. -
积极处理
-
语法: try{
可能产生异常的代码
}catch(捕获到的异常类型){
对异常的处理
}
-
注意: 1.可以存在多个catch,异常对象产生后,和catch从上到下依次进行匹配,如果匹配成功执行catch块中的内容
2.由于多态的原因,父类异常类型可以匹配子类异常对象,catch中父类异常写在后面,子类异常写在前面
3.可以捕获未发生的运行时异常,不能捕获未发生的非运行时异常
-
finally try{
可能发生异常的代码
}catch(捕获的异常类型){
异常的处理
}finally{
无论异常是否发生都必须执行的代码
}
面试重点:final/finalize/finally的区别?
-
异常处理条线(异常处理结构) try{}catch(){}
try{}catch(){}catch(){}...
try{}catch(){}finally{}
try{}catch(){}catch(){}...finally{}
try{}finally{}
try{try{}catch(){}}catch(){}
try{}catch(){try{}catch(){}}
try{}catch(){}finally{try{}catch(){}}
-
作用:不仅保证程序编译通过,而且程序不会因为异常产生而中止
六.自定义异常
-
自定义一个类,必须继承Exception或者其子类 class AgeException extends RuntimeException{
public AgeException() {}
public AgeException(String message) {
super(message);
}
}
-
在产生异常的地方抛出异常
七.方法覆盖
-
方法覆盖初级版 方法名相同,参数列表相同,返回值类型相同,访问修饰符相同或更宽
-
方法覆盖升级版 方法名相同,参数列表相同,返回值类型相同,访问修饰符相同或更宽
子类不能比父类抛出更大的异常(与运行时异常无关)
class Super{
public void m1(){
System.out.println("m1---super");
}
}
class Sub extends Super{
public void m1() throws RuntimeException{
System.out.println("m1---sub");
}
}
-
方法覆盖终极版 方法名相同,参数列表相同,访问修饰符相同或更宽
子类不能比父类抛出更大的异常(与运行时异常无关)
子类的返回值可以是父类方法返回值的子类型
class Super{
public Object m2() {
System.out.println("m2---super");
return null;
}
}
class Sub extends Super{
public Double m2() {
System.out.println("m2---sub");
return null;
}
}
重点:
- 异常的概念
- 异常的分类
- 异常的消极处理及积极处理\finally语句
- 自定义异常语法
- 方法覆盖的终极要求
- final与finalize与finally的区别(面试题)
chap14-IO
思考: 在之前,程序中的数据存在哪里?
答: 通过变量\对象\数组\集合,存在内存中,但是无法持久化保存
注意: 可以通过IO技术,讲数据存在磁盘上,完成持久化存储
I\O: input(输入)\output(输出)
流: 是一种对象,类似于水管\管道,通过流对象可以实现数据的传输(输入与输出)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bVgyzbu2-1628609627996)(CoreJava笔记.assets/image-20210320110722914.png)]
流的分类
按照不同的分类方式:
-
按照数据的传输方向:
- 输入流 将数据输入到JVM中
- 输出流 将数据从JVM中输出到外界
-
按照数据的传输单位:
- 字节流 以字节为传输单位,可以传输任意类型的数据(文本\图片\视频\音频等等)
- 字符流 以字符为传输单位,只能传输文本文件(文本文件: 可以通过记事本打开的文件,例如: .txt .java .html等)
-
按照流的功能:
- 节点流 具有实际传输能力的流
- 过滤流 不具有实际传输能力,但是可以增强节点流的传输能力或为节点流增加新的功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qr39jjUg-1628609627997)(CoreJava笔记.assets/image-20210320112815234.png)]
字节流
字节流的总父类
- InputStream(抽象类) 字节输入流
- OutputStream(抽象类) 字节输出流
节点流
-
FileOutputStream 文件字节输出流
-
创建语法1: FileOutputStream fos = new FileOutputStream(“文件路径”);
-
注意: 默认覆盖掉文件中的原有内容 -
绝对路径: 以电脑本身为参照物, 盘符名:/文件夹名/文件名,例如: c:/file/a.txt 注意:从盘符开始找起,若文件不存在,会自动创建文件;但文件夹必须存在 -
相对路径: 以项目本身为参照物, 文件夹名/文件名, 例如: file/a.txt 注意: 在项目名(CoreJava)下寻找file文件夹,再寻找a.txt;若文件不存在,会自动创建文件;但文件夹必须存在 -
创建语法2: FileOutputStream fos = new FileOutputStream(“文件路径”,true\false); true 代表在原有内容上追加新内容,不会覆盖掉原有内容 false 覆盖掉原有内容 -
常用方法
- void write(int b) 将字节b输出到指定的文件中
class TestFileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("a.txt",true);
fos.write(67);
fos.close();
}
}
- void write(byte[] b) 将字节数据b中的数据输出到指定文件中
class TestFileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("a.txt");
String str = "ABCDEFGHIJKLMN";
byte[] bs = str.getBytes();
fos.write(bs);
fos.close();
}
}
- void write(byte[] b, int off, int len) 从b数组的off下标开始(包含off下标),往后的len个元素输出到指定文件中
class TestFileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("a.txt");
String str = "ABCDEFGHIJKLMN";
byte[] bs = str.getBytes();
fos.write(bs,1,5);
fos.close();
}
}
-
FileInputStream 文件字节输入流 创建语法: FileInputStream fis = new FileInputStream(“文件路径”); ? 注意: 文件必须存在,不会自动创建!!! 常用方法:
- int read() 每次读取一个字节的数据,返回值为读取内容;若读取到文件末尾,返回-1
class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.txt");
int i = fis.read();
System.out.println((char)i);
fis.close();
}
}
===================================================================
class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.txt");
while(true) {
int i = fis.read();
if(i==-1)break;
System.out.println((char)i);
}
fis.close();
}
}
- int read(byte[] b) 每次读取一个字节数组的数据, 返回值为读取元素个数;若读取到文件末尾,返回-1
class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.txt");
byte[] bs = new byte[10];
int len = fis.read(bs);
System.out.println(len);
for(byte b:bs) {
System.out.println((char)b);
}
fis.close();
}
}
============================================================
class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.txt");
byte[] bs = new byte[10];
while(true) {
int len = fis.read(bs);
System.out.println(len);
if(len==-1)break;
for(int i=0;i<len;i++) {
System.out.println((char)bs[i]);
}
}
fis.close();
}
}
- int read(byte[] b, int off, int len) 每次读取一个字节数组,从数组的off下标开始存储,存储len个长度;若读取到文件末尾,返回-1
class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("a.txt");
byte[] bs = new byte[10];
int len = fis.read(bs, 2, 4);
for(byte b:bs) {
System.out.println(b);
}
fis.close();
}
}
IO标准异常处理:
try-catch-finally
class TestFileOutputStream1 {
public static void main(String[] args){
FileOutputStream fos =null;
try {
fos = new FileOutputStream("b.txt");
fos.write(65);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class TestFileInputStream1 {
public static void main(String[] args){
FileInputStream fis = null;
try {
fis = new FileInputStream("b.txt");
int i = fis.read();
System.out.println(i);
} catch (Exception e) {
e.printStackTrace();
}finally {
if (fis!=null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
======================================================================
JDK7.0 try-with-resource 实现自动关流 可以写多个对象,对象之间使用;隔开
try(实现了AutoCloseable接口的对象1;
实现了AutoCloseable接口的对象2
){
}catch(异常 e){
}
class TestFileOutputStream2 {
public static void main(String[] args){
try (FileOutputStream fos = new FileOutputStream("b.txt")) {
fos.write(67);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class TestFileInputStream2 {
public static void main(String[] args){
try (FileInputStream fis = new FileInputStream("b.txt")) {
int i = fis.read();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件拷贝----复制粘贴
class TestFileCopy {
public static void main(String[] args) {
fileCopy2("d:/a.wmv", "f:/a.wmv");
}
public static void fileCopy1(String startAddr,String endAddr) {
long t1 = System.nanoTime();
try (FileInputStream fis = new FileInputStream(startAddr);
FileOutputStream fos = new FileOutputStream(endAddr)) {
while(true) {
int i = fis.read();
if(i==-1)break;
fos.write(i);
}
} catch (IOException e) {
e.printStackTrace();
}
long t2 = System.nanoTime();
System.out.println((t2-t1)/1E9);
}
public static void fileCopy2(String startAddr,String endAddr) {
long t1 = System.nanoTime();
try (FileInputStream fis = new FileInputStream(startAddr);
FileOutputStream fos = new FileOutputStream(endAddr)) {
byte[] bs = new byte[1024];
while(true) {
int len = fis.read(bs);
if(len==-1)break;
fos.write(bs,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
long t2 = System.nanoTime();
System.out.println((t2-t1)/1E9);
}
}
过滤流
-
BufferedInputStream BufferedOutputStream 缓冲过滤流 创建语法: ? BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos);
特点: 内置数组缓冲区,减少访问磁盘次数,提高效率 注意:由于数据会先进入缓冲区,所以需要flush()将数据从缓冲区中刷新出来;或者直接close()也行,会自动刷新缓冲区 注意:若使用过滤流,只需要关闭最外层流即可! class TestBufferedOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("c.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(66);
bos.close();
FileInputStream fis = new FileInputStream("c.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int i = bis.read();
System.out.println(i);
bis.close();
}
}
文件拷贝----过滤流版 class TestFileCopy {
public static void main(String[] args) {
fileCopy3("d:/a.wmv", "f:/a.wmv");
}
public static void fileCopy1(String startAddr,String endAddr) {
long t1 = System.nanoTime();
try (FileInputStream fis = new FileInputStream(startAddr);
FileOutputStream fos = new FileOutputStream(endAddr)) {
while(true) {
int i = fis.read();
if(i==-1)break;
fos.write(i);
}
} catch (IOException e) {
e.printStackTrace();
}
long t2 = System.nanoTime();
System.out.println((t2-t1)/1E9);
}
public static void fileCopy2(String startAddr,String endAddr) {
long t1 = System.nanoTime();
try (FileInputStream fis = new FileInputStream(startAddr);
FileOutputStream fos = new FileOutputStream(endAddr)) {
byte[] bs = new byte[1024];
while(true) {
int len = fis.read(bs);
if(len==-1)break;
fos.write(bs,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
long t2 = System.nanoTime();
System.out.println((t2-t1)/1E9);
}
public static void fileCopy3(String startAddr,String endAddr) {
long t1 = System.nanoTime();
try (FileInputStream fis = new FileInputStream(startAddr);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(endAddr);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
byte[] bs = new byte[1024];
while(true) {
int len = bis.read(bs);
if(len==-1)break;
bos.write(bs,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
long t2 = System.nanoTime();
System.out.println((t2-t1)/1E9);
}
}
-
PrintStream 标准输出流
- 面试问题: 请你说一下你对输出语句(System.out.println())的理解-----面试问题
- 回答: System是一个类,out是该类的静态属性,类型为PrintStream,println()是该流的一个方法
-
ObjectInputStream ObjectOutputSteam 对象过滤流
-
作用1: 增加了读写8种基本数据类型的能力 class TestObjectOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("d.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeDouble(1.25);
oos.close();
FileInputStream fis = new FileInputStream("d.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
double d = ois.readDouble();
System.out.println(d);
ois.close();
}
}
-
作用2: 增加了读写对象的能力
- 注意1: 当读取到文件末尾时,会报异常:java.io.EOFException;可以作为结束循环的条件
class TestObjectOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("f.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject("窗前明月光");
oos.writeObject("疑是地上霜");
oos.close();
FileInputStream fis = new FileInputStream("f.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
while(true) {
try {
String str = (String)ois.readObject();
System.out.println(str);
} catch (Exception e) {
break;
}
}
ois.close();
}
}
-
注意2:若读取的对象类型为自定义类型,该类型必须实现序列化接口–Serializable,表示该类型的对象可以放在流上传输;注意,该类型中的属性的类型若是自定义类型,也需要实现Serializable; 可以通过transient修饰符,完成反序列化,即该属性的值不会在流上传输 class TestObjectOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("g.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Student s1 = new Student("小郭",18,100);
oos.writeObject(s1);
oos.close();
FileInputStream fis = new FileInputStream("g.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Student stu =(Student)ois.readObject();
System.out.println(stu);
ois.close();
}
}
class Student implements Serializable{
private String name;
private int age;
private transient double scoer;
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 getScoer() {
return scoer;
}
public void setScoer(double scoer) {
this.scoer = scoer;
}
public Student() {
super();
}
public Student(String name, int age, double scoer) {
super();
this.name = name;
this.age = age;
this.scoer = scoer;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(scoer);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(scoer) != Double.doubleToLongBits(other.scoer))
return false;
return true;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", scoer=" + scoer + "]";
}
}
字符流
概念: 专门用来处理char及String类型的数据, 设置编解码集
字符编码集
计算机底层只能识别二进制码,而所有二进制都可以转换为十进制的整数,每一个字符都对应了相应的一个整数
编码: 字符-------------->整数
? 例如: 加密过程: 将原文加密为密文
解码: 整数-------------->字符
? 例如: 解密过程: 将密文解为原文
字符编码集: 即每个字符与整数的对应关系
常见的字符编码集:
- GBK 简体中文
- Big5 繁体中文
- ISO-8859-1 西欧
- ASCII 美国 美国交换标准,美国是永远不会出现乱码的国家
- Unicode 万国码\统一码
- UTF-8 行业标准 每个字符根据大小可能占用1个字节\2个字节\3个字节
- UTF-16 Java底层默认使用的编码集,每个字符,无论大小固定占用两个字节
字符流
字符流总父类(抽象类):
节点流
-
FileWriter 文件字符输出流 ? 创建语法: FileWriter fw = new FileWriter(“文件路径”); ? 常用方法: void write(String str) 输出一个字符串 class TestFileWriter {
public static void main(String[] args) throws Exception {
FileWriter fw = new FileWriter("a.txt");
fw.write("好好学习,天天向上");
fw.close();
}
}
-
FileReader 文件字符输入流 创建语法: FileReader fr = new FileReader(“文件路径”); 常用方法: int read() 每次读取一个字符,返回值为读取内容;读取到文件末尾,返回-1; class TestFileReader {
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("a.txt");
int i = fr.read();
System.out.println((char)i);
}
}
==========================================================
class TestFileReader {
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("a.txt");
while(true) {
int i = fr.read();
if(i==-1)break;
System.out.print((char)i);
}
fr.close();
}
}
过滤流
-
PrintWriter|BufferedReader 缓冲过滤流
- 注意: 使用PrintWriter代替BufferedWriter,是因为PrintWriter中有更多好用\强大的方法
- 创建语法: 基于字符节点流
class TestPrintWriter {
public static void main(String[] args) throws Exception {
FileWriter fw = new FileWriter("b.txt");
PrintWriter pw = new PrintWriter(fw);
pw.println("好好学习,天天向上");
pw.println("锻炼身体,报效祖国");
pw.close();
FileReader fr = new FileReader("b.txt");
BufferedReader br = new BufferedReader(fr);
while(true) {
String str = br.readLine();
if(str==null)break;
System.out.println(str);
}
br.close();
}
}
-
InputStreamReader|OutputStreamWriter 桥转换流
- 作用:可以将字节流转换为字符流,并指定编码集
- 创建语法:
- InputStreamReader isr = new InputStreamReader(字节输入流对象,“编码集”);
- OutputStreamWriter osw = new OutputStreamWriter(字节输出流对象,“编码集”);
class TestOutputStreamWriter {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("c.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
PrintWriter pw = new PrintWriter(osw);
pw.println("好好学习");
pw.println("天天向上");
pw.println("锻炼身体");
pw.println("报效祖国");
pw.close();
FileInputStream fis = new FileInputStream("c.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
BufferedReader br = new BufferedReader(isr);
while(true) {
String line = br.readLine();
if(line==null)break;
System.out.println(line);
}
br.close();
}
}
File类
IO流: 处理文件中的数据
File类: 处理文件或文件夹的
File对象: 代表一个文件或文件夹
创建语法: File f = new File(“文件路径”); e:/2101/a/b a.txt
常用方法:
- boolean createNewFile() 创建文件,若文件不存在,则创建成功并返回true;若文件已存在,则创建失败并返回false
- boolean mkdir() 创建文件夹,若文件夹不存在,则创建成功并返回true;若文件夹已存在,则创建失败并返回false;注意:父文件夹必须存在!!!
- boolean mkdirs() 创建文件夹,并同时创建不存在的父文件夹
- boolean delete() 删除文件或空文件夹,删除成功返回true,反之返回false
- boolean exists() 判断文件或文件夹是否存在,存在返回true,不存在返回false
- String getAbsolutePath() 返回当前File对象绝对路径(String形式)
- String getName() 返回当前File对象的名字
- String getParent() 返回当前File对象的父目录地址
- String getPath() 返回当前File对象相对路径(String形式)
- boolean isDirectory() 判断当前File对象是否为文件夹
- boolean isFile() 判断当前File对象是否为文件
- long length() 获取当前File对象的大小
- File[] listFiles() 返回当前File对象(文件夹对象)中所有File对象
class TestFile {
public static void main(String[] args) {
File f2 = new File("E:\\2101\\CoreJava");
System.out.println(f2.isDirectory());
System.out.println(f2.getName());
System.out.println(f2.getAbsolutePath());
System.out.println(f2.getPath());
System.out.println(f2.mkdir());
System.out.println(f2.getParent());
File[] files = f2.listFiles();
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
}
}
随堂小练习: 查询 E:\2101\CoreJava 目录下所有的java文件,并复制到桌面上的copyJava的文件夹中
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class TestFileCopy {
public static void main(String[] args) {
searchJava(new File("E:\\2101\\CoreJava"));
}
public static void searchJava(File f) {
if(f.exists()&&f.isDirectory()) {
File[] files = f.listFiles();
for (File file : files) {
if(file.isDirectory()) {
searchJava(file);
}else {
if(file.getName().endsWith(".java")) {
copyJava(file);
}
}
}
}
}
public static void copyJava(File f) {
File tofile = new File("C:\\Users\\Administrator\\Desktop\\copyJava");
tofile.mkdir();
try (
FileInputStream fis = new FileInputStream(f.getAbsolutePath());
InputStreamReader isr = new InputStreamReader(fis,"GBK");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream(tofile.getAbsolutePath()+"\\"+f.getName());
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
PrintWriter pw = new PrintWriter(osw)) {
while(true) {
String line = br.readLine();
if(line==null) break;
pw.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
chap15-多线程
进程
概念: OS(操作系统)中并发执行(同时执行)的多个任务程序
注意: 只有正在执行的程序才是进程!
并发原理: 宏观并行,微观串行
OS会将一个时间段的CPU处理能力,分成多个时间片,单个时间片内CPU只能执行一个程序,只要时间片划分的足够细小,交替频率足够快,就能形成宏观并行的假象,实质上依然是微观串行!!
线程
概念: 是进程中并发执行的多个任务逻辑,轻量级进程
例如: QQ是一个进程,而QQ中打开的多个聊天窗口是多个线程
注意: 线程是进程的基本组成单元
并发原理: 宏观并行,微观串行
Java中没有多进程,只有多线程!!!
因为所有的Java代码都是运行在JVM中,而JVM本身就是一个进程!在之间所学中,JVM中只有一个线程(main主线程),当main函数执行结束了,就意味着主线程结束了,即JVM进程也就结束了;但从今天开始,我们除了主线程之外,可能还会创建其他的线程,若主线程结束,其他线程尚未执行结束,则程序(JVM进程)也不会结束!
线程的组成:
- CPU时间片 OS负责分配
- 数据
- 栈 每个线程都有自己独立的栈空间
- 堆 堆空间是所有线程共享的
- 代码
创建线程
-
继承线程类(Thread), 覆盖run()方法 class TestThread {
public static void main(String[] args) {
System.out.println("main start ");
Thread t1 = new MyThread();
t1.start();
System.out.println("main end ");
}
}
class MyThread extends Thread{
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("t1: "+i);
}
}
}
-
实现Runnable接口,实现run方法 class TestThread {
public static void main(String[] args) {
System.out.println("main start ");
Task task = new Task();
Thread t2 = new Thread(task);
t2.start();
System.out.println("main end ");
}
}
class Task implements Runnable{
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("t2: "+i);
}
}
}
-
匿名内部类创建线程对象–特殊写法而已,不属于新的创建方式! class TestThread1 {
public static void main(String[] args) {
System.out.println("main start ");
Thread t1 = new Thread() {
public void run() {
for(int i=1;i<=1000;i++) {
System.out.println("t1: "+i);
}
}
};
Thread t2 = new Thread(new Runnable() {
public void run() {
for(int i = 1;i<=1000;i++) {
System.out.println("t2: "+i);
}
}
});
t1.start();
t2.start();
System.out.println("main end ");
}
}
线程状态
线程池
程序存在问题: 线程的频繁创建与销毁, 浪费系统资源,影响程序性能
可以通过线程池解决该问题!
概念: 一个存放线程对象的容器,当需要线程对象时,从池中获取,不需要时,不会立马销毁线程对象,而是归还给线程池,以待下次复用
作用: 减少创建与销毁线程对象的次数,提高程序性能,节约系统资源
Executor : 线程池总接口
ExecutorService: 该接口的实现类对象,即线程池对象
- submit(Runnable task) 发布任务
- void shutdown() 关闭线程池
Executors: 线程池工具类,定义了一些创建或操作线程池的方法
- static ExecutorService newFixedThreadPool(int nThreads) 创建一个具有固定数量线程对象的线程池对象
- static ExecutorService newCachedThreadPool() 创建一个线程对象数量不固定的线程池对象
class TestExecutor {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
Runnable r1 = new Runnable() {
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("r1: "+i);
}
}
};
Runnable r2 = new Runnable() {
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("r2: "+i);
}
}
};
Runnable r3 = new Runnable() {
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("r3: "+i);
}
}
};
es.submit(r1);
es.submit(r2);
es.submit(r3);
es.shutdown();
}
}
Callable
Callable: JDK5.0出现,类似于Runnable接口,其接口实现类对象,类似于Runnable接口实现类对象,同样都属于线程的任务对象
- V call() throws Exception ; 类似于run()方法,都是线程对象需要执行的具体逻辑代码;该方法的返回值即当前线程对象执行结束后的最终结果
call()与run()的区别: call方法具有返回值,并且可以上抛异常
Future : 接口, Future对象中存储着当前线程执行结束后的最终结果
- V get() 返回Future对象中存储的线程执行结果.若通过该方法获取线程最终执行结果时,线程尚未执行结束,没有最终结果,那么当前线程(即调用get方法的线程)会进入无限期等待状态,等待线程执行结束,返回其结果
class TestCallable {
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newCachedThreadPool();
Callable<Integer> c1 = new Callable<Integer>() {
public Integer call() throws Exception {
int sum = 0;
for(int i=1;i<=50;i++) {
sum+=i;
}
return sum;
}
};
Callable<Integer> c2 = new Callable<Integer>() {
public Integer call() throws Exception {
int sum = 0;
for(int i=51;i<=100;i++) {
sum+=i;
}
return sum;
}
};
Future<Integer> f1 = es.submit(c1);
Future<Integer> f2 = es.submit(c2);
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
System.out.println(i1+i2);
es.shutdown();
}
}
线程安全问题
class TestList {
public static void main(String[] args) throws Exception {
MyList list = new MyList();
Thread t1 = new Thread() {
public void run() {
list.add("C");
}
};
Thread t2 = new Thread(new Runnable() {
public void run() {
list.add("D");
}
});
t1.start();
t2.start();
t1.join();
t2.join();
list.print();
}
}
class MyList{
private String[] ss = {"A","B","",""};
private int index = 2;
public void add(String str) {
ss[index] = str;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
}
public void print() {
for(int i=0;i<index;i++) {
System.out.print(ss[i]+"\t");
}
System.out.println("有效元素个数为: "+index);
}
}
线程安全问题: 当多个线程访问同一对象(临界资源)时,可能破坏其原子性操作,导致数据不一致的问题
临界资源: 被多个线程同时访问的数据(对象)
原子性操作: 多步操作被当做一个整体,不能分割,要么同时执行成功,要么同时执行失败
线程同步
-
同步代码块 为原子性操作加锁,使其能够同步执行 语法: 对o对象加锁的同步代码块,锁住的原子性操作
synchronized (锁对象 o) {
}
注意: Java中的每个对象,都有一个唯一的互斥锁标记,可以用来分配给线程,只有拿到锁标记的线程才能够进入对该对象加锁的同步代码块;若多个线程都具有对该对象加锁的同步代码块,那么多个线程之间除了争抢时间片外,还需要争抢锁标记,只有拿到锁标记的线程才能执行代码,并且在执行结束之前,其他线程会进入阻塞状态,无法执行;只有当拿到锁标记的线程执行结束,就会释放锁标记,其他线程才可以拿到锁标记,然后回到就绪状态,等待分配时间片执行代码
Object o = new Object();
t1线程
synchronized (o) {
}
t2线程
synchronized (o) {
}
注意: 此时t1与t2需要争抢时间片与锁标记,只有同时具有二者,才可以执行代码
人 线程 t1与t2
厕所隔间 临界资源 list
脱裤子\拉屎 原子性操作 add()
隔间的锁 锁对象\锁标记 o
-
同步方法 对当前对象加锁,只有拿到当前对象的锁标记才能执行该方法中的代码
修饰符 synchronized 返回值类型 方法名(形参列表){
}
等价于:
修饰符 返回值类型 方法名(形参列表){
synchronized (this) {
}
}
class TestList {
public static void main(String[] args) throws Exception {
MyList list = new MyList();
Object o = new Object();
Thread t1 = new Thread() {
public void run() {
list.add("C");
}
};
Thread t2 = new Thread(new Runnable() {
public void run() {
list.add("D");
}
});
t1.start();
t2.start();
t1.join();
t2.join();
list.print();
}
}
class MyList{
private String[] ss = {"A","B","",""};
private int index = 2;
public synchronized void add(String str) {
ss[index] = str;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
}
public void print() {
for(int i=0;i<index;i++) {
System.out.print(ss[i]+"\t");
}
System.out.println("有效元素个数为: "+index);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k9YYXzjz-1628609627998)(CoreJava笔记.assets/image-20210324110634601.png)]
死锁
t1
synchronized(o1){
synchronized(o2){
}
}
t2
synchronized(o2){
synchronized(o1){
}
}
线程通信:
- o.wait() 使当前线程释放自身所具有的所有锁标记并进入无限期等待状态,并且当前线程也会进入o的等待队列当中,等待被唤醒,然后进入就绪状态等待分配时间片\获取锁标记
- o.notify() 随机唤醒自身等待队列中的一个线程
- o.notifyAll() 唤醒自身等待队列中的全部线程
- 注意: 上述三个方法都必须用在对o加锁的同步代码中
面试题: 请问wait()与sleep()方法的区别?
答:
- wait()方法属于Object类,sleep()属于Thread的静态方法
- sleep()只会释放时间片,不会释放锁标记,并且不需要被唤醒
- wait()会同时释放时间片与锁标记,并且需要被唤醒
class TestDeathLock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread() {
public void run() {
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("A");
System.out.println("B");
System.out.println("C");
System.out.println("D");
o2.notify();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
synchronized (o2) {
try {
o2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
}
}
}
};
t1.start();
t2.start();
}
}
线程安全的集合类
悲观锁: 总是悲观的认为肯定会出现线程安全问题,所以会直接使用synchronized或Lock加锁,保证线程安全
乐观锁: 总是乐观的认为不会出现线程安全问题,所以不会使用synchronized加锁或Lock加锁,而是等到真正出现了数据不一致问题时,通过算法(CAS)保证线程安全
队列
Queue : 队列,是一种集合接口
特点: FIFO(先进先出)
常用方法:
- boolean add(E e) 向队列中添加元素,若集合满了,添加失败则报异常
- boolean offer(E e) 向队列中添加元素,若集合满了,添加失败则返回false
- E element() 获取队列中的头元素,若队列为空,则获取失败则报异常
- E peek() 获取队列中的头元素,若队列为空,则获取失败则返回null
- E remove() 删除队列中的头元素,若队列为空,则删除失败则报异常
- E poll() 删除队列中的头元素,若队列为空,则删除失败则返回null
实现类:
- LinkedList
- ConcurrentLinkedQueue 线程安全的队列,采用CAS算法
class TestQueue {
public static void main(String[] args) {
Queue<String> list = new LinkedList<String>();
list.add("A");
list.offer("B");
System.out.println(list.element());
list.remove();
System.out.println(list.peek());
}
}
子接口
- BlockingQueue 阻塞队列 当某个线程从该队列中获取元素时,若该队列为空,获取失败,则该线程会进入阻塞状态
- ArrayBlockingQueue 有界队列 当某个线程向该队列中添加元素时,若该队列满了,添加失败,则该线程会进入阻塞状态
- LinkedBlockingQueue 无界队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NUgGryi2-1628609627999)(CoreJava笔记.assets/image-20210324164027030.png)]
chap16-反射
反射
概念: 是一个底层技术,常用与框架底层的设计或开发工具的设计
类对象
类对象: 是类加载的产物,存储了一个类的全部信息(属性\方法\父类\接口等),存储在方法区中
注意: 一般的类加载只会进行一次,所以类对象也只存在一个!!
类的对象:根据类所创建出来的一个实例对象
动物园: 老虎Tiger
? 记录老虎全部信息的牌子--------类对象 Tiger.class Class类
? 笼子里面的活生生的老虎--------类的对象 new Tiger(); Tiger类
获取类对象的方式:
-
类名.class 也可以获取基本数据类型的类对象: 基本数据类型.class 例如: int.class Class class1 = ArrayList.class;
Class c2 = int.class;
-
引用.getClass(); ArrayList<String> list = new ArrayList<String>();
Class c1 = list.getClass();
-
Class.forName(“类的全限定名”); Class c2 = Class.forName("java.util.ArrayList");
Class c3 = Class.forName("day23.Student");
常用方法:
-
getName() 获取类的全限定名–包名.类名 -
getPackage 获取Package对象—包对象 -
getSuperclass() 获取父类类对象 -
Class[] getInterfaces() 获取全部接口类对象 class TestReflect {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<String>();
Class c1 = list.getClass();
System.out.println(c1);
System.out.println(c1.getName());
System.out.println(c1.getPackage());
System.out.println(c1.getSuperclass());
Class[] cs = c1.getInterfaces();
for (Class class1 : cs) {
System.out.println(class1);
}
}
}
基于类对象获取Field对象(即属性对象)
- Field getField(String name) 获取本类或父类中的某个公开属性,参数代表属性名
- Field getDeclaredField(String name) 获取本类的某个属性,参数代表属性名
- Field[] getFields() 获取本类及父类中的全部公开属性
- Field[] getDeclaredFields() 获取本类的全部属性
基于类对象获取Method对象(即方法对象)
- Method getMethod(String name, 类对象…) 获取本类或父类中的某个公开方法
- 参数1: 代表方法名
- 参数2: 代表该方法的形参列表属性所对应的属性的类对象列表
- 例如:
- public void println(int a, String s, double d);
- getMethod(“println”,int.class,String.class,double.class)
- Method getDeclaredMethod(String name, 类对象…) 获取本类的某个方法
- Method[] getMethods() 获取本类及父类中的全部公开方法
- Method[] getDeclaredMethods() 获取本类的全部方法
基于类对象获取Constructor对象(即构造方法对象)
- Constructor getConstructor(类<?>… parameterTypes) 获取本类的某个公开构造方法
- 参数: 代表构造方法的形参列表的数据类型对应的类对象列表
- 例如:
- public Student(String name,int age){}
- getConstructor(String.class,int.class);
- Constructor getDeclaredConstructor(类<?>… parameterTypes) 获取本类的某个构造方法
- Constructor<?>[] getConstructors() 获取本类的全部公开构造方法
- Constructor<?>[] getDeclaredtConstructors() 获取本类的全部构造方法
使用反射手段,获取或操作对象的信息
class TestReflect {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
Student s1 = (Student)c1.newInstance();
System.out.println(s1);
Constructor con1 = c1.getConstructor(String.class,int.class);
Student s2 = (Student)con1.newInstance("小郭",18);
System.out.println(s2);
Field fname = c1.getDeclaredField("name");
fname.setAccessible(true);
fname.set(s1,"小红");
System.out.println(s1);
Method m1 = c1.getMethod("study");
m1.invoke(s1);
m1.invoke(s2);
Method m2 = c1.getDeclaredMethod("study", String.class);
m2.setAccessible(true);
m2.invoke(s1, "Java");
m2.invoke(s2, "Java");
}
}
class Student{
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public void study() {
System.out.println(name+"沉迷学习");
}
private void study(String keCheng) {
System.out.println(name+"沉迷学习"+keCheng);
}
}
反射的优缺点:
优点:
缺点:
设计模式
概念: 是一种被反复使用,被众多程序员认可的代码套路
23种 GOF(Gang of Four 四人组)
单例模式
概念: 创建一个类,该类只能创建一个实例对象
-
饿汉式 可能浪费空间
class ClassA{
private static ClassA ca = new ClassA();
public static ClassA newInstance() {
return ca;
}
private ClassA() {}
}
-
懒汉式 不会浪费空间,但是并发效率低
class ClassB{
private static ClassB cb = null;
public static synchronized ClassB newInstance() {
if(cb==null) cb = new ClassB();
return cb;
}
private ClassB() {}
}
-
双重校验锁 不浪费空间,在保证线程安全的前提下,尽量提高了并发效率
class ClassC {
private static ClassC cc = null;
public static ClassC newInstance() {
if(cc==null) {
synchronized(ClassC.class) {
if(cc==null)cc = new ClassC();
}
}
return cc;
}
private ClassC() {}
}
-
静态内部类实现 懒加载
class ClassD{
private static class Holder{
static ClassD cd = new ClassD();
}
public static ClassD newInstance() {
return Holder.cd;
}
private ClassD() {}
}
工厂模式
概念: 将对象的创建,交给工厂方法(工厂对象)
开闭原则: 修改关闭,扩展开放
class TestFactory {
public static void main(String[] args) {
Animal a = createAnimal();
a.eat();
}
public static Animal createAnimal() {
Animal a = null;
try (FileReader fr = new FileReader("animal.properties")) {
Properties pro = new Properties();
pro.load(fr);
String className = pro.getProperty("className");
Class c = Class.forName(className);
a = (Animal)c.newInstance();
} catch (Exception e1) {
e1.printStackTrace();
}
return a;
}
}
class Animal{
public void eat() {
System.out.println("吃饭饭");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("狗吃骨头");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
}
class Wolf extends Animal{
public void eat() {
System.out.println("狼吃喜洋洋");
}
}
class Tiger extends Animal{
public void eat() {
System.out.println("老虎吃人");
}
}
ss TestQueue { public static void main(String[] args) { Queue list = new LinkedList();
list.add("A");
list.offer("B");
System.out.println(list.element());
list.remove();
System.out.println(list.peek());
}
}
子接口
* BlockingQueue 阻塞队列 当某个线程从该队列中获取元素时,若该队列为空,获取失败,则该线程会进入阻塞状态
* ArrayBlockingQueue 有界队列 当某个线程向该队列中添加元素时,若该队列满了,添加失败,则该线程会进入阻塞状态
* LinkedBlockingQueue 无界队列
[外链图片转存中...(img-NUgGryi2-1628609627999)]
# chap16-反射
## 反射
概念: 是一个底层技术,常用与框架底层的设计或开发工具的设计
### 类对象
类对象: 是类加载的产物,存储了一个类的全部信息(属性\方法\父类\接口等),存储在方法区中
注意: 一般的类加载只会进行一次,所以类对象也只存在一个!!
类的对象:根据类所创建出来的一个实例对象
>动物园: 老虎Tiger
>
>? 记录老虎全部信息的牌子--------类对象 Tiger.class Class类
>
>? 笼子里面的活生生的老虎--------类的对象 new Tiger(); Tiger类
获取类对象的方式:
1. 类名.class 也可以获取基本数据类型的类对象: 基本数据类型.class 例如: int.class
~~~java
Class class1 = ArrayList.class;
Class c2 = int.class;
-
引用.getClass(); ArrayList<String> list = new ArrayList<String>();
Class c1 = list.getClass();
-
Class.forName(“类的全限定名”); Class c2 = Class.forName("java.util.ArrayList");
Class c3 = Class.forName("day23.Student");
常用方法:
-
getName() 获取类的全限定名–包名.类名 -
getPackage 获取Package对象—包对象 -
getSuperclass() 获取父类类对象 -
Class[] getInterfaces() 获取全部接口类对象 class TestReflect {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<String>();
Class c1 = list.getClass();
System.out.println(c1);
System.out.println(c1.getName());
System.out.println(c1.getPackage());
System.out.println(c1.getSuperclass());
Class[] cs = c1.getInterfaces();
for (Class class1 : cs) {
System.out.println(class1);
}
}
}
基于类对象获取Field对象(即属性对象)
- Field getField(String name) 获取本类或父类中的某个公开属性,参数代表属性名
- Field getDeclaredField(String name) 获取本类的某个属性,参数代表属性名
- Field[] getFields() 获取本类及父类中的全部公开属性
- Field[] getDeclaredFields() 获取本类的全部属性
基于类对象获取Method对象(即方法对象)
- Method getMethod(String name, 类对象…) 获取本类或父类中的某个公开方法
- 参数1: 代表方法名
- 参数2: 代表该方法的形参列表属性所对应的属性的类对象列表
- 例如:
- public void println(int a, String s, double d);
- getMethod(“println”,int.class,String.class,double.class)
- Method getDeclaredMethod(String name, 类对象…) 获取本类的某个方法
- Method[] getMethods() 获取本类及父类中的全部公开方法
- Method[] getDeclaredMethods() 获取本类的全部方法
基于类对象获取Constructor对象(即构造方法对象)
- Constructor getConstructor(类<?>… parameterTypes) 获取本类的某个公开构造方法
- 参数: 代表构造方法的形参列表的数据类型对应的类对象列表
- 例如:
- public Student(String name,int age){}
- getConstructor(String.class,int.class);
- Constructor getDeclaredConstructor(类<?>… parameterTypes) 获取本类的某个构造方法
- Constructor<?>[] getConstructors() 获取本类的全部公开构造方法
- Constructor<?>[] getDeclaredtConstructors() 获取本类的全部构造方法
使用反射手段,获取或操作对象的信息
class TestReflect {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
Student s1 = (Student)c1.newInstance();
System.out.println(s1);
Constructor con1 = c1.getConstructor(String.class,int.class);
Student s2 = (Student)con1.newInstance("小郭",18);
System.out.println(s2);
Field fname = c1.getDeclaredField("name");
fname.setAccessible(true);
fname.set(s1,"小红");
System.out.println(s1);
Method m1 = c1.getMethod("study");
m1.invoke(s1);
m1.invoke(s2);
Method m2 = c1.getDeclaredMethod("study", String.class);
m2.setAccessible(true);
m2.invoke(s1, "Java");
m2.invoke(s2, "Java");
}
}
class Student{
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public void study() {
System.out.println(name+"沉迷学习");
}
private void study(String keCheng) {
System.out.println(name+"沉迷学习"+keCheng);
}
}
反射的优缺点:
优点:
缺点:
设计模式
概念: 是一种被反复使用,被众多程序员认可的代码套路
23种 GOF(Gang of Four 四人组)
单例模式
概念: 创建一个类,该类只能创建一个实例对象
-
饿汉式 可能浪费空间
class ClassA{
private static ClassA ca = new ClassA();
public static ClassA newInstance() {
return ca;
}
private ClassA() {}
}
-
懒汉式 不会浪费空间,但是并发效率低
class ClassB{
private static ClassB cb = null;
public static synchronized ClassB newInstance() {
if(cb==null) cb = new ClassB();
return cb;
}
private ClassB() {}
}
-
双重校验锁 不浪费空间,在保证线程安全的前提下,尽量提高了并发效率
class ClassC {
private static ClassC cc = null;
public static ClassC newInstance() {
if(cc==null) {
synchronized(ClassC.class) {
if(cc==null)cc = new ClassC();
}
}
return cc;
}
private ClassC() {}
}
-
静态内部类实现 懒加载
class ClassD{
private static class Holder{
static ClassD cd = new ClassD();
}
public static ClassD newInstance() {
return Holder.cd;
}
private ClassD() {}
}
工厂模式
概念: 将对象的创建,交给工厂方法(工厂对象)
开闭原则: 修改关闭,扩展开放
class TestFactory {
public static void main(String[] args) {
Animal a = createAnimal();
a.eat();
}
public static Animal createAnimal() {
Animal a = null;
try (FileReader fr = new FileReader("animal.properties")) {
Properties pro = new Properties();
pro.load(fr);
String className = pro.getProperty("className");
Class c = Class.forName(className);
a = (Animal)c.newInstance();
} catch (Exception e1) {
e1.printStackTrace();
}
return a;
}
}
class Animal{
public void eat() {
System.out.println("吃饭饭");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("狗吃骨头");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("猫吃鱼");
}
}
class Wolf extends Animal{
public void eat() {
System.out.println("狼吃喜洋洋");
}
}
class Tiger extends Animal{
public void eat() {
System.out.println("老虎吃人");
}
}
|