赞
踩
(1)Maven是什么?
Maven 是 Apache 开源组织奉献的一个开源项目。Maven 这个词可以翻译为“知识的积累”,
也可以翻译为“专家”或“内行”。
Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM)。
开发人员只需做一些简单的配置,就可以批量完成项目的构建、报告和文档的生成工作。
Maven 是跨平台的,这意味着无论是在 Windows 上,还是在 Linux 或者 Mac 上,都可以
使用同样的命令。
当然,Maven 除了是一个优秀的项目构建方面的管理工具外,还有项目管理相关的其他特殊优势。
比如,项目相关的第三方依赖包,这是每个 Java 程序员不可回避的问题。
每个老程序员都在自己的计算机里有个专门目录,分类保存过去项目开发过程中使用的第三方jar 包,需要的时候从里面筛选。
新程序员就麻烦了,测试项目的时:经常会遇到ClassNoFoundException,导致一整天在搜索和重启中度过。
Maven 可以统一管理所有的依赖 jar,甚至是不同的版本。程序员也可以动态地将自己写好的模块打包成 jar 包让它管理。需要的时候,可以直接通过简单的描述文件告诉 Maven,它会自动帮助程序员找出来,集成到项目中。并且它提供了中央仓库,能帮我们自动下载构件。
在这个开源的年代里,几乎任何 Java 应用都会借用一些第三方的开源类库,这些类库都可通过
依赖的方式引入到项目中来。
总结一下,Maven 的作用如下:
(1)Maven 统一集中管理好所有的依赖包,不需要程序员再去寻找。
(2)对应第三方组件用到的共同 jar,Maven 自动解决重复和冲突问题。
(3)Maven 作为一个开放的架构,提供了公共接口,方便同第三方插件集成。程序员可以将自己需要
的插件,动态地集成到 Maven,从而扩展新的管理功能。
(4)Maven 可以统一每个项目的构建过程,实现不同项目的兼容性管理。
①下载:
②安装:
注意:装好之后不要企图去运行它-------它相当于车的油
JAVA_HOME : 配置的是JDK安装的目录
Path : 配置的是JDK的bin目录,不新建的
CLASS_PATH:配置的是JDK的lib目录
win+R键,在运行窗口输入cmd
验证命令为 : java -version 出现JDK版本号即为成功
JetBrains公司的,分为社区版(免费的)和终级版(收费的)
(1)开发步骤三步走:
①新建Java项目/工程-------------------------小区
②新建Java包-----------------------------------楼+单元
③新建Java类-----------------------------------房子
main中:System.out.println(“hello world”);
——————————————————————————
//注意1:开发工具无需纠结,重要的是编程的思路,对于工具而言,选一个自己喜欢的就好,重要的是提高这个自己常用软件的熟练度(快捷键 字体设置 配置JDK…面向百度进行开发)
//注意2:大家在安装的时候,不要选择C盘系统盘,而且路径中不要出现中文或者空格等等其他特殊符号,因为会出现一些未知的问题
(2)具体安装步骤:
package:声明包
1.作用:避免类的命名冲突
2.同包中的类不能同名,不同包中的类可以同名
3.类的全称:包名.类名,包名常常有层次结构
4.建议:包名所有字母都小写
------------说明:package声明包必须位于第一行
import:导入类
1.同包中的类可以直接访问,不同包的类不能直接访问,若想访问:
----①先import导入类,再访问类----------建议
----②类的全称----------------------- 太繁琐,不建议
说明:import导入类必须位于声明包的下一行
①打开idea→File→New→projet→next→next→项目名cgb2112、路径→src→New→package→包名(day01)→此时出现包名→右键day01→New→Java class(类)→类名(HelloWorld),回车
运行HelloWorld代码如下:
```java
package day01; //声明包day01(楼+单元)
public class HelloWorld { //声明类HelloWorld(房子)
//主方法,为程序的入口(大门口),程序的执行从main开始,main结束则程序结束
public static void main(String[] args) {
//输出hello world
//1)严格区分大小写
//2)所有符号必须是英文模式的
//3)每句话必须以分号结尾
System.out.println("hello world"); //双引号中的原样输出
System.out.println("欢迎大家来到达内");
}
}
```
②此时点击空格处“右键执行Run”或右上角绿色三角运行即可。
(3)
①如果之前创建过项目,在这个文件打开的基础上,
点击File→New→projet→next→next→项目名cgb2112、路径,则会出现以下该提示:
This windows:关闭之前的,打开现在的。若选择这个,之前的文件需要从File→open中打开。
New windows:和现在这个同时运行。 如下:
②若此时新建的 .java文件出现左下角显示“J”图标,是属于idea中其他操作导入模块:
:有两种解决方案:1)和 2),建议用1)。
1)首先找到带J图标的源文件,按住ctrl+c复制到idea中你放到的包文件夹下即可。:
2)解决方案:在File中点击Project Structuere项目结构,或快捷键Ctrl + Alt + Shift + S
接下来如图: 红色部分Content Root(添加内容根目录)去掉 , 重新添加新的 Content Root
成功!!!
①Java开发环境:编译运行过程:
- 编译期:.java源文件,经过编译,生成.class字节码文件
- 运行期:JVM加载.class并运行.class(0和1)
> 特点:跨平台、一次编译到处使用
跨平台如何实现?
在各个系统中,仅安装安装对应的JVM即虚拟机,则可在各个系统均可使用。
②名词解释:
加载.class并运行.class
除了包含JVM以外还包含了运行java程序所必须的环境
JRE = JVM+java系统类库(小零件)
除了包含JRE以外还包含了开发java程序所必须的命令工具
JDK = JRE(包含JVM)+编译、运行等命令工具
= JRE+开发需要的工具
> 说明:
> 1. 运行java程序的最小环境JRE(单纯运行的时候只装JRE就行)
> 2. 开发java程序的最小环境JDK(单纯开发的时候只装JDK就行)
50个全小写的单词,在Java有特殊的意义。
还包含2个保留关键字const goto
1、
可以改变的数,称为变量。在Java语言中,所有的变量在使用前必须声明。
一般通过“变量类型 变量名 = 变量值 ;”这三部分来描述一个变量。如:int a = 3 ;
变量的使用原则:
(1)就近原则,即尽量控制变量的使用范围到最小
——————变量的就近原则:离谁近 就使用谁:
如果想指定本类的成员变量,使用this.变量名来指定
如果想指定父类的成员变量,使用super.变量名来指定
(2)局部变量使用的时候,必须赋值,可以:
-------声明的时候并且赋值 Cat cat = new Cat();
-------先声明再赋值,即初始化 Cat cat; cat = new Cat();
-------注意:基本类型保存的是值,引用类型保存的是地址值
①声明:-------------相当于在银行开帐户
int a; //声明一个整型的变量,名为a
int b,c,d; //声明三个整型的变量,名为b,c,d
//int a; //编译错误,变量不能同名
②初始化:第一次赋值---------相当于给帐户存钱:
int a = 250; //声明整型变量a并赋值为250
int b; //声明整型变量b
b = 250; //给变量b赋值为250
b = 360; //修改变量b的值为360
int c=5,d=10; //声明两个整型变量c和d,并分别赋值为5和10
③使用:---------使用的是帐户里面的钱
对变量的使用就是对它所存的那个数的使用:
int a = 5;
int b = a+10; //取出a的值5,加10后,再赋值给变量b
System.out.println(b); //输出变量b的值15
System.out.println("b"); //输出b,双引号中的原样输出
a = a+10; //在a本身基础之上增10
int c = 5000; //帐户余额
c = c-1000; //取款1000
④变量在用之前必须声明并初始化:
//System.out.println(m); //编译错误,变量m未声明
int m;
//System.out.println(m); //编译错误,变量m未初始化
(1)变量的默认值测试:
创建包: cn.tedu.basic
创建类: TestVariable1.java
package cn.tedu.design;
/*本类用于测试各种类型变量的默认值*/
public class TestVariable1 {
static String name;
static byte b;//整数类型默认值都是0
static short c;
static int d;
static long e;
static float f;//小数类型的默认值是0.0
static double g;
static char j;//char类型的默认值是\u0000
static boolean h;//boolean类型的默认值是false
public static void main(String[] args) {
System.out.println(name);//null,引用类型的默认值
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
System.out.println(f);
System.out.println(g);
System.out.println(h);
System.out.println(j);
System.out.println(h);
}
}
(1)只能包含字母、数字、_和$符,并且不能以数字开头
(2)严格区分大小写
(3)不能使用关键字
(4)允许中文命名,但不建议,建议"英文的见名知意"、"小驼峰命名法"
int a_5$,_3c,$_;
//int a*b; //编译错误,不能包含*号等特殊符号
//int 1a; //编译错误,不能以数字开头
int aa = 5;
//System.out.println(aA); //编译错误,严格区分大小写
//int class; //编译错误,不能使用关键字
int 年龄; //允许,但不建议
int age; //建议"英文见名知意"
int score,myScore,myJavaScore; //建议"小驼峰命名法"
位置:定义在类里方法外
注意:不用初始化,也会自动被初始化成默认值
生命周期:整个类中,类消失了,成员变量才会释放
分为:
1.实例变量:
(1)无static修饰属于对象 例:类中有一个变量:int a;
(2)存储在堆中,有几个对象就有几份 例:Student zs = new Student();
(3)通过对象(引用)打点来访问 例:zs.name;
2.静态变量:
(1)由static修饰属于类
(2)存储在方法区中,只有一份
(3)常常通过类名点来访问
局部变量没有默认值
;但是作为方法的参数的局部变量有默认值
!!!位置:定义在方法里或者方法的声明上
注意:必须手动初始化来分配内存.如:int i = 5;或者int i; i = 5;
生命周期:随着方法的调用而存在,方法运行完毕就释放了
package oopday9.day04.wanke;
import oopday9.day01.Aoo;
import java.util.Arrays;
/**
* 成员变量:写在类中,方法外的。可以在整个类中访问。有默认值。
* 局部变量:写在方法中的(包括方法的参数)。只能在当前方法中访问。没有默认值。
* 【注:方法中的局部变量没有默认值,作为参数的局部变量有默认值。】
*/
public class Doo {
int a;//--------------------成员变量(整个类中访问),有默认值
public static void main(String[] args) {
int d;
System.out.println(Arrays.toString(args));
//System.out.println(d);//编译报错
Eoo o = new Eoo();
o.show(2);
}
}
class Eoo extends Doo{
/**
* 为什么局部变量c没有默认值,而作为方法的参数的局部变量b却有?
* 答:
* 局部变量b也没有默认值,但是当new一个对象时会传参(见上面main中的new对象↑),
* 就自然给局部变量b赋值初始化了【o.show(b: 2);】,所以在方法里打印b的值时不会编译报错。
*
*/
void show(int b){//----------局部变量b(当前方法中访问)和main方法里的局部变量类似,详情见该包的图片
int c;//-----------------局部变量c(当前方法中访问):只声明了没有初始化,没有默认值
System.out.println(a);
System.out.println(b);
//System.out.println(c);//编译报错
}
}
(1)局部变量与成员变量测试:
创建包: cn.tedu.basic
创建类: TestVariable2.java
package cn.tedu.oop;
/**本类用于测试变量的使用*/
public class TestVariable2 {
//2.定义成员变量:
//1)位置:类里方法外
//2)无需手动初始化,会自动赋予对应类型的默认值
//3)作用域:在整个类中生效,类消失,变量才会消失
static int count;
//3.变量有一个使用的原则:就近原则
static int sum = 200;
public static void main(String[] args) {
//1.定义局部变量:
//1)位置:在方法里/局部代码块里
//2)必须手动初始化
//3)作用域:在方法/局部代码块中,对应的代码执行完局部变量就被释放
int sum = 100;//定义在方法中的局部变量sum
System.out.println(sum);//变量的就近原则:使用的都是自己附近的变量,100
System.out.println(count);
for (int i = 0; i < 10; i++) {//局部变量i只能在循环中使用
System.out.println(i);
}
//System.out.println(i);//报错:无法引用变量i:i cannot be resolved to a variable
}
}
-----解释性文本(计算机不执行)
几乎所有编程语言都允许程序员在代码中输入注释
因为编译器会忽略注释,所以注释并不会影响程序的运行结果。
注释的真正作用是: 它可以向任何阅读代码的人描述或者解释程序的实现思路,如何使用以及其它任何相关信息, 提高代码的可读性,方便后期的维护与复用。
(1)Java的注释有3种:
1. 单行注释: 注释单行内容.
格式: 每行都以”//”开头.
快捷方式: Ctrl+/ 添加注释,同样的快捷键,再按一次取消注释
2. 多行注释:注释多行内容,虽然叫多行注释,也可注释单行内容.
格式: 以” /* ”开头, 以” * / ”结束.
快捷方式: 可以输入” /* ”之后按回车添加注释
3. 文档注释: 一般用来注释类和方法,通过注释内容来记录类或者方法的信息.
格式: 以” /** ”开头。 以” * / ”结尾
快捷方式: 输入 “ /** ” 之后按回车添加注释
注1:还可以添加一些额外的信息:作者 时间 版本 参数 返回值类型
注释可以注释内容,被注释的内容不执行,所以我们可以利用注释手段对进行分段代码测试
(1)Java的数据类型分为两类:基本类型 + 引用类型
八种基本数据类型包括: byte,short--->int,long,float,double,boolean
|| ↑
char------↑
byte:字节型, 存整数的,占1个字节,范围-128到127 2^7=128
*short:短整型, 存整数的,占2个字节,范围-32768到32767 2^15=32768
*char:字符型, 存单个字符的,占2个字节,范围从0到65535,采用Unicode字符编码格式
int:最常用整型,存整数的,占4个字节,范围-2^31到-2^31-1(21亿多)
long:长整型,存较大的整数的,占8个字节,范围-2^63到2^63-1(900万万亿),字面值后缀加L(l)
float:单精度浮点型,存小数的,占4个字节,不能表示精确的值,字面值后缀加F(f)
double:双精度浮点型,最常用存小数类型的,占8个字节,不能表示精确的值,字面值后缀加D(d)
boolean:布尔型,存储true或false,占用1个字节
(2)练习题:基本类型的注意事项
1.int:整型,4个字节,-21个多亿到21个多亿
1)整数直接量默认为int类型,但不能超范围,若超范围则发生编译错误
2)两个整数相除,结果还是整数,小数位无条件舍弃(不会四舍五入)
3)整数运算时,若超出int范围则发生溢出(溢出不是错误,但需要避免)
//1)int:整型,4个字节,-21个多亿到21个多亿
int a = 25; //25称为整数直接量,默认int类型
//int b = 10000000000; //编译错误,100亿默认为int类型,但超出int范围了
//int c = 3.14; //编译错误,整型变量中只能装整数
System.out.println(5/2); //2
System.out.println(2/5); //0
System.out.println(5/2.0); //2.5
int d = 2147483647; //int的最大值
d = d+1;
System.out.println(d); //-2147483648(int最小值),发生溢出了,溢出是需要避免的
——————————————————————————————————————————————————————————————————
2.long:长整型,8个字节,很大很大很大
1)长整型直接量需在数字后加L或l
2)运算时若有可能溢出,建议在第1个数字后加L
//2)long:长整型,8个字节,很大很大很大
long a = 25L; //25L为长整型直接量
//long b = 10000000000; //编译错误,100亿默认为int类型,但超出int范围了
long c = 10000000000L; //100L为长整型直接量
//long d = 3.14; //编译错误,长整型变量只能装整数
//运算时若有可能溢出,建议在第1个数字后加L
long e = 1000000000*2*10L;
System.out.println(e); //200亿
long f = 1000000000*3*10L;
System.out.println(f); //不是300亿
long g = 1000000000L*3*10;
System.out.println(g); //300亿
——————————————————————————————————————————————————————————————————
3.double:浮点型,8个字节,很大很大很大
1)浮点数直接量默认为double型,若想表示float需在数字后加F或f
double与float型数据参与运算时,有可能会出现舍入误差,精确场合不能使用
//3)double:浮点型,8个字节,很大很大很大
double a = 3.14; //3.14为浮点数直接量,默认double型
float b = 3.14F; //3.14F为float型直接量
double c = 1234000000000000000000000000000000.0;
System.out.println(c);//1.234E33,科学计数法表示,相当于1.234*(10的33次幂)
double d=3.0,e=2.9;
System.out.println(d-e);//0.10000000000000009,有可能会发生舍入误差,精确场合不能使用
——————————————————————————————————————————————————————————————————
4.boolean:布尔型,1个字节
1)只能赋值为true或false
//4)boolean:布尔型,1个字节
boolean a = true; //true为布尔型直接量-----真
boolean b = false; //false为布尔型直接量----假
//boolean c = 250; //编译错误,布尔型只能赋值为true或false
——————————————————————————————————————————————————————————————————
5.char:字符型,2个字节
1)采用Unicode字符集编码格式,一个字符对应一个码
2)表现的形式是字符char,但本质上是码int(0到65535之间)
3)ASCII码:'a'---97 'A'---65 '0'---48
4)字符型直接量必须放在单引号中,并且只能有1个
5)特殊符号需通过\来转义
//5)char:字符型,2个字节
char c1 = '女'; //字符女
char c2 = 'f'; //字符f
char c3 = '6'; //字符6
char c4 = ' '; //空格符
//char c5 = 女; //编译错误,字符型直接量必须放在单引号中
//char c6 = ''; //编译错误,必须有字符
//char c7 = '女性'; //编译错误,只能有1个字符
char c8 = 65; //0到65535之间
//若c8为char型,则显示字符
//若c8为int型,则显示数字
System.out.println(c8); //println输出时会依据c8的数据类型显示
char c9 = '\\';
System.out.println(c9); //\
char c10 = 0;
System.out.println(c10);//打印出特殊符号:长方形里边一个斜杠
char c11 = 'A';
System.out.println((int)c11);//65
char c12 = 'a';
System.out.println((int)c12);//97
char c13 = '0';
System.out.println((int)c13);//48
——————————————————————————————————————————————————————————————————
5.1拓展:char和int的输出打印问题:
package day02.homework;
public class charDemo {
public static void main(String[] args) {
/**
* 1、char和int的输出打印问题:
* 最后打印出来的是啥 看定义的数据类型
* -(1)若是int 无论变量的值是什么都转为int型再输出
* -(2)若是char 无论变量的值是什么都转为char(即字符型)型再输出
* -(3)若没有数据类型,只有一句打印语句,那括号里是啥数据类型就输出啥数据类型:
* -①若是字符型,就输出字符型
* -②若是字符串,就输出字符串
*/
int a = 'b';
System.out.println(a);//98------若是int 无论变量的值是什么都转为int型再输出
int c = 6;
System.out.println(c);//6
char b = 'A'; //0到65535之间
System.out.println(b);//A------若是char 无论变量的值是什么都转为char(即字符型)型再输出
char d = 97;
System.out.println(d);//a char是字符
char d2 = 'b';
System.out.println(d2);
System.out.println('b');//b -----若没有数据类型,若是字符型,就输出字符型
System.out.println('a');//a
System.out.println('0');//0
System.out.println("haha");//------若没有数据类型,若是字符串,就输出字符串
System.out.println(0);//0
}
}
——————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————
练习题:
变量定义如下:int i=128;,下列赋值语句正确的是:(ACD)
A、int j=i; B、short s=i; C、short s=128; D、long l=i;
注:B选项:--------i为int型,大转小需要强转,i前加(short)
D选项:-------为自动转换
(1)整数类型的字面值类型是int
(2)浮点类型的字面值类型是double
(3)byte short char 三种比int小的类型,可以在范围内直接赋值
(4)三种字面值后缀 :
(5)三种字面值前缀: 0b-二进制 0-八进制 0x-十六进制
(6)练习:查看字面值前缀
package cn.tedu.basic;值
/*本类用于测试字面值前缀*/
public class TestTypePre {
public static void main(String[] args) {
System.out.println(100);//100,十进制,10的平方
System.out.println(0b100);//4,二进制,2的平方
System.out.println(0100);//64,八进制,8的平方
System.out.println(0x100);//256,十六进制,16的平方
System.out.println(101);//101=100+1,十进制,10的平方+10的0次方
System.out.println(0b110);//6=4+2,二进制,2的平方+2的1次方
System.out.println(0111);//73=64+8+1,八进制,8的平方+8的1次方+8的0次方
System.out.println(0x101);//257=256+1,十六进制,16的平方+16的0次方
}
}
运算结果的数据类型与参与运算的最大类型保持一致 int+int->int double/int->double
整数运算会出现溢出的现象,一旦溢出,数据就不正确了(光年案例)
byte short char三种比int小的类型,运算的时候需要先自动提升int类型,再参与运算
浮点数的特殊值:Infinity NaN
浮点数运算不精确的问题:解决方案:BigDecimal
注意1:不能使用double类型参数的构造方法,需要使用String类型的构造函数,否则还会出现不精确的问题
注意2:除法运算时,如果出现除不尽的现象还会报错,所以需要指定保留位数与舍入方式(四舍五入)
口诀:小转大,直接转 大转小,强制转 浮变整,小数没
基本类型由小到大依次为:
byte,short---> int,long,float,double,boolean
|| ↑
char-------↑
(1)两种方式:
//类型间的转换:
int a = 5;
long b = a; //自动类型转换
int c = (int)b; //强制类型转换
long d = 5; //自动类型转换
double e = 5; //自动类型转换
/* 1、强制类型转换有可能发生溢出 */
long f = 10000000000L;
int g = (int)f; //强制类型转换
System.out.println(g); //1410065408,强转有可能发生溢出
/* 2、强制类型转换(cast)会丢失精度 */
double h = 25.987;
int i = (int)h; //强制类型转换
System.out.println(i); //25,强转有可能丢失精度
/**
* 3、Math.round()方法可以四舍五入
* 9.4-----9
* 9.5-----10
* 9.6-----10
*/
double a1 = 9.55;
int b1 = (int)Math.round(a1);
System.out.println(b1);//10
(2)两点规则:
byte b1 = 5; //byte的范围为-128到127之间
byte b2 = 6;
byte b3 = (byte)(b1+b2);
int a = '0';
System.out.println(a); //48
System.out.println('2'); //因为进入print的底层中,传的该参数是啥,就打印啥
System.out.println(2+2); //4
System.out.println(2+'2'); //52,2加上'2'的码50
System.out.println('2'+'2'); //100,'2'的码50,加上'2'的码50
(1)注意:布尔类型不参与类型转换
(2)注意:基本类型之间能否转换,不取决于字节数,字节数只能做参考,取决于类型的取值范围
(3)注意:我们这里所说的是基本类型之间的转换,引用类型之间的转换取决于是否有继承关系
比如:你可以说小猫是小动物,但是不能说小猫是小汽车,不然后面的这种错误的情况会报:类型转换异常
(4)练习题:类型之间的转换与字面值规则:
package cn.tedu.basic;
/*本类用于测试类型转换
* 1.byte1--short2--char2--int4--long8--float4--double8
* */
public class TestTypeChange {
public static void main(String[] args) {
byte a = 10;
short b = a;//不会报错,小转大
int c = 1;
long d = c;//不会报错,小转大
float f = 3.14f;
double e = f;//不会报错,小转大
long g = 97525205273520L;
float h = g;//不会报错,小转大
char i = 'a';
int j = i;//不会报错,小转大
System.out.println(j);//97
int a1 = 1;
byte b1 = 2;
//byte c1 = a1 + b1;//会报错,大转小
byte c1 = (byte) (a1 + b1);//强制类型转换就不会报错了
System.out.println(c1);//3
byte d1 = (byte) 128;
System.out.println(d1);//-128,强制类型转换要在小类型的范围内转,否则会发生数据溢出的问题
short e1 = 'a';
char f1 = 120;
System.out.println(e1);//97
System.out.println(f1);//'x'
float h1 = 32874.456f;
int i1 = (int) h1;
System.out.println(i1);//32874
}
}
普通的四则运算,并不能直接改变变量本身的值,除非 i = i*10+8
//当一个整数"除以0"时,会出现:-------ArithmeticException 运算异常
System.out.println(2/0);
1)可以改变变量本身的值
2)前缀式: 符号在前,先改变变量本身的值(+1/-1),再使用(打印/参与运算…)
3)后缀式: 符号在后,先使用(打印/参与运算…),再改变变量本身的值(+1/-1)
4)注意:不管是前缀式还是后缀式,一定是会改变变量本身的值,区别在于执行的时机不同
//++单独使用:
int a=5,b=5;
a++; //相当于a=a+1
++b; //相当于b=b+1
System.out.println(a); //6
System.out.println(b); //6
//++被使用:
int a=5,b=5;
int c = a++; //1)保存a++的值5 2)a自增1变为6 3)将第1步保存的值5赋值给c--底层运算过程
//---粗暴记法:a++的值为5,c就是5
int d = ++b; //1)保存++b的值6 2)b自增1变为6 3)将第1步保存的值6赋值给d--底层运算过程
//---粗暴记法:++b的值为6,d就是6
System.out.println(a); //6
System.out.println(b); //6
System.out.println(c); //5
System.out.println(d); //6
//--单独使用:
int a=5,b=5;
a--; //相当于a=a-1
--b; //相当于b=b-1
System.out.println(a); //4
System.out.println(b); //4
//--被使用:
int a=5,b=5;
int c = a--; //a--的值为5,所以c的值为5
int d = --b; //--b的值为4,所以d的值为4
System.out.println(a); //4
System.out.println(b); //4
System.out.println(c); //5
System.out.println(d); //4
==
,!比较运算符最终的结果是布尔类型的
练习题:==比较的练习
package cn.tedu.basic;
/*本类用于测试运算符*/
public class TestOperator {
public static void main(String[] args) {
//创建小猫类的对象
Cat c1 = new Cat();
Cat c2 = new Cat();
int[] a1 = {1,2,3};
int[] a2 = {1,2,3};
int b1 = 4;
int b2 = 4;
boolean f1 = true;
boolean f2 = true;
/*==如果比较的是引用类型,比较的值是引用类型变量保存的地址值*/
System.out.println(c1 == c2);//false
System.out.println(a1 == a2);//false
/*==如果比较的是基本类型,比较的值就是字面值,也就是这个变量具体存的那个数*/
System.out.println(b1 == b2);//true
System.out.println(f1 == f2);//true
}
}
class Cat{
String name;
int age;
public void bark(){
System.out.println("喵喵叫");
}
}
&&:短路与(并且),两边都为真则为真,见false则false(前边判断为false后,后边的就不执行了)
//当第1个条件为false时,发生短路(后面的不执行了)
||:短路或(或者),有真则为真,见true则true
//当第1个条件为true时,发生短路(后面的不执行了)
!:逻辑非(取反),非真则假,非假则真
逻辑运算符:&&(见false则false) ||(见true则true) !(非真则假,非假则真)
短路与的演示:见false则false(前边判断为false后,后边的就不执行了)
package day05.homework;
public class practice {
public static void main(String[] args) {
int a = 10;
int b = 20;
boolean flag = a++>b-- && b++>a--;//短路与,只执行:a++>b--
System.out.println(flag+",a="+a+",b="+b);//false,a=11,b=19
}
}
int a=5,b=10,c=5;
//&&的演示:
boolean b1 = b>=a && b<c;
System.out.println(b1); //true&&false=false
System.out.println(b<=c && b>a); //false&&true=false
System.out.println(a==b && c>b); //false&&false=false
System.out.println(b!=c && a<b); //true&&true=true
int age = 25;
System.out.println(age>=18 && age<=50); //看age是否在18到50之间
//||的演示:
System.out.println(b>=a || b<c); //true||false=true
System.out.println(b<=c || b>a); //false||true=true
System.out.println(b!=c || a<b); //true||true=true
System.out.println(a==b || c>b); //false||false=false
int score = 89;
System.out.println(score<0 || score>100); //看score是否不合法
//!的演示
boolean b2 = !(a<b);
System.out.println(b2); //!true=false
System.out.println(!(a>b)); //!false=true
//短路的演示
int a=5,b=10,c=5;
boolean b3 = a>b && c++>2;
System.out.println(b3); //false
System.out.println(c); //5,发生短路了
boolean b4 = a<b || c++>2;
System.out.println(b4); //true
System.out.println(c); //5,发生短路了
语法:
boolean?数1:数2
执行过程:
> 注:整个表达式是有值的,它的值要么是?号后的数1,要么是:号后的数2
- 计算boolean的值:
- 若为true,则整个表达式的值为?号后的数1
- 若为false,则整个表达式的值为:号后的数2
//计时器:以毫秒为单位返回当前时间 程序的初始和末尾定义两个时间t1和t2 可以计算运行的时间
long t1 = System.currentTimeMillis();
int num = 5;
int flag = num>0?1:-1;
System.out.println(flag); //1
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
int a=8,b=55;
int max = a>b?a:b;
System.out.println("max="+max);
——————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————
练习题:
下列代码输出的结果是?(B)
boolean b = true?false:true==true?false:true;
System.out.println(b);
A、true B、false C、null D、空字符串
因为:
b = true ?false :true==true?false:true;
-------------------------------------是一种简写的形式,比较方便,运算时会自动进行类型转换
简单赋值运算符:=
扩展赋值运算符:+=,-=,*=,/=,%=
注:扩展赋值自带强转功能
int a = 5;
a += 10; //相当于a=(int)(a+10)
System.out.println(a); //15
a *= 2; //相当于a=(int)(a*2)
System.out.println(a); //30
a /= 6; //相当于a=(int)(a/6)
System.out.println(a); //5
//小面试题:
short s = 5;
//s = s+10; //编译错误,需强转: s=(short)(s+10);
s += 10; //相当于s=(short)(s+10)--------符合运算符自带强转功能!!!
+:
- 若两边为数字,则做加法运算
- 若两边出现了字符串,则做字符串连接
- 任何类型与字符串相连,结果都会变为字符串类型----同化作用
//字符串拼接演示
int age = 38;
System.out.println("age="); //age=
System.out.println(age); //38
System.out.println("age="+age); //age=38
System.out.println("我的年龄是"+age); //我的年龄是38
System.out.println("我今年"+age+"岁了"); //我今年38岁了
String name = "WKJ";
System.out.println("name="+name); //name=WKJ
System.out.println("大家好,我叫"+name); //大家好,我叫WKJ
System.out.println("大家好,我叫"+name+",今年"+age+"岁了"); //大家好,我叫WKJ,今年38岁了
//同化作用演示
System.out.println(10+20+30+""); //60---------String
System.out.println(10+20+""+30); //3030-------String
System.out.println(""+10+20+30); //102030-----String
&与:全真才真
| 或:全假才假
^异或:相同为0 不同为1
~ 非: 非0为1,非1为0
(11)优先级控制:如果表达式的运算比较复杂,需要控制优先级,可以使用小括号
(12)拓展:instanceof
顺序结构:从上往下逐行执行,每句必走
分支结构:有条件的执行某语句一次,并非每句必走
循环结构:有条件的执行某语句多次,并非每句必走
顺序结构中的代码会按照顺序一行一行向下执行所有的代码语句,可以用来进行输入 输出 计算等的操作
但顺序结构不可以完成先做判断,再做选择的流程
{ }
!!!if(判断条件){
如果判断条件的结果为true,就执行此处代码,不符合条件,此处跳过
}
例:
//1)偶数的判断:
int num = 6; //带数(6,5)
if(num%2==0){
System.out.println(num+"是偶数");
}
System.out.println("继续执行...");
//2)满500打8折:
double price = 600.0; //消费金额 带数(600.0,300.0)
if(price>=500){ //满500
price *= 0.8; //打8折
}
System.out.println("最终结算金额为:"+price);
练习题:-------注意:if里有一句话 可以不执行!!!
package day05.homework;
public class practice {
public static void main(String[] args) {
int pigs = 5;
boolean isOne = true;
boolean isTwo = false;
if ((pigs == 4) && !isTwo)//if里若有一句话可以不加"{}"
System.out.println("f");
System.out.println("s");//打印:s
if ((isTwo = true) && isOne)//一个"="是赋值 直接输出t
System.out.println("t");//打印:t
}
}
if(判断条件){
如果判断条件的结果为true,就执行此处的代码
}else{
如果不符合条件,执行else处的代码
例:
//1)偶数、奇数的判断:
int num = 5; //带数(6,5)
if(num%2==0){
System.out.println(num+"是偶数");
}else{
System.out.println(num+"是奇数");
}
System.out.println("继续执行");
//2)满500打8折,不满500打9折:
double price = 300.0; //带数(600.0,300.0)
if(price>=500){
price *= 0.8;
}else{
price *= 0.9;
}
System.out.println("最终结算金额为:"+price);
if(判断条件1){
符合判断条件1,执行此处代码,不符合,继续向下判断
}else if(判断条件2){
符合判断条件2,执行此处代码,不符合,继续向下判断
}else if(判断条件3){
符合判断条件3,执行此处代码,不符合,继续向下判断
}else{
保底选项,以上条件均不符合的情况下,执行此处代码
}
例:
package day04;
import javax.sql.rowset.spi.SyncResolver;
import java.util.Scanner;
/**
* 1、创建ScoreLevel类,要求:接收用户输入的成绩score(double),并输出
* 2、在“1”的基础上加一个功能----递归调用:在一个方法的内部,调用自己的方法(循环整个过程) 而循环是重复步骤
*/
public class ScoreLevel {
public static void main(String[] args) {
/*
1、创建ScoreLevel类,要求:接收用户输入的成绩score(double),并输出:
Scanner scanner = new Scanner(System.in);
System.out.println("请输入成绩");
Double score = scanner.nextDouble();
if(score<0 || score>100){
System.out.println("成绩不合法");
}else if(score <= 90){
System.out.println("A-优秀");
}else if(score <= 80){
System.out.println("B-良好");
}else if(score <= 70){
System.out.println("C-中等");
}else{
System.out.println("D-不及格");
}
*/
/* 2、在“1”的基础上加一个功能----递归调用:在一个方法的内部,调用自己的方法(循环整个过程) 而循环是重复步骤 */
System.out.println("请输入一个成绩");
demo(); //递归
}
public static void demo() {
Scanner scanner = new Scanner(System.in);
double score = scanner.nextDouble();
if(score<0 || score>100) {
System.out.println("成绩不合法,请再次输入!");
demo();
}else if(score >=90){
System.out.println("A-优秀");
} else if (score >= 80) {
System.out.println("B-良好");
} else if (score >= 70) {
System.out.println("C-中等");
} else{
System.out.println("D-不及格");
}
}
}
break:
- 若case中有break:打印对应的case语句后,直接跳出switch
- 若case没有break:从对应命令的case开始往下贯穿打印每一句话
default:
当case中没有break时会一直往下贯穿,执行default这个保底选项
当case中没有break时并且没有任何的case被匹配到,会先执行default这个“保底选项”,再往下贯穿
常见面试题:switch可以作用于什么类型的数据上
------------byte,short,int,char,String,枚举类型
注:jdk 1.7以后添加了string类型
1)小括号中变量支持的类型:byte short char int String enum 与4个基本类型对应的包装类
2)注意: 如果配置了default默认选项,而且没有任何的case被匹配到,就会执行default这个“保底选项”
3)case的个数 是否加break 是否加default全部都是可选的,根据自己的具体业务做决定
4)小括号中变量的类型必须与case后value的类型一致
5)执行顺序:先拿着变量的值,依次与每个case后的值做比较,如果相等,就执行case后的语句
若这个case后没有break,就会继续向下执行下一个case,如果一直没有遇到break,就会发生穿透现象,包括default
```java
switch (变量名){
case value1 : 操作1;break;//可选
case value2 : 操作2;break;//可选
case value3 : 操作3;break;//可选
case value4 : 操作4;break;//可选
default:保底选项;//可选
}
例:
package ooday6.day04.switch0_0;
import java.util.Scanner;
/**
* //命令解析程序
* 创建commandBySwitch类 要求:接收用户输入的命令command(int),并输出
*
* switch…case结构:多条路 只能对整数进行判断
* 优点:效率高、结构清晰
* 缺点:只能对整数判断相等
*
* break:
* 若case中有break:打印对应的case语句后,直接跳出switch
* 若case没有break:从对应命令的case开始往下贯穿打印每一句话
*
* default:可以写在switch中的任何地方;写不写都可以;
* 若写了:在case没有匹配参数时会按顺序执行(此时不执行case,因为都未匹配成功先执行default再贯穿剩余case);
* :在case匹配了参数时不会执行default
* 若没写:会按顺序执行case匹配到的语句;若没有匹配的则全部执行
*/
public class CommandBySwitch {
public static void main(String[] args) {
// System.out.println("请选择功能:1.取款 2.存款 3.查询余额");//-----注意:要想打印这句话,需放在扫描数的上边。
// Scanner scanner = new Scanner(System.in);
// int command = scanner.nextInt();
// System.out.println("请选择功能:1.取款 2.存款 3.查询余额 4.退卡操作");
// int command = new Scanner(System.in).nextInt();
// switch (command) { //byte,short,int,char,String,枚举类型
// case 1:
// System.out.println("取款操作...");
// break;
// case 2:
// System.out.println("存款操作...");
// break;
// case 3:
// System.out.println("查询余额...");
// break;
// case 4:
// System.out.println("退卡操作...");
// break;
// default:
// System.out.println("操作错误...");
// }
int num = 5;
switch (num) {
case 3:
System.out.println("555");
case 6:
System.out.println("8888");
default://default:可以写在switch中的任何地方;写不写都可以;
// 若写了:在case没有匹配参数时会按顺序执行;
// :在case匹配了参数时不会执行default
//若没写:会按顺序执行case匹配到的语句;若没有匹配的则全部执行
System.out.println("11");
//break;//若有break,直接跳出switch;若没有break,则贯穿执行全部输出语句
case 0:
System.out.println("22");
}
}
}
————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————
练习题:
如下变量的定义,double d = 1.5;int x=1,y=2,z=3;则正确的switch语句是(AC)
A、switch((int)d){
case 1: System.out.println(x);break;
case 2: System.out.println(y);break;
}
B、switch(d){
case 1.5: System.out.println(x);break;
case 2.0: System.out.println(y);break;
}
C、switch(x+y){
case 1: System.out.println(x);break;
case 2: System.out.println(y);break;
}
D、switch(x+y){
case 1: System.out.println(x);break;
case z: System.out.println(y);break;
}
注:D选项------------case后不能为变量!!!应该为常量表达式!!!
跳过循环体中剩余的语句而强制执行下一次循环
,
如下语句的输出结果是什么?( A )
public class Test04 {
public static void main(String[] args) {
int x=0;
int y=10;
do {
y--;
if(y==6)continue;
++x;
} while (x < 5);
System.out.print(x + "," + y);
}
}
A.5,4
B.4,5
C.5,6
D.6,6
总结:
按步骤走一遍:
y=9 x=1
y=8 x=2
y=7 x=3
y=6 此时if判断为true,执行continue,跳过下面的语句强行进入下一次 循环从while再开始;此时x没有执行上一句,还为3;
y=5 x=4 此时x=4还是小于5,继续从do执行
y=4 x=5,此时x=5,不小于5了,整个循环结束,最终x=5;y=4
所以程序结束后:x=5;y=4
总结:本题考查
continue:continue 语句是跳过循环体中剩余的语句而强制执行下一次循环,
其作用为结束本次循环,即跳过循环体中下面尚未执行的语句,
接着进行下一次是否执行循环的判定。
while(boolean):直到boolean的值为false时,while循环结束
6)练习:复习分支结构
package cn.tedu.basic;
import java.util.Scanner;
/*本类用于复习分支结构*/
public class TestIf {
public static void main(String[] args) {
//1.提示并接收用户输入的月份
System.out.println("请输入您要测试的月份:");
int month = new Scanner(System.in).nextInt();
//2.对用户输入的数据进行判断
if(month<=0 || month >12){
System.out.println("您输入的数据不正确!");
}else{
//3.如果用户输入的数据正确,我们就进行季节的判断
if(month >=3 && month <=5){
System.out.println(month+"月是春天");
}else if(month >=6 && month <=8){
System.out.println(month+"月是夏天");
}else if(month >=9 && month<=11){
System.out.println(month+"月是秋天");
}else{
System.out.println("冬天就要来啦,春天还会远吗?");
}
}
}
}
1)循环:反复多次执行一段相同或相似的代码
2)循环三要素:
循环变量的初始化
循环的条件(以循环变量为基础)
循环变量的改变(向着循环的结束变)
注:循环变量:在整个循环过程中所反复改变的那个数
while(判断条件){
如果符合判断条件,继续循环
}
注意:常用来完成死循环,但死循环必须设置出口!
练习题1:
package day04;
/** while结构的演示: */
import com.sun.xml.internal.ws.resources.UtilMessages;
/**
* while结构:先判断后执行,有可能一次都不执行
*
* 语法:
* while(boolean){
* 语句块------反复执行的代码
* }
*
* 执行过程:
* 判断boolean的值,若为true则执行语句块,
* 再判断boolean的值,若为true则再执行语句块,
* 再判断boolean的值,若为true则再执行语句块,
* 如此反复,直到boolean的值为false时,while循环结束
*/
public class WhileDemo {
public static void main(String[] args){
//1、输出5次行动是成功的阶梯
int times = 0; //1)循环变量的初始化
while(times < 5){ //2)循环的条件
System.out.println("行动是进步的阶梯");
times++; //3)循环变量的改变
}
System.out.println("继续执行...");
/*
2、输出9的乘法表
int num = 1;
while(num<=9){
System.out.println(num+"*9="+num*9);
num++;
}
System.out.println("继续执行");
*/
}
}
————————————————————————————————————————————————————————
练习题2:while猜数字小游戏
package day04;
import java.util.Random;
import java.util.Scanner;
/**
* 猜数字小游戏 :
* while(){
* if(){
* .....
* }else if(){
* .....
* }else{
*
* }
* }
*/
public class Guessing {
public static void main(String[] args) {
/*
int num = (int)(Math.random()*1000+1); //1到1000
Math.random()--------------0.0到0.9999999999999999...
*1000----------------------0.0到999.99999999999999...
+1-------------------------1.0到1000.9999999999999...
(int)----------------------1到1000
*/
Scanner scanner = new Scanner(System.in);
// int num = 250;//定死的数
int num = (int)(Math.random()*1000+1);//1~1000的随机数
System.out.println(num);//作弊 直接将生成的随机数显示在控制台
System.out.println("猜吧");
int guess = scanner.nextInt();
while(guess!=num){
if(guess>num){
System.out.println("猜大了!继续猜");
}else{
System.out.println("猜小了!继续猜");
}
guess = scanner.nextInt();
}
System.out.println("猜对了");
}
}
格式:
do{ }while( );
循环一定会执行一次,然后再判断,如果符合条件,再执行后面的循环
do{
//循环体......
}while(判断条件);
练习题:do{ if(){}else if() }while();
package day04;
import java.util.Scanner;
/**
* 猜数字小游戏:
* do{
* if(){
* .....
* }else if(){
* .....
* }while();
*
* 注意:在if中-----可以以else if结尾。
*/
public class DoWhileDemo {
public static void main(String[] args) {
/*
int num = (int)(Math.random()*1000+1); //1到1000
Math.random()--------------0.0到0.9999999999999999...
*1000----------------------0.0到999.99999999999999...
+1-------------------------1.0到1000.9999999999999...
(int)----------------------1到1000
*/
/** 第一种方式: */
Scanner scanner = new Scanner(System.in);
int num = (int)(Math.random()*1000+1);//1到1000之内的随机数
System.out.println(num);
//假设num=250
//300(大) 200(小) 250(对)
int guess;
do{
System.out.println("猜吧");
/*
若上边不写【int guess;】,从此处声明guess的数据类型int 则编译错误
原因:从变量的声明开始,到包含它最近的大括号结束
*/
// int guess = scanner.nextInt();
guess = scanner.nextInt();
if(guess>num){
System.out.println("猜大了,继续猜");
}else if(guess<num){
System.out.println("猜小了,继续猜");
}
}while(guess!=num);
System.out.println("猜对了");
/*
第二种方式:
Scanner scanner = new Scanner(System.in);
int num = (int)(Math.random()*1000+1);//1到1000之内的随机数
System.out.println(num);//作弊
//假设num=250
//300(大) 200(小) 250(对)
int guess;
do{
System.out.println("猜吧");
guess = scanner.nextInt();
if(guess>num){
System.out.println("猜大了,继续猜");
}else if(guess<num){
System.out.println("猜小了,继续猜");
}else{
System.out.println("猜对了");
}
}while(guess!=num);
*/
}
}
可以帮我们多次重复的做某一件事
1)for循环-----不同作用域可以同名
for(开始条件;循环条件;更改条件){
如果符合循环条件,就会执行循环体里的内容
}
注意1:写法小窍门:从哪开始 到哪结束 循环变量如何变化
注意2:for循环能够执行多少次,取决于循环变量可以取到几个值
2)嵌套for循环--------若求次数,则外循环的次数x内循环的次数
-外层循环控制的是轮数,内层循环控制的是每一轮中执行的次数
-对于图形而言,外层循环控制的是行数,内层循环控制的是列数
for(开始条件;循环条件;更改条件){//外层循环---行数
for(开始条件;循环条件;更改条件){//内层循环---列数
循环体
}
}
注意:外层循环控制的是行数,内层循环控制的是列数
注意:外层循环控制的是轮数,内层循环控制的是在这一轮中执行的次数
练习题:输出1~9的乘法表
package day05;
public class ForDemo {
public static void main(String[] args) {
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
System.out.print(i+"*"+j+"="+i*j+"\t");
}
System.out.println();
}
}
}
3)高效for循环
for(遍历到的元素的类型 遍历到的元素的名字 :要遍历的数组/集合名){
循环体
}
优点:写法简单,效率高
缺点:只能从头到尾的遍历数据,不能进行步长的选择
//1.如果明确知道循环的次数/需要设置循环变量的变化情况时–使用for循环
//2.如果想写死循环–while(true){}
//3.如果需要先执行一次,再做判断–do-while循环
//4.循环之间是可以互相替换的,但是最好使用比较合适的循环结构
静态创建 int[] a = {1,2,3,4,5};
静态创建 int[] a = new int[]{1,2,3,4,5};
动态创建 int[] a = new int[5]; //后续可以动态的给数组中的元素赋值
注意:不管是什么样的创建方式,都需要指定数组的类型与长度
(1)我们可以通过数组的下标来操作数组中的元素
数组下标从0开始,最大下标是数组的长度-1,即length-1。
如果访问到了不是这个数组的下标,会出现数组下标越界异常
比如:a[5]表示的就是数组中的第6个元素
(2)获取数组的长度:-----a.length
数组一旦创建,长度不可改变,长度指的是数组中元素的个数a.length,并且数组的长度允许为0:[ ]
(3)数组的创建过程:
复制和扩容都是创建了一个新的数组!
<————①toString(数组名) : 除了char类型的数组以外,其他类型的数组想要查看具体元素,需要使用本方法,否则打印的是地址值
②copyOf(原数组名,新数组的长度) : 用来实现数组的复制 扩容 缩容
如果新数组的长度 > 原数组长度,就扩容,
反之,则缩容,
如果两者长度一致,就是普通的复制数组
注意:一定是创建了新数组,而不是对原数组的长度做操作
(1)【两种复制 和 一种扩容】:
(1.1)
【Arrays.copyOf(a, 6); //灵活性差】:
/** 常规复制:复制一个数组变为另一个新的数组 */
int[] a = {10, 20, 30, 40, 50};
//a:源数组
//b:目标数组
//6:目标数组的长度(元素个数)
//---若目标数组长度>源数组长度,则末尾补默认值
//---若目标数组长度<源数组长度,则将末尾的截掉
int[] b = Arrays.copyOf(a, 6); //灵活性差
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + "\t");
/*
控制台输出:
10 20 30 40 50 0
*/
}
(1.2)
【System.arraycopy(a1, 1, b1, 0, 4); //灵活性好】:
/** 灵活性好 的复制:可以自己指定想要的源数组的元素下标位置进行复制 */
int[] a1 = {10, 20, 30, 40, 50};
int[] b1 = new int[6]; //0,0,0,0,0,0
//a1:源数组
//1:源数组的起始下标
//b1:目标数组
//0:目标数组的起始下标
//4:要复制的元素个数
System.arraycopy(a1, 1, b1, 0, 4); //灵活性好
for (int i = 0; i < b1.length; i++) {
System.out.print(b1[i] + "\t");
/*
控制台输出:
20 30 40 50 0 0
*/
}
————————————————————————————————————————————————————————————————————
(2)
【c = Arrays.copyOf(c, a.length );】:
/** 数组的扩容:直接将之前的数组进行扩容变成一个新的数组 */
int[] c = {10, 20, 30, 40, 50};
//数组扩容(创建了一个更大的新的数组,并将源数组数据复制进去了)
c = Arrays.copyOf(c, a.length );
for (int i = 0; i < c.length; i++) {
System.out.print(c[i] + "\t");
/*
控制台输出:
10 20 30 40 50 0
*/
}
(2)数组的遍历
一般习惯使用for循环,循环变量代表的就是数组的下标,从0开始,最大值是a.length-1
package day06;
import java.util.Arrays;
//求数组元素的最大值,并将最大值放在数组最后一个元素的下一个位置
public class MaxOfArray {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i=0;i<arr.length;i++){
arr[i] = (int)(Math.random()*100);
System.out.println(arr[i]);
}
int max = arr[0]; //假设第1个元素为最大值
for(int i=1;i<arr.length;i++){ //遍历剩余元素
if(arr[i]>max){ //若剩余元素大于max
max = arr[i]; //则修改max为较大的
}
}
System.out.println("最大值为:"+max);
arr = Arrays.copyOf(arr,arr.length+1); //扩容
arr[arr.length-1] = max; //将最大值max赋值给arr中的最后一个元素
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
//8)数组的排序:
int[] arr = new int[10];
for(int i=0;i<arr.length;i++){
//给数组arr中的10个元素赋值为0~~100的随机数
arr[i] = (int)(Math.random()*100);
System.out.println(arr[i]);
}
Arrays.sort(arr);
//1.对arr进行升序排列:
System.out.println("数组排序后的数据:");
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
//2.对arr进行降序排列:
System.out.println("倒序输出:");
for(int i=arr.length-1;i>=0;i--){
System.out.println(arr[i]);
}
外层循环控制比较的轮数,所以循环变量从1到n-1轮,代表的是轮数
内层循环控制的是这一轮中比较的次数,而且是数组中两个相邻元素的比较,所以循环变量代表的是数组的下标
(8)练习题:
package cn.tedu.basic;
/*本类用于复习数组的操作*/
public class TestArray {
public static void main(String[] args) {
//需求1:求出数组中所有元素之和
//f1();
//需求2:求出数组中所有元素的最大值
f2();
}
private static void f2() {
//需求2:求出数组中所有元素的最大值
//1.定义一个数组
int[] a = {10,20,98,40,55};
//2.定义一个变量,用来存储结果,也就是数组中所有元素的最大值
int max = a[0];//这个位置不能写0,应该是数组中的第一个元素
//3.遍历数组,依次拿出数组中的每一个元素与之前设定的元素做比较
for (int i = 0; i < a.length; i++) {
//4.判断当前遍历到的元素,与max谁大
if(a[i]>max){//如果当前遍历到的元素比max大
max = a[i];//就把遍历到的这个元素的值赋值给max
}
}
//5.循环结束,输出数组中最大的值
System.out.println("数组中的最大值为:"+max);
}
private static void f1() {
//需求1:求出数组中所有元素之和
//1.定义一个数组
int[] a = {12,12,22,22,22};
//2.定义一个变量,用来保存最终求和的结果
int sum = 0;
//3.通过数组的遍历,遍历数组中的每一个元素,然后累加
for (int i = 0; i < a.length; i++) {
sum = sum + a[i];
}
System.out.println("数组累加所有元素之和为:"+sum);
}
}
(1)10个随机数求最大值-----《MaxOfArray.java》
package day06;
import java.util.Arrays;
//求数组元素的最大值,并将最大值放在数组最后一个元素的下一个位置
public class MaxOfArray {
public static void main(String[] args) {
//10个随机数求最大值:
int[] arr = new int[10];
for(int i=0;i<arr.length;i++){
arr[i] = (int)(Math.random()*100);
System.out.print(arr[i]+" ");//10 60 4 52 17 63 94 61 78 62
}
int max = arr[0];//假设第一个元素为最大值
for(int i=1;i<arr.length;i++){//遍历剩余元素
if(arr[i]>max){//若剩余元素大于max
max = arr[i];//将max修改为较大的
}
}
System.out.println("最大值为:"+max);//最大值为:94
//通过扩容,把数组的最大值放到最后一位
arr = Arrays.copyOf(arr,arr.length+1);//扩容,即创建了一个新的数组,长度加了1:
arr[arr.length-1] = max;//将最大的max赋值给arr的最后一个元素
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);//10 60 4 52 17 63 94 61 78 62 94
}
System.out.println("最大值:"+max);
/*
例2://10个随机数求最小值
int[] arr =new int[10];
for(int i=0;i<arr.length;i++){
arr[i] = (int)(Math.random()*100);
System.out.println(arr[i]);
}
int min = arr[0];//假设第一个元素为最小值
for(int i=0;i<arr.length;i++){//遍历剩余元素
if(arr[i]<min){//若剩余元素小于min
min = arr[i];
}
}
System.out.println("最小值为:"+min);
*/
}
}
(2)返回最小值-----《MinOfArray.java》
package day06;
import java.util.Arrays;
public class MinOfArray {
/** 返回最小值的演示 */
public static void main(String[] args) {
/*
//降序排序:利用遍历i--:
int[] c = new int[]{1,4,55,999,2,7};
Arrays.sort(c);
System.out.println("进行降序排序:");
for(int i=c.length-1;i>=0;i--){
System.out.print(c[i]+" ");//999 55 7 4 2 1
}
System.out.println();
System.out.println("最小值为:"+c[0]);//最小值为:1
*/
int e = getMinOfArray();
System.out.println(e);//19
}
public static int getMinOfArray(){
int[] o = new int[5];
System.out.println("进行排序");
for(int i=o.length-1;i>=0;i--){
o[i] = (int)(Math.random()*100+1);
}
// System.out.println("进行排序");
// for(int i=0;i<o.length;i++){
// o[i] = (int)(Math.random()*100+1);
// }
Arrays.sort(o);
System.out.println(Arrays.toString(o));//[19, 23, 36, 40, 66]-----将字符串的形式按数组的形式表现出来
int min = o[0];
return min;
}
}
package day06.homework;
import java.util.Arrays;
public class notes {
public static void main(String[] args) {
//1.数组的定义三种方式:声明并初始化
int[] a1 = new int[10];
int[] a2 = {1,4,7};
int[] a = new int[]{1,2,9,888,8,6,99};
//2.获取数组某下标的值。注意:下标超过数组长度会报数组下标越界异常(ArrayIndexOutOfBoundsException)
System.out.println(a[2]);//9
//3.判断数组的长度:
System.out.println(a.length);//5
//4.输出数组中最后一个元素的值
System.out.println(a[a.length-1]);
//5.(1)求数组的最大值:【Arrays.sort();】默认是按照从小到大排序的
Arrays.sort(a);
System.out.println(a[a.length-1]);//888
//5.(2)求数组的最大值:
int max = a[0];
for(int i=1;i<a.length;i++){
if(a[i]>max){
max = a[i];
}
}
System.out.println("最大值为:"+max);//最大值为:888
//拓展:给数组赋随机值,并求出最大值
for(int i=0;i<a.length;i++){
a[i] = (int)(Math.random()*100);//给数组的所有元素赋随机值
System.out.print(a[i]+" ");//5 63 74 1 57 63 68
}
int max2 = a[0];
for(int i=1;i<a.length;i++){
if(a[i]>max2){
max2 = a[i];
}
}
System.out.println("最大值为:"+max2);//最大值为:74
//6.升序排序:利用for循环遍历i++:
int[] b = new int[]{1,4,55,999,2,7};
Arrays.sort(b);
System.out.println("进行升序排序:");
for(int i=0;i<b.length;i++){
System.out.print(b[i]+" ");//1 2 4 7 55 999
}
System.out.println(" ");
//7.降序排序:利用遍历i--
int[] c = new int[]{1,4,55,999,2,7};
Arrays.sort(c);
System.out.println("进行降序排序:");
for(int i=c.length-1;i>=0;i--){
System.out.print(c[i]+" ");//999 55 7 4 2 1
}
System.out.println();
//8.数组复制之-----(1).arraycopy(a,1,b,0,4);
int[] d = {10,20,20,30,50,70};
int[] e = new int[8];
//d:源数组
//1:源数组的起始下标
//e:目标数组
//0:目标数组的起始下标
//4:要复制的元素个数
System.arraycopy(d,1,e,0,4);//灵活性好
for(int i=0;i<e.length;i++){
System.out.print(e[i]+" ");//20 20 30 50 0 0 0 0
}
System.out.println();
//8.数组的复制之-----(2)Arrays.copyOf(a,6);
int[] f = Arrays.copyOf(d,7);//灵活性差
//a:源数组
//b:目标数组
//7:目标数组的长度(元素个数)
//---若目标数组长度>源数组长度,则末尾补默认值
//---若目标数组长度<源数组长度,则将末尾的截掉
for(int i=0;i<f.length;i++){
System.out.print(f[i]+" ");//10,20,20,30,50,70,0
}
//9.通过数组本身的扩容,再利用【q[q.length-1] = max;】把数组的最大值放到最后一位
int[] q = new int[]{1,2,3,5};
q = Arrays.copyOf(q,q.length+1);//将数组q扩容并使其长度加1,即比原数组长一个元素,默认值为0
q[q.length-1] = max;//将上边的最大值max赋值给数组q的最后一个元素
for(int i =0;i<q.length;i++){
System.out.print(q[i]+" ");//1 2 3 5 888
}
}
}
(1)基本类型:
(2)引用类型数组练习题及内存图:
package oopday9.day03.StudentReference;
/**
* 基本数据类型(basic data type)默认值为————————0!
* //声明整形数组arr,包含3个元素,每个元素都是int类型,默认值为0
* int[] arr = new int[3];//基本数据类型
* arr[0] = 100;
*
*
* 引用数据类型(reference data type)数组的演示:————————默认值为null!
* //声明Student类型数组s,包含3个元素,每个元素都是Student型,默认值为null
* //Student是自己写的,就是引用数据类型:
* Student[] stus = new Student[3];//创建Student数组对象
*
*/
public class Student {
String name;
int age;
String address;
Student(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
public static void main(String[] args) {
int[] arr = new int[3];//基本数据类型
arr[0] = 100;
System.out.println(arr[0]);
/* 声明Student引用类型数组s,包含3个元素,每个元素都是Student型,默认值为null
Student是自己写的,就是引用数据类型: */
//1.创建Student数组对象
Student[] stus = new Student[3];
System.out.println(stus);//【@28d93b30】----------变量存着是地址!!!
/*
Student stus1 = new Student("张三",25,"河北");
stus[0] = stus1;
上两句同下:
*/
//2.创建Student对象(即给变量传参赋值的时候)-------注1:只要是引用类型,就得new一个构造方法来传参:Student();
stus[0] = new Student("张三",25,"河北");
stus[1] = new Student("李四",26,"佳木斯");
stus[2] = new Student("王五",27,"山东");
//注1:【引用类型直接访问数组的元素输出的是地址!】
System.out.println(stus[0]);//@28d93b30----------数组的元素存着是地址!!!
//(1)输出第一个学生的名字----------------------注2:【引用类型想访问数组的元素需要通过数组元素打点】
System.out.println(stus[0].name);//张三
//(2)修改第2个学生的年龄为24------------------注3:【若想访问具体元素的特有属性(name/age...)先取到想访问的元素,再去打点】
stus[1].age = 24;
//(3)第3个学生跟大家问好
stus[2].sayHi();
//(4)遍历所有学生
for(int i=0;i<stus.length;i++){
System.out.println(stus[i].name);//①输出每个学生的名字
stus[i].sayHi();//②跟所有学生问好
}
//内存过程:
//1.创建了一个Student数组对象:
// 【变量:即引用】保存在【栈中】并且【有自己的地址】-------------------------指向堆中的Student数组对象;
// 【Student数组对象保存在堆中】,在堆中就有了Student数组对象,【包含数组的所有元素(stus[0]/stus[1]...)】,引用数据类型默认值为null
//2.创建两个Student对象的同时在堆中又产生了两个,因为是引用类型,
// 所以在堆中元素保存的是地址【@28d93b30,即内存图中的0x2222】-------指向了Student对象
//3.当修改第2个学生的age为24时,又因为数组是连续的,基于地址就找到了Student对象(保存着是属性name/age...)中的三个参数!
}
}
(1)概念
方法其实是具有一定功能的代码块,我们可以把需要多次使用的功能提取成一个方法,这样多次使用的时候也不需要把这些重复的代码写多次造成代码的冗余。
(2)格式:
方法定义的格式五要素:修饰符 返回值类型 方法名(参数列表) { 方法体-----具体的业务逻辑功能实现 }
例: public static void main(String[] args) { }
注1:方法签名 = 方法名+(参数列表)
注2:如何确定我们要调用哪个方法呢? 方法名+参数列表
(3)注意事项
注意:我们这里说的是返回值类型(void)而不是返回值
,如果一个方法有返回值,那么返回值类型必须设置为与返回值相同的类型,并且返回值需要使用return关键字来返回。
(4)调用方法:
调用方法:
(1)无返回值:方法名(有参传参);
(2)有返回值:数据类型 变量 = 方法名(有参传参);
return的用法:
(1)return 值; //1)结束方法的执行 2)返回结果给调用方----用在有返回值方法中
(2)return; //1)结束方法的执行--------------------用在无返回值的方法中
package ooday6.day06;
/**
* 《方法的演示》:-----函数/过程
* 方法可以有参,也可以无参。(有参可以使方法更加灵活)
* :何时有参?何时无参?
* 1)若方法中的数据都可以写成固定/写死的---------无参
* 2)若方法中的数据不是固定的数据-----------有参
*
* 一、概念:
* 1.封装一段特定的业务逻辑功能
* 2.尽可能的独立,一个方法只干一件事
* 3.方法可以被反复多次调用
* 4.减少代码重复,有利于代码复用,有利于代码维护
*
* 二、定义方法:-----五要素
* 修饰词 返回值类型 方法名(参数列表) {
* 方法体--------------具体的业务逻辑功能实现
* }
*
* 三、调用方法:-----有、无返回值
* 1.无返回值:【方法名(有参传参);】
* return; //1)结束方法的执行------------------------(用在无返回值的方法中)
*
* //形参:形式参数,定义方法时的参数为形参 例:public static void sayHi(String name){...}
* //实参:实际参数,调用方法时的参数为实参 例:main里调用的方法中的参数----sayHi("张三");
*
*
* 2.有返回值:【数据类型 变量 = 方法名(有参传参);】
* return 值; //1)结束方法的执行 2)返回结果给调用方-----(用在有返回值方法中)
*
*/
public class MethodDemo {
/**-----无返回值:方法名(有参传参);-----*/
public static void main(String[] args) {
a();
say();
//sayHi();//编译错误,有参必须传参
//sayHi();//编译错误,参数类型必须匹配
sayHi("张三");//String name = "张三" -----实参(调用方法时的参数为实参)
sayHi("李四");//String name = "李四"
sayHello("王五",25);//string name = "王五",int age = 25
System.out.println("继续执行...");
}
public static void a(){
System.out.println(111);
b();
System.out.println(222);
}
public static void b(){
System.out.println(333);
}
//无参无返回值:
public static void say(){
System.out.println("大家好");
}
//有参无返回值(单个参数类型)
public static void sayHi(String name){//-----形参(定义方法时的参数为形参)
System.out.println("我叫"+name);
}
//有参无返回值(多个参数类型)
public static void sayHello(String name,int age){//-----形参
if(age>5){
return; //【return; 】://1)结束方法的执行 就不执行下边的话了
}
System.out.println("我叫"+name+",今年"+age+"岁了。");
}
}
package ooday6.day06;
import java.util.Arrays;
//有返回值
public class MethodDemo2 {
/**-----有返回值: 数据类型 变量 = 方法名(有参传参);-----*/
public static void main(String[] args) {
//这两句和下面一句意思相同:
// double a = getNum(); //getNum()为调用方
// System.out.println(a);//8.88
System.out.println(getNum());//8.88-----模拟对返回值的后续操作
//调用plus()第一种方式:
int b = plus(5,6);
System.out.println(b);//11-----模拟对返回值的后续操作
//System.out.println(plus(5,6));
//调用plus()第二种方式:
int m=5,n=6;
int c = plus(m,n);//传的是m和n里边的数
System.out.println(c);//11-----模拟对返回值的后续操作
int[] d = testArray();
System.out.println(d.length);//2-----模拟对返回值的后续操作
Arrays.sort(d);//对数组排序(默认为升序排列)
System.out.println(d[d.length-1]);//71 输出排序后的最大值
for(int i=0;i<d.length;i++){//遍历数组的每个元素
System.out.print(d[i]+" ");//21 71 输出每个元素的值-----模拟对返回值的后续操作
}
}
//无参有返回值
public static double getNum(){
//return ;//编译错误,return后必须跟一个数(这个数与double类型相同)
//return "abc";//编译错误,返回值类型必须匹配!
return 8.88;//1)结束方法的执行 2)返回结果给调用方(---getNum())
}
//有参有返回值:
public static int plus(int num1,int num2){
int num = num1+num2;
return num;//11------返回的是变量num的值
//return num1+num2;//返回的是变量num1与的num2的和
}
//无参有返回值
public static int[] testArray(){
int[] arr = new int[]{21,71};
// int[] arr = new int[2];
// for(int i=0;i<arr.length;i++){
// arr[i] = (int)(Math.random()*100);
// }
return arr;//【return 值;】: //1)结束方法的执行 2)返回结果给调用方
}
}
发生在父子类中,方法名相同,参数列表相同
重写方法被调用时,看对象的类型()------------这是规定,记住就OK
当派生类觉得超类的行为不够好时,可以重写。子类继承了父类以后,想要在不改变父类代码的情况下,实现功能的修改与拓展
(1)重写的规则:两同 两小 一大
两同
: 方法名与参数列表 和 父类方法保持一致两小
:一大
: 子类方法的修饰符权限 >= 父类方法的修饰符权限(2)重写的意义:是在不修改源码的前提下,进行功能的修改和拓展(OCP原则:面向修改关闭,面向拓展开放)
package oopday9.day04.override;
import oopday9.day04.Person;
import oopday9.day04.Student;
/**
* 《方法的重写(override/overriding)》
* 1、概念:重新写(当派生类觉得超类的行为不够好时,可以重写)
*
* 2、规定:
* -(1)发生在父子类中,方法名相同,参数列表相同
* -(2)重写方法被调用时,看对象的类型()
*
* 3、规则:两同 两小 一大
* 两同: 方法名与参数列表 和 父类方法保持一致
* 两小:
* 子类方法的返回值类型 <= 父类方法的返回值类型。
* 子类方法抛出的异常类型 <= 父类方法抛出的异常类型
* 注意1:这里的<=说的是继承关系,不是值的大小
* 注意2:如果父类方法的返回值类型是void,子类保持一致即可
* 注意3:子类不可以重写父类的私有方法,还是因为不可见
* 一大: 子类方法的修饰符权限 >= 父类方法的修饰符权限
*
* 4、意义:是在不修改源码的前提下,进行功能的修改和拓展(OCP原则:面向修改关闭,面向拓展开放)
*
*/
public class Restaurant {
void show(){
System.out.println("做中餐");
}
double test(){ return 0.0; }
Student say(){ return null; }
public static void main(String[] args) {
Aoo o = new Aoo();
//---------2、(2)重写方法被调用时,看【引用(o:即变量)new出来】对象的类型(Aoo)
//【超类中的doing()方法,只是为了能点出来。若超类和派生类都有该方法,则调用派生类的;若派生类中没有则调超类的】
o.doing("0"); //调用的是------>Boo的doing()
Boo o2 = new Boo();
o2.doing("1");//调用的是------>Boo的doing()
}
}
class Aoo extends Restaurant{
//(1)还是想做中餐,不需要重写
void show(){//(2)我想改做西餐----------------需要重写(只写西餐)
System.out.println("做西餐");
}
// void show(){//(3)想在中餐基础上+西餐--------需要重写(先super中餐,再加入西餐)
// super.doing();
// System.out.println("做西餐");
// }
//int show(){ return 5; } //编译错误,子类方法的返回值类型 <= 父类方法的返回值类型。
//int test(){ return 0; } //编译错误,基本数据类型此时不符合大小原则,必须为double
//Person say(){ return null; } //编译错误,在派生类中重写方法为引用类型时必须:<= 派生类
void doing(String r){
System.out.println("我是Aoo中自己写的方法");
}
}
class Boo extends Aoo{
void doing(String r){
System.out.println("我是Boo中重写Aoo的方法");
}
}
---------------------------------------更加方便用户的访问
(1)重载的规则:
同一个类中;出现多个方法名相同;但参数列表不同的方法
编译器在编译时会根据方法的签名自动绑定方法
(2)方法的传值:基本类型传递的是实际值,引用类型传递的是地址
(3)重载规则:
//重载的演示
public class OverloadDemo {
public static void main(String[] args) {
Aoo o = new Aoo();
o.show(); //编译器根据方法的签名自动绑定方法
o.show("zhangsan");
o.show(25);
o.show("zhangsan",25);
o.show(25,"zhangsan");
}
}
class Aoo{
void show(){}
void show(String name){}
void show(int age){}
void show(String name,int age){}
void show(int age,String name){}
//int show(){ return 1;} //编译错误,重载与返回值类型无关
//void show(String address){} //编译错误,重载与参数名称无关
}
(4)重载的意义: 是为了方便外界对方法进行调用,什么样的参数程序都可以找到对应的方法来执行,体现的是程序的灵活性。
/重写:发生在父子类中,方法名相同,参数列表相同
/重载:发生在同一类中,方法名相同,参数列表不同
重写与重载的区别:重点(常见的面试题)
&&重写:发生在父子类中,方法名相同,参数列表相同;一般用于在派生类中修改超类的方法
规定:重写方法被调用时,看对象的类型(例:超类是void do(){} 当超类中重写方法被调用时,派生类中必须写成和超类一样:super.do();)
原则:两同,两小,一大
两同:签名(方法名和参数)相同
两小:①派生类方法的返回值类型必须 <= 超类方法;②派生类方法抛出的异常必须<=超类方法;
一大:派生类的访问权限必须 >= 超类方法
&&重载:发生在同一类中,方法名相同,参数列表不同;是完全不同的方法,只是方法名相同而已,对于返回值类型并没有要求。
(总结):
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
(1)练习题1:求数字阶乘(递归解法版)
需求:接收用户输入的数字,计算该数字的阶乘结果
已知:负数不可以有阶乘,0的阶乘结果是1,
5 ! = 5 x 4 x 3 x 2 x 1
package cn.cxy.design;
//需求:求用户输入数字的阶乘结果
//f(int n)--用来求阶乘
//规律:
//f(n)= n*f(n-1)
//f(5)= 5*4*3*2*1 = 5*f(4)
//f(4)= 4*3*2*1 = 4*f(3)
//f(3)= 3*2*1 = 3*f(2)
//f(2)= 2*1 = 2*f(1)
//f(1)= 1
//
//5!=5*4*3*2*1=120
//4!=4*3*2*1
//3!=3*2*1
//2!=2*1
//1!=1
public class TestRecursion {
public static void main(String[] args) {
int result = f(15);//调用f()用来求阶乘
System.out.println(result);
}
/**递归的两要素 1.总结规律 2.最简问题*/
public static int f(int n) {
if(n == 1) {//最简问题
return 1;
}else {//其他情况 n*f(n-1)
//递归:再方法内部自己调用自己
return n*f(n-1);
}
}
}
(2)练习题2:递归求目录总大小
需求:递归求目录的总大小 D:\ready,步骤分析如下:
1.列出文件夹中的所有资源–listFiles()–>File[]
2.判断,当前资源是文件还是文件夹–文件夹大小为0,文件大小需要累加
–是文件,求文件的字节量大小length(),累加就行
–是文件夹,继续列出文件夹下的所有资源–listFiles()–>File[]
–判断,是文件,求文件的字节量大小length(),累加就行
–判断,是文件夹,再一次列出文件夹下的所有资源
–…重复操作
也就是说,规律就是:只要是文件夹,就需要重复步骤1 2
package cn.cxy.file;
import java.io.File;
/**本类用来递归求目录总大小*/
public class FileSumRecursion {
public static void main(String[] args) {
//1.指定要求哪个目录的总大小
/**注意:此处指定的目录必须是真实存在的
* 如果传一个不存在的文件夹会报错,如果是传了一个空文件夹,大小为0*/
File file = new File("D:\\ready");
//2.调用size()求目录大小
long total = size(file);
//3.接收结果并打印
System.out.println("文件夹的总大小为:"+total);
}
private static long size(File file) {
//1.列出文件夹中的所有资源--listFiles()-->File[]
File[] fs = file.listFiles();
//2.遍历数组,获取每file对象
//2.1定义变量,记录总和
long sum = 0;
for(int i = 0;i < fs.length ; i++) {
//2.2通过下标操作当前遍历到的资源
File f = fs[i];
//2.3判断,当前资源是文件还是文件夹--文件夹大小为0,文件大小需要累加
if(f.isFile()) {
//--是文件,求文件的字节量大小length(),累加就行
sum += f.length();//相当于:sum = sum + f.length();
}else if(f.isDirectory()) {
//--是文件夹,继续列出文件夹下的所有资源,1 2步骤--listFiles()-->File[]
/**方法的递归,递归现象,就是在方法的内部调用方法自身*/
sum += size(f);
}
}
return sum;//把sum记录的值返回调用位置
}
}
(3)练习题3:递归删除文件夹
需求:递归删除文件夹 D:\ready\a
1.列出文件夹下的所有资源listFiles()
2.判断,当前资源是文件还是文件夹
–判断,是文件,直接删除delete()
–判断,是文件夹,继续重复操作1 2
具体思路可以分为这么几步:
1.首先,我们需要指定一个根目录作为要删除的对象
2.列出文件夹下的所有资源listFiles(),并进行遍历
3.判断当前的资源,如果是文件,直接删除;如果是文件夹,则执行步骤2
4.将文件夹中的内容删除完毕后,删除文件夹本身
package cn.tedu.file;
import java.io.File;
/**本类用于递归删除目录*/
public class TestFileDeleteRecursion {
public static void main(String[] args) {
//1.指定要删除的目录
/**为了更好的测试,注意指定的目录是已存在的目录,但是,千万不要删盘符!!!!*/
/*我们也有一些没有权限的文件夹,那个是无法访问且不能删除的哦*/
File file = new File("D:\\ready\\a");
//2.调用删除目录的方法
boolean result = del(file);
//3.打印删除的结果
System.out.println("删除的结果为:"+result);
}
public static boolean del(File file) {//完成的同学不是很多,抓紧时间写,写完截图发群里哈,这首歌结束我们继续
//1.列出文件夹下的所有资源
File[] fs = file.listFiles();
//2.循环遍历拿到的所有资源
for (int i = 0; i < fs.length; i++) {
//2.1获取本次循环遍历到的file对象
File f = fs[i];
//3.判断,当前资源是文件还是文件夹
if(f.isFile()) {
f.delete();//是文件,直接删除
System.out.println(file.getName()+"文件删除成功!");
}else if(f.isDirectory()) {
//是文件夹,需要继续进行步骤1 2 ,出现了重复调用的情况
//递归,在方法的内部调用自己
del(f);
}
}
//位置:在for循环执行之外删除文件夹
file.delete();//空文件夹直接删除
System.out.println(file.getName()+"文件夹删除成功!");
return true;
}
}
-----------------------------封装的意义:隐藏一些东西,暴露一些东西,来保护数据的安全
-数据(成员变量)私有化private,行为(方法)公开化
//访问权限范围:
package ooday04;
//演示访问控制修饰符
public class Aoo {
public int a; //任何类
protected int b; //本类、派生类、同包类
int c; //本类、同包类
private int d; //本类
void show(){
a = 1;
b = 2;
c = 3;
d = 4;
}
}
class Boo{ //---------------演示private
void show(){
Aoo o = new Aoo();
o.a = 1;
o.b = 2;
o.c = 3;
//o.d = 4; //编译错误
}
}
package ooday04_vis;
import ooday04.Aoo;
public class Coo { //演示同包的概念
void show(){
Aoo o = new Aoo();
o.a = 1;
//o.b = 2; //编译错误
//o.c = 3; //编译错误
//o.d = 4; //编译错误
}
}
//跨包继承,需要导包
class Doo extends Aoo{ //演示protected
void show(){
a = 1;
b = 2;
//c = 3; //编译错误
//d = 4; //编译错误
}
}
(8)练习题:方法调用的顺序
package cn.tedu.basic;
/*本类用于测试方法的调用*/
public class MethodDemo {
//1.创建程序的入口函数main()
public static void main(String[] args) {
System.out.println("main() start...");
m1();
System.out.println("main() stop...");
int a =5;
System.out.println(a);
}
//2.创建m1()
private static void m1() {
System.out.println("m1() start...");
m2();
System.out.println("m1() stop...");
}
private static void m2() {
System.out.println("m2() start...");
}
}
(1)两者都是一种编程的思想
(2)面向对象强调的是事情的结果,我们通过对象完成对应的功能
(3)面向过程强调的是事情的过程,我们做任何事情,都要亲力亲为,经过每一个步骤
(4)Java是一门面向对象的语言
现实生活中是由很多很多对象组成的,基于对象抽出了类
。是自己创建的一种引用数据类型。
(1)类的概述
①类:类别/类型,代表一类个体;
②定义类通过关键字class来定义,类是对象的模子
④一个类可以创建出多个对象,
类中可以包含:
对象的属性/特征-----------------------成员变量
对象的行为/动作-----------------------方法
(2对象的概述
①对象:软件中真实存在的单个个体/东西
②对象是类的具体的实例
③对象之间是相互独立的,互不影响。我们把创建对象也称作“实例化”
④创建对象通过new关键字触发构造函数生成,对象是根据类创建出来的具体的内容
(1)前提:对象是根据类的设定来创建的,目前我们可以在类中添加很多的元素:
属性 方法 静态方法 构造代码块 静态代码块 局部代码块 构造方法…所以不限制类里具体写什么,取决于业务
(2)对象创建的过程:Person p = new Person();
Person p = new Person();//短短这行代码发生了很多事情
1.把Person.class文件加载进内存
2.在栈内存中,开辟空间,存放引用变量p
3.在堆内存中,开辟空间,存放Person对象
4.对成员变量进行默认的初始化,比如对应的属性都有自己的对应类型的默认值
5.对成员变量进行显示初始化,对象创建完毕后,会生成一个唯一的地址值用于区分不同的对象
6.执行构造方法(如果有构造代码块,就先执行构造代码块再执行构造方法)
7.堆内存完成
8.把堆内存的地址值赋值给引用类型变量p来保存 ,p 引用了Person对象的地址值
9.后续如果想要使用这个类的功能,可以从引用类型变量中保存的地址值找到对应的对象做进一步的操作
(3)匿名对象:new Phone();
匿名对象是没有名字的对象,所以创建过程:
练习题:
//:如何创建类?如何创建对象?如何访问成员?
public class Student { //Student类就是我们自己造的一种引用类型
//成员变量
String name;
int age;
String address;
//方法
void study(){
System.out.println(name+"在学习...");
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
}
public class StudentTest {
public static void main(String[] args){
//创建一个学生对象
Student zs = new Student();
//给成员变量赋值
zs.name = "zhangsan";
zs.age = 25;
zs.address = "河北廊坊";
//调用方法
zs.study();
zs.sayHi();
Student ls = new Student();
ls.name = "lisi";
ls.age = 24;
ls.address = "黑龙江佳木斯";
ls.study();
ls.sayHi();
//1)创建了一个学生对象
//2)给所有成员变量赋默认值
Student ww = new Student();
ww.study();
ww.sayHi();
}
}
(1)前提:
为了保证数据的安全,也为了程序的使用者能够按照我们预先设计好的方式来使用资源
(2)体现在哪三个方面?:
类:封装对象的属性(即成员变量)和行为(即方法)
方法:封装的是具体的业务逻辑实现
访问控制修饰符:封装的是具体的访问权限
(1)继承的概述
–继承意味着代码虽然我没有写,但也属于我,只是没有写在一起而已
–泛化:将共有的抽出来的过程,泛化是设计层面的概念,从代码实现层面来说咱们就是继承,泛化就是继承
package oopday9.day03.extends1;
/**
* 继承:------------------extends关键字
*
* 继承格式:
* class 父类 {
* }
*
* class 子类 extends 父类 {
* }
*
* 继承特点:
* (1)作用:代码复用
* (2)通过extends来实现继承
* (3)超类/父类:共有的属性和行为
* (4)派生类/子类:特有的属性和行为
* (5)派生类既能访问自己的,也能访问超类的,但超类不能访问派生类的
* (6)一个超类可以有多个派生类;一个派生类只能有一个超类-----------单一继承
* (7)具有传递性
* (8)java规定:构造派生类之前必须先构造超类
*
*/
public class Person {//--------可以访问a------(6)一个超类可以有多个派生类;一个派生类只能有一个超类
int a = 5;
int b = 6;
Person(){
System.out.println("我是超类构造");
}
void eat(){ }
public static void main(String[] args) {
Father f = new Father();
f.eat();
}
}
class Father extends Person{//-可以访问b+a
int b;
Father() { }
void eat(){//------方法的重写
this.a = 6;//因为继承了超类,this默认超类的
super.b = 7;//访问超类的成员变量
}
}
class Boy extends Father{//---可以访问b+a+c---(7)具有传递性
int c;
void eat(String name){//-----派生类自己的方法
}
}
0.继承关键字extends格式: 子类extends父类
1.接口:---------------------------------------------------------部分派生类所共有的属性和行为
2.继承相当于子类把父类的功能复制了一份,包括私有资源-------------------代码复用
3.Java只支持单一继承:一个子类只能有一个父类,一个父类可以有多个子类-----单一继承
4.继承具有传递性:爷爷的功能会传给爸爸,爸爸的功能会传给孙子------------多接口实现
5.子类只可以使用父类的非私有资源,私有资源不可用的原因是不可见
6.子类可以拥有自己的特有功能,父不可访问子类-------------------------派生类所特有的属性和行为
7.继承是is a 强耦合的关系,依赖性非常强,比如我们看到”熊孩子”,就知道他有一个”熊父母”
注:一个超类可以有多个派生类;派生类既能访问自己的,也能访问超类的;但超类不能访问派生类的
(1)继承相当于子类把父类的功能复制了一份,包括私有资源
注意1:虽然私有资源继承了,但是私有资源不可用,原因是被private限制了访问,私有资源只能在本类使用
注意2:构造方法不能继承,原因是:构造方法要求名字是本类的类名,我们不能在子类中出现父类名字的构造方法
(2)继承是可以传递的
:爷爷的功能会传给爸爸,爸爸的功能会传给孙子
注意:爸爸从爷爷那里继承的功能,也会一起传给孙子
(3)Java的类是单一继承的
:一个子类只能有一个父类,但是一个父类可以有多个子类
(4)子类在继承了父类以后,如果对父类的功能不满意
-可以在不修改父类功能的【满足OCP原则】前提下,在子类中,重写继承过来的这个方法
-重写需要满足的规则:两同 两小 一大,我们可以在重写的方法上加@Override注解验证是否写对
(5)继承是一种is a的关系,强耦合,关联性特别强,而且类的继承机会只有一次,要谨慎使用
(6)子类可以直接使用父类的所有非私有资源
java规定:构造派生类之前必须先构造超类
//1.派生类的构造方法中若没有调用超类的构造方法,则默认super()调用超类的无参构造方法
//2.派生类的构造方法中若自己调用了超类的构造方法,则不再默认提供
----------super()调用超类构造方法,必须位于派生类构造方法的第一行
补充:
super:指代当前对象的超类对象
super的用法:
super.成员变量名-----------------------访问超类的成员变量(了解)
super.方法名()------------------------调用超类的方法
super()------------------------------调用超类的构造方法
(1)多态的意义:
如上练习题:
例1:
①对象的多态:同一个对象被造型为不同的类型时,有不同的功能
例:我、你、水…————所有对象都是多态的(明天体会)
我 me = new 我();
讲师 o1 = me;
孩子他妈 o2 = me;
老公的老婆 o3 = me;
o1.授课;
o2.做饭;
o3.收工资;
——————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————
例2:
②行为(方法)的多态:同一类型的引用指向不同对象时,有不同的实现
例:cut()、move()、getImage()、getScore()————所有的抽象方法都是多态的
人 p1 = new 理发师();
人 p2 = new 外科医生();
(2)多态的要求:继承 + 重写
(3)多态的口诀1:
(4)多态的口诀2:
编译看左边,运行看右边:
父类中定义的功能,子类才能使用,否则报错
多态中,方法的定义看的是父类的,方法的实现看的是子类重写后的功能
(5)多态中资源的使用:
1)成员变量:使用的是父类的
2)成员方法:对于方法的定义看的都是父类的,对于方法实现,重写后使用的是子类的
3)静态资源:静态资源属于类资源,不存在重写的概念,在哪个类中定义的,就属于哪个类
向上转型 和 向下转型的由来:
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展:那么在这个过程中就存在着多态的应用-------有两种转型方式
:
(1)向上造型(即自动类型转换):Father f = new Son();
①超类型的引用(变量)指向了派生类的对象------------------------例:Animal o = new Tiger();
②引用能"点" 出来什么,看引用(o:即变量)本身的类型----------例:【o.Animal类中的东西】
③能造型成为的数据类型只有2种:超类 和 所实现的接口
------超类型的引用指向了派生类的对象------
1.何时向上造型???
---当多重角色能干的事是一样的,可以将多种角色造型到超类型数组中,统一访问。
2.向上造型的意义:--------实现代码复用
当多种角色能干的事是一样的,可以将那多种角色造型到超类数组中,统一访问
如下例题:
package oopday9.day04.upload;
public class Aoo {
int a;
void show(){
}
Aoo(){
System.out.println("我是超类");
}
}
package oopday9.day04.upload;
public class Boo extends Aoo{//还默认包含Aoo的a和show()
int b;
void test(){
}
Boo(){
System.out.println("我是派生类");
}
}
package oopday9.day04.upload;
//向上造型的演示
public class UploadDemo {
public static void main(String[] args) {
Aoo o1 = new Aoo();
o1.a = 1;
o1.show();
// o1.b; //---------编译错误,超类的引用不能【点】出派生类的,能点出来什么,看引用的类型
// o1.test();
Boo o2 = new Boo();
o1.a = 1;//---------正确。派生类的引用可以【点】出超类的
o1.show();
o2.b = 2;
o2.test();
Aoo o3 = new Boo();//!向上造型 一个超类型的引用(o3)指向了派生类
o3.a = 3;
o3.show();
// o3.b = 4; //------编译错误 超类的引用(o3)不能点出派生类的,能点出来什么,看引用的类型
// o3.test();//编译错误,能点出来什么,看引用的类型
}
}
(1)引用类型强制类型转换成功的两个条件↓,不符合这俩条件否则发生ClassCastException类型转换异常。
①引用所指向的对象,就是该类型
②引用所指向的对象,实现了该接口或继承了该类
③建议:在强转之前先通过instanceof来判断引用的对象是否是该类型
注1:基本类型强转不会报异常
注2:何时需要强转?
--想访问的属性 或 行为在超类中没有,必须强转,强转之前先instanceof判断
注3:为什么有向下造型:?
--之前被看作是父类类型的子类对象,想使用子类的特有功能,那就需要向下造型
如下例题:
package oopday9.day09;
/**
* 引用类型《强制类型转换》成功的两个条件↓,不符合这俩条件否则发生ClassCastException类型转换异常
* ①引用所指向的对象,就是该类型
* ②引用所指向的对象,实现了该接口或继承了该类
* ③建议:在强转之前先通过instanceof来:判断引用的对象是否是该类型
*
* 注1:基本类型强转不会报异常
*/
public class MuItiTypeDemo {
public static void main(String[] args) {
Aoo o = new Boo();//向上造型 自动类型转换
// Boo o1 = o;//编译错误(只检查语法),o是超类,o1是派生类,大转小---所以为强转(前边必须加小括号并且符合强转的两种条件)
Boo o1 = (Boo) o;//正确。因为符合条件①:---------①引用(o1)所指向的对象(new Boo),就是该类型(Boo).
Inter o2 = (Inter) o;//正确。因为符合条件②:-----②引用(o2)所指向的对象(new Boo),实现了该接口(Inter).
// Coo o3 = (Coo) o;//不会编译错误,但是运行时发生:ClassCastException
/** ③建议:在强转之前先通过instanceof来:判断引用的对象是否是该类型 */
if(o instanceof Boo){ //先判断:为true,
Boo o4 = (Boo) o;
System.out.println("o是Boo类型");//输出
}else{
System.out.println("o不是Boo类型");
}
if(o instanceof Inter){ //先判断,为true
Inter o5 = (Inter) o;
System.out.println("o是Inter接口");//输出
}else{
System.out.println("o不是Inter接口");
}
if(o instanceof Coo){ //false,先判断
Coo o6 = (Coo)o; //再强转
System.out.println("o是Coo类型");
}else{
System.out.println("o不是Coo类型");
}
}
}
interface Inter{ //接口权限是默认public的,但是java规定:在一个.java文件中,只能有一个类写public
}
class Aoo{
}
class Boo extends Aoo implements Inter{
}
class Coo extends Aoo{
}
——————————————————————————————————————————————————————————————————
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
——————————————————————————————————————————————————————————————————
强制类型转换例题:
package apiday;
public class Test {
public static void main(String[] args) {
IC c1=new C();//向上造型 自动类型转换
/* 通过instanceof来:判断引用的对象是否是该类型 */
System.out.println(c1 instanceof C);
System.out.println(c1.M);
/*
调用"N"时会报错,原因:
引用 c1 是 IC型的超类型,
在继承中规定,子类中是特有的属性和行为,所以超类不能访问派生类的
若想超类访问子类的,需要做强转,即在 c1的前面加一个:(C) 转为C类型的子类型才可以!
即:((C) c1).N
*/
//System.out.println(c1.N);//调用"N"时会报错
System.out.println(((C) c1).N);
}
}
interface IC{
int M = 10;
}
class C implements IC{
int M = 20;
int N = 30;
}
(1)特点:
1.格式:修饰符 类名就是方法名(){} 。注意!!!--------与类同名,没有返回值类型(连void都没有)
2.作用:给成员变量赋初始值(用于创建对象,每次new对象时,都会触发对应的构造函数,new几次,触发几次)
3.在创建(new)对象时被自动调用
4.若自己不写构造方法,则编译器默认提供一个无参构造方法,若自己写了构造方法,则不再默认提供
5.构造方法可以重载:---------可以是无参构造 含参构造 全参构造【创建对象+给所有的属性赋值】
6.构造方法不能继承,原因是:构造方法要求名字是本类的类名,我们不能在子类中出现父类名字的构造方法
7.构造方法也属于类中成员,可以用4种访问修饰符来修饰!!!!
练习题:
package oopday9.day02;
/** 学生类 */
//基于对象(在StudentTest类中new的对象Student)抽出的类不需要main方法
public class Student {
String name;//默认值为null--------------------------------------成员变量(在整个类中):name、age、address
int age;//默认值为0
String address;
// //若自己不写构造方法,则编译器默认提供下面这个无参构造方法:-----------与类同名,没有返回值类型(连void都没有)
// //若写了(即下面带参数的构造),则系统不再提供
// //在创建(new)对象时被自动调用
// Student(){
// }
//构造方法可以重载:------------构造方法也属于类中成员,可以用4种访问修饰符来修饰!!!!
Student(String name){
this.sayHi();//------------我们可以在本类构造函数的第一行使用this();
//调用本类的无参构造 / 使用this(参数);
//构造函数的调用只有这一种方式,或者创建对象时被动触发,不能在外面自己主动调用
}
//若自己写了构造方法,则不再默认提供上面那个无参构造:(此时StudentTest类中会有错误,
// -即new的对象Student()→括号会爆红,因为自己写的是有参构造,java规则是有参必须传参!)
Student(String name, int age, String address){//----------局部变量(当前方法中):name、age、address
this.name = name;//成员变量(this.name)与局部变量(name)同名时,若想访问成员变量则this不能省略
this.age = age;//局部变量没有默认值!!!
this.address = address;
}
//方法
void study(){
System.out.println(name+"在学习...");
}
void sayHi(){
System.out.println("大家好我叫"+name+",今年"+age+"岁了,家住"+address);
}
}
package oopday9.day02;
/** 学生测试类-----构造(Constructor)方法 */
public class ConstructorDemo {
public static void main(String[] args) {
//Student zs = new Student();//编译错误,学生类没有无参构造方法(因为自己写了构造方法,系统则不默认提供无参构造,所以报错)
//1.创建一个学生对象:
Student zs = new Student("张三",25,"河北");//new Student()是对象
//2.调用方法
zs.study();
zs.sayHi();
}
}
(2)注意事项:
1.每个类默认存在一个无参构造
一旦提供了其他的构造函数,默认的无参构造会被覆盖掉
所以如果想不传参数创建对象,就需要手动提供本类的无参构造
2.构造函数是要创建对象时被触发的,要创建几个对象,构造函数就会被触发几次
具体触发的是哪个构造函数,取决于调用时的参数
构造函数执行完毕,对象就创建成功了
3.无参构造–没有参数的构造方法
含参构造–包含参数的构造方法,这个参数比较自由,自己决定就好
全参构造–构造方法的参数与本类的所有属性一致,全参构造除了可以创建对象,还可以给对象的所有属性赋值
4.构造函数是被动触发的,不是我们像普通方法那样主动调用的
5.全参构造里必须添加给属性赋值的语句,如果不写,即使传入了参数值,属性也不会被赋值
6.一个类中默认存在无参构造,如果这个构造不被覆盖的话,我们可以不传参数,直接创建这个类的对象
如果这个类中提供了其他的构造函数,默认的无参构造会被覆盖,所以记得手动添加无参构
7.构造方法也存在重载的现象:无参构造 含参构造 全参构造【创建对象+给所有的属性赋值】
创建包: cn.tedu.oop
创建类: TestConstructor.java
package cn.tedu.oop;
/*本类用于测试构造方法*/
public class TestConstructor {
public static void main(String[] args) {
//4.创建Perosn类的对象进行测试
/*1.每次new(实例化)对象时,都会自动触发对应类的构造方法*/
/*2.每一个类都会默认存在一个没有参数的构造方法
* 但是,如果你提供了其他的构造函数,默认的无参构造会被覆盖
* 所以,我们要手动的提供无参构造,这样才能不传参数,也能创建对象*/
Person p = new Person();
System.out.println(p.age);
System.out.println(p.name);
System.out.println(p.address);
p.eat();
//6.创建Person类的第二个对象
/*4.每次创建对象时,都会执行构造方法
* 构造方法的作用:用来创建对象的*/
Person p2 = new Person();
//8.触发含参构造
Person p3 = new Person(88);
System.out.println(p3.name);
Person p4 = new Person("张三",18,"柳树下");
System.out.println(p4.name);
System.out.println(p4.age);
System.out.println(p4.address);
}
}
//1.创建Person类,用来描述人这一类事物
class Person{
//2.属性--成员变量
String name;//姓名
int age;//年龄
String address;//住址
//8.创建本类的全参构造
//右键->Generate->Constructor->Shift全选所有属性->OK
public Person(String name, int age, String address) {
this.name = name;//局部变量name的值赋值给this指定的成员变量name
this.age = age;//局部变量age的值赋值给this指定的成员变量age
this.address = address;//局部变量address的值赋值给this指定的成员变量address
System.out.println("我是Person类的全参构造");
}
/*3.构造方法的格式:没有返回值类型并且与类名同名的方法*/
//5.创建本类的无参构造
public Person(){
System.out.println("我是Person类的无参构造~");
}
//7.创建本类的含参构造---------构造方法可以重载
public Person(int n){
System.out.println("我是Person类的含参构造"+n);
}
//3.功能--方法
public void eat(){
System.out.println("干饭不积极,思想有问题~");
}
}
(1)形式:
{ 代码… }
(2)构造代码块的特点
1.位置: 在类的内部,在方法的外部
2.作用: 用于抽取构造方法中的共性代码
3.执行时机: 每次调用构造方法前都会调用构造代码块。
注意事项: 创建对象时执行,创建几次,执行几次,并且构造代码块优先于构造方法加载
(3)局部代码块的特点
1.位置: 在方法里面的代码块
2.作用:用于限制变量的作用范围,出了局部代码块(即花括号)就失效
3.执行时机:当其所处的方法被调用时才会执行
注意事项: 变量的作用范围越小越好,成员变量会存在线程安全的问题
(4)静态代码块 static { }
1.位置:类里方法外
2.作用:一般用来加载那些只需要加载一次并且第一时间就需要加载资源,称作:初始化
3.执行时机:随着类的加载而加载,最先加载到内存,优先于对象进行加载,直到类小消失,它才会消失
(5)三种代码块的比较
构造代码块:在创建对象时会自动调用,每次创建对象都会被调用,提取构造共性
局部代码块:方法里的代码块,限制局部变量的范围
静态代码块:在类加载时就加载,并且只被加载一次,一般用于项目的初始化
程序的执行过程(若都有的话):如下先执 行1).....
1)超类的static块
2)派生类的static块
3)超类的构造方法【对象创建成功】
4)派生类的构造方法
5)普通方法【当普通方法里有局部代码块,局部代码块才会执行】
(4)练习:测试几种代码块的加载顺序
创建包: cn.tedu.oop
创建类: TestBlock.java
package cn.tedu.oopstatic;
/*本类用于学习静态代码块*/
/*
执行顺序:
静态代码块->构造代码块->构造方法【对象创建成功】->普通方法->局部代码块
1.当创建对象时,会触发构造函数
2.创建对象时,也会触发构造代码块,并且构造代码块优先于构造方法执行
3.我们创建好对象后才能通过对象调用普通方法
4.如果普通方法里有局部代码块,才会触发对应的局部代码块
*/
public class TestStaticBlock {
public static void main(String[] args) {
//6.创建对象进行测试
Person p = new Person();
Person p2 = new Person();
//7.触发局部代码块
p.play();
}
}
//1.创建Person类
class Person{
//8.创建静态代码块 static{}
/*位置:类里方法外
* 执行时机:静态代码块也属于静态资源,随着类的加载而加载,优先于对象加载
* 并且静态资源只会加载一次
* 作用:用于加载那些需要第一时间就加载,并且只加载一次的资源*/
static{
System.out.println("我是静态代码块");
}
//2.创建构造代码块 {}
/*位置:类里方法外
执行时机:每次创建对象时被触发,并且优先于构造方法执行
作用:用于提取所有构造方法的共性功能*/
{
System.out.println("我是构造代码块");
}
//5.创建构造方法
public Person(){
System.out.println("我是无参构造");
}
//3.创建普通方法
public void play(){
System.out.println("我是一个普通方法");
//4.创建局部代码块 {}
/*位置:方法里
* 执行时机:执行本局部代码块所在的方法时才会执行
* 作用:用于限制变量的作用范围*/
{
System.out.println("我是一个局部代码块~");
}
}
}
(1)概念:
(2)形式:
(3)this的3个用法:
(4)练习如上用法之:1)变量名相同时使用------this.成员变量
只能用在方法中,方法中访问成员变量之前默认有个 【this.】
package oopday9.day02;
/** 学生类 */
//基于对象(在StudentTest类中new的对象Student)抽出的类不需要main方法
public class Student {
String name;//默认值为null-----------------//成员变量(在整个类中):name、age、address
int age;//默认值为0
String address;
//方法
void study(){
System.out.println(name+"在学习...");
}
void sayHi(){
System.out.println("大家好我叫"+name+",今年"+age+"岁了,家住"+address);
}
//构造方法可以重载:
Student(String name){
this.sayHi();//------------我们可以在本类构造函数的第一行使用this();
//调用本类的无参构造 / 使用this(参数);
//构造函数的调用只有这一种方式,或者创建对象时被动触发,不能在外面自己主动调用
}
//构造方法可以重载:
Student(String name, int age, String address){//----------局部变量(当前方法中):name、age、address
//使用的时候默认采取的是就近原则(第二个name):
this.name = name;//成员变量(this.name)与局部变量(name)同名时,若想访问成员变量则this不能省略
}
}
(5)练习如上用法之:3)构造方法间的调用--------this()
我们可以在本类构造函数的第一行使用this();
调用本类的无参构造 / 使用this(参数);
调用本类对应参数的构造方法
构造函数的调用只有这一种方式,或者创建对象时被动触发,不能在外面自己主动调用
构造函数直接不能互相调用,否则会死循环
package cn.tedu.oop;
/*本类用于测试this的用法2*/
public class TestThis2 {
public static void main(String[] args) {
//3.1触发无参构造创建本类对象
Dog d1 = new Dog();
//3.2触发含参构造创建本类对象
//Dog d2 = new Dog("旺财");
}
}
//1.创建小狗类
class Dog{
//2.1创建本类的无参构造
public Dog(){
/*在无参构造中,调用含参构造的功能
* 注意:调用是单向的,不能来回双向调用,否则会死循环*/
this("小旺旺");
System.out.println("无参构造");
}
//2.2创建本类的含参构造String s
public Dog(String s){
/*在含参构造中,调用无参构造的功能
* 规定:this关键字必须在构造函数的第1行*/
//this();
System.out.println("含参构造"+s);
}
}
(6)隐式对象:
隐式对象:
this:当前对象
super:当前对象的超类对象
外部类名.this:指代当前对象的外部类对象-----在成员内部类中有应用
(1)必须记住的,API中会用的:
外部类名.this:指代当前对象的外部类对象!!!
匿名内部类不能修改外面变量的值,因为在此处默认为final的
super:指代当前对象的超类对象
super的用法:当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
super.成员变量名----------------------访问超类的成员变量(了解)
super.方法名()------------------------调用超类的方法
super()------------------------------调用超类的构造方法
综合练习题:---------!!!!!!!!
package oopday9.day03;
/**
*《 super的演示》:
* super----------指代当前对象的超类对象
* super的用法:
* (1)super.成员变量名----------------------访问超类的成员变量(了解)
* (2)super.方法名()------------------------调用超类的方法
* (3)super()------------------------------调用超类的构造方法
*
* java规定:构造派生类之前必须先构造超类:
* -派生类的构造方法中若没有调用超类的构造方法,则默认super()调用超类的无参构造方法
* -派生类的构造方法中若自己调用了超类的构造方法,则不再默认提供
* -super()调用超类构造方法,必须位于派生类构造方法的第一行
*
*
* 例:下面程序中,为什么会先输出【超类构造方法】,再输出【派生类构造方法】?
* 原因:在main中,new了一个Boo对象,所以先调用Boo的无参构造,在Boo无参构造中:
* 若不写super有参构造,则在Boo的无参构造里第一行默认有【super();】无参构造,
* 此时会调用超类Aoo的无参构造先输出:【超类构造方法】,再输出:【派生类构造方法】
*/
public class SuperDemo {
int c = 5;
SuperDemo(int a){ }
public static void main(String[] args) {
Boo o = new Boo();
int a = 9;
int b = 10;
}
}
class Aoo extends SuperDemo{
Aoo(){
/* ↓↓↓↓↓↓↓重要重要重要重要重要重要重要重要↓↓↓↓↓↓↓↓ */
/** 若超类中为有参构造【 SuperDemo(int a){ } 】,
则在派生类的构造中必须写:【super(3);】来调用超类的有参构造,
且位于第一行! 否则会报错!
但不会影响Boo报错,因为Boo默认调用的Aoo的无参构造---Boo是间接继承了SuperDemo,! */
super(3);//-------------------------1.必须位于第一行!!
super.c = 15;//super.成员变量名------2.访问超类的成员变量
System.out.println("我是超类构造方法");
}
void show(){
System.out.println("我是超类自己写的方法");
}
}
class Boo extends Aoo{
Boo(){
//super()调用超类构造方法,必须位于派生类构造方法的第一行:
super();//即使没有这句话,但系统也默认用:【super();】调用的超类的无参构造方法!!
super.show();//super.方法名()---------3.调用超类的方法
System.out.println("我是派生类构造方法");
//super();//编译错误,-----super()-----4.调用超类构造方法,必须位于派生类构造方法的第一行!!
}
}
——————————————————————————————————————————————————————————————————
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
——————————————————————————————————————————————————————————————————
例题:
package apiday;
public class Test {
public static void main(String[] args) {
A a=new B(20);
System.out.println(a.a);
}
}
class A{
int a=10;
public A(int a){
this.a=a;
}
}
class B extends A{
int a;
public B(int a){//若不写【super()】,此处会报错:'apiday.A'中没有可用的默认构造函数
/*
解决【public B(int a)】报错问题:
若超类中为有参构造,
则在派生类的构造中必须写:【super(有参);】来调用超类的有参构造,
且位于第一行! 否则会报错!
*/
super(3);
this.a=a;
}
}
(1)this代表的是本类,super代表的是父类
(2)当本类的成员变量与局部变量同名时,我们可以通过this.变量名指定本类的成员变量
当父类的成员变量与子类的变量同名时,我们可以通过super.变量名指定父类的成员变量
(3)我们可以在子类构造函数的第一行
使用super();调用父类的无参构造 / 使用super(参数); 调用父类对应参数的构造方法
注意:子类默认调用super();父类的无参构造,如果父类没有无参构造,需要手动指定调用哪个含参构造
this:
1)this.成员变量名------------ 访问成员变量(成员变量与局部变量同名时,若想访问成员变量则this不能省略)
2)this.方法名()--------------调用方法(了解)
3)this()--------------------调用构造方法(了解)
super:
1)super.成员变量名------------------访问超类的成员变量(了解)
2)super.方法名()-------------------调用超类的方法
3)super()-------------------------调用超类的构造方法
(1)概念
是java中的一个关键字,用于修饰成员(成员变量和成员方法)。
(1)被static修饰的资源统称为静态资源,可以用来修饰变量、方法、代码块、内部类
(2)静态资源属于类资源,随着类的加载而加载,最先加载一次,优先于对象进行加载。
(3)静态资源可以不通过对象,使用类名直接调用,不需要创建对象;也被称作类资源。
(4)静态资源只有一份,被全局所有对象共享
(5)静态的调用关系:静态资源只能调用静态资源
(6)静态资源是优先于对象的,所以静态区域内不允许使用this与super关键字
常常
通过类名点来访问 或
也可用通过引用打点访问(不常用)public class StaticDemo {
public static void main(String[] args) {
Eoo o1 = new Eoo();
o1.show();
Eoo o2 = new Eoo();
o2.show();
Eoo o3 = new Eoo();
o3.show();
System.out.println(Eoo.b); //------常常通过类名点来访问:Eoo.b
}
}
class Eoo{ //演示静态变量
int a;
static int b; //------b为静态变量 static修饰成员变量b
Eoo(){
a++;
b++;
}
void show(){
System.out.println("a="+a+",b="+b);
}
}
在类被加载期间自动执行(所以执行顺序优于构造方法)
,一个类只被加载一次,所以静态块也只执行一次package oopday9.day05.ivsv;
import oopday9.day05.person.Student;
import java.util.Scanner;
/**
* 《成员变量分两种》:-----------实例变量和静态变量都属于成员变量
* 面试题:实例变量与静态变量的区别:
* ①实例变量:没有static修饰,是属于对象的,在创建对象的时候存储在内存堆中,
* 创建多少个对象,则实例变量就会在内存中存在多少份,需要通过引用名打点来进行访问
* ②静态变量:有static修饰,是属于类的,在类被加载时存储在内存方法区中,
* 无论创建多少个对象,静态变量在内存中都只存在一份, 常常通过类名打点来访问
*
* 一、实例变量:
* (1)无static修饰,属于对象 例:类中有一个变量:String name;
* (2)存储在堆中,有几个对象就有几份 例:Student zs = new Student();
* (3)通过对象(引用)打点来访问 例:zs.name;
*
*
* 二、
* 1.静态变量:-----需要被存储
* (1)由static修饰,属于类
* (2)存储在方法区中,只有一份
* (3)常常通过类名点来访问
* (4)何时用:所有对象所共享的数据(图片、音频、视频等)
*
* 2.静态方法:-----需要被存储
* - 由static修饰
* - 属于类,存储在方法区中,只有一份
* - 常常通过类名点来访问
* - 静态方法没有隐式this传递,所以不能直接访问实例成员
* :静态方法中只能直接访问静态成员,实例方法中可以直接访问实例的也可以直接访问静态的
* - (1)何时用:方法的操作与对象无关时可以做成静态的
*
* 构造方法:【初始化实例变量】
* 3.静态块:------需要被执行------------静态块:【初始化静态变量】
* - 由static修饰
* - 属于类,在类被加载期间自动执行,一个类只被加载一次,所以静态块也只执行一次
* - 何时用:初始化/加载静态资源(图片、音频、视频等)
* --顺序:静态块 > 构造方法
*
* 补充:
* 1. 内存管理:由JVM来管理的
* 堆:new出来的对象(包括成员变量)
* 栈:局部变量(包括方法的参数)
* 方法区:.class字节码文件(包括静态变量、所有方法)
*
* 2. 静态块中----------给静态变量做初始化
* 构造方法中--------给实例变量做初始化
*
* 3.程序的执行过程(若都有的话):
* 1)超类的static块 2)派生类的static块 3)超类的构造方法 4)派生类的构造方法
*/
public class StaticDemo {
Scanner s = new Scanner(System.in);
int b = s.nextInt();//---------------------实例方法:需要先new个对象再用对象(引用)打点来访问
StaticDemo(){ }
public static void main(String[] args) {
Loo o1 = new Loo();//------------------Loo中的实例变量a:每new一个对象就会在堆中产生一个新的Loo对象
o1.show();
Loo o2 = new Loo();//------------------Loo中的静态变量b:不管new几次对象,只会在方法区中产生一个,每new一次就会修改这一个b的值一次
o2.show();
Loo o3 = new Loo();
o3.show();
System.out.println(Loo.b);//------------静态变量:常常通过类名点来访问
//System.out.println(o3.b);//注意:静态变量:用引用打点虽然不报错,但是不建议
Moo.test();//---------------------------静态方法:常常通过类名点来访问
Poo o4 = new Poo();//-------------------静态块:只走一遍,并且早于构造方法(因为先加载类)
Poo o5 = new Poo();
}
}
/* 1.演示静态变量--------见静态变量内存图 */
class Loo{
int a;//--------------a为实例变量:无static修饰,属于对象;存储在堆中,有几个对象就有几份;通过对象(引用)打点来访问
static int b; //------b为静态变量:由static修饰,属于类;存储在方法区中,只有一份;常常通过类名点来访问
Loo(){
a++;
b++;
}
void show(){
System.out.println("a="+a+",b="+b);
}
}
/* 2.演示静态方法 */
class Moo {
int a;//------------------实例变量(对象点访问)
static int b;//-----------静态变量(类名点访问)
void show() {//-------------------------------普通方法:有隐式this
System.out.println(a);//()中:默认是this.a
System.out.println(b);//()中:默认是Moo.b
}
static void test() {//------------------------静态方法:没有隐式this
//静态方法中没有隐式this传递!
//没有this就意味着没有对象
//而实例变量a是必须由对象来访问的
//所以如下代码发生编译错误:
// System.out.println(a); //编译错误
// System.out.println(this.a);//加了this也没用,原因如上四句话
System.out.println(b);
}
}
/* 2、(1)演示静态方法在何时用 */
class Noo{
int a;//实例变量a----是----对象的属性
//在show()方法中用到了对象的属性a,意味着show()方法与对象有关,所以不能设计成静态方法
void show(){
System.out.println(a);
}
//在plus()方法中没有用到对象的属性和行为,意味着plus()方法与对象无关,所以可以设计成静态方法
static int plus(int num1,int num2){
int num = num1+num2;
return num;
}
}
/* 3.演示静态块 */
class Poo{
//只在加载类的时候会读取一次,无论new了多少个对象-----------优点:提高性能
static {
System.out.println("静态块");//执行顺序:静态块 > 构造方法
}
//每当new一个对象时就会产生一次,new100次就会产生100次------缺点:大大降低性能,浪费内存
Poo(){
System.out.println("构造方法");
}
}
(1)练习:static入门案例
创建包: cn.tedu.oop
创建类: TestStatic1.java
package cn.tedu.oop;
/*本类用作静态static的入门案例*/
/*0.被static修饰的资源统称为静态资源
* 静态资源是随着类加载而加载到内存中的,比对象优先进入内存
* 所以静态资源可以不通过对象,直接通过类名调用*/
public class TestStatic1 {
public static void main(String[] args) {
//5.通过类名直接调用静态资源
Fruit.clean();//我们可以通过类名直接调用静态方法,这个IDEA会提示
System.out.println(Fruit.kind);//我们可以通过类名直接调用静态属性,这个IDEA会提示
//4.创建水果类的对象
Fruit f1 = new Fruit();
Fruit f2 = new Fruit();
f1.grow();
f1.clean();//没有提示,需要自己写
System.out.println(f1.weight);
System.out.println(f1.kind);//没有提示,需要自己写
//6.修改普通变量的值
f1.weight = 6.6;
System.out.println(f1.weight);//6.6
System.out.println(f2.weight);//0.0
/*3.静态资源在内存中只有一份,而且会被全局所有对象共享
* 所以:不管我们使用哪种方式修改了静态变量的值,使用任何方式来查看
* 都是静态变量那个刚刚修改了的值*/
//7.修改静态变量的值
Fruit.kind = "苹果";
System.out.println(Fruit.kind);
System.out.println(f1.kind);
System.out.println(f2.kind);
f1.kind = "猕猴桃";
System.out.println(Fruit.kind);
System.out.println(f1.kind);
System.out.println(f2.kind);
f2.kind = "香蕉";
System.out.println(Fruit.kind);
System.out.println(f1.kind);
System.out.println(f2.kind);
}
}
//1.创建水果类
class Fruit{
//2.定义属性
/*1.可以用static修饰成员变量吗?--可以*/
static String kind;//品种
double weight;//重量
//3.定义方法
/*2.可以用static修饰方法吗?--可以*/
public static void clean(){
System.out.println("洗水果呀洗水果~");
}
public void grow(){
System.out.println("这个果子长的一看就很好吃~");
}
}
(2)练习:static静态调用关系
创建包: cn.tedu.oopstatic
创建类: TestStatic2.java
package cn.tedu.oopstatic;
/*本类用于测试静态的调用关系*/
/*总结:
* 1.普通资源既可以调用普通资源,也可以调用静态资源
* 2.静态资源只能调用静态资源*/
public class TestStatic2 {
}
//1.创建老师类
class Teacher{
//2.定义普通属性与方法
String name;
public void teach(){
System.out.println("正在授课中...");
/*1.普通资源能否调用静态资源?--可以!!!*/
System.out.println(age);
ready();
}
//3.定义静态属性与方法
static int age;
public static void ready(){
System.out.println("正在备课中...");
/*2.静态资源能否调用普通资源?--不可以!*/
//System.out.println(name);
//teach();
}
public static void eat(){
System.out.println("正在吃饭中...");
/*3.静态资源能否调用静态资源?--可以!*/
System.out.println(age);
ready();
}
}
(1)概念
1.是java提供的一个关键字
2.final是最终的意思
3.final可以修饰类,方法,字段(属性)
初衷:java出现继承后,子类可以更改父类的功能,当父类功能不许子类改变时,可以利用final关键字修饰父类。
(2)特点
常量的定义形式:final 数据类型 常量名 = 值
package oopday9.day05.Final;
/**
* 《演示final修饰变量》:
* 1、修饰变量:变量不能被改变
* 2、修饰方法:方法不能被重写
* 3、修饰类:类不能被继承
*
*/
/* 1、修饰变量:变量不能被改变 */
public class Aoo {
final int num = 5;
void show() {
//num = 55; //编译错误,——————————————final的变量不能被改变
}
}
/* 2、修饰方法:方法不能被重写 */
class Boo{
final void show(){}
}
class Coo extends Boo{
//void show(){} //编译错误,——————————————final修饰的方法不能被重写
}
/* 3、修饰类:类不能被继承 */
final class Doo{
}
//class Eoo extends Doo{} //编译错误,————————final的类不能被继承
class Foo{
}
final class Goo extends Foo{//不能当老爸,但能当儿子
}
package oopday9.day06;
/**
* 《static final常量》-------------应用率高
* 1.必须`声明同时初始化`
* 2.通过`类名点来访问`,不能被改变
* 3.建议:常量名所有`字母都大写`,多个单词用_分隔
* 4.特点:编译器在编译时会将常量直接替换为具体的值,效率高
* 5.何时用:数据永远不变,并且经常使用
*/
public class StaticFinal {
public static void main(String[] args) {
System.out.println(Aoo.PI);//-----------2.常量通过类名打点来访问,不能被改变
//Aoo.PI = 3.1415926;//编译错误,---------5.数据永远不变,并且经常使用。常量不能被改变
//打印Aoo.num时,会发生以下三个步骤:
//1)加载Boo.class到方法区中
//2)将静态变量num一并存储到方法区中
//3)到方法区中获取num的值并输出
System.out.println(Aoo.NUM);//静态变量,类名打点
//4.特点:编译器在【编译时】将常量直接替换为具体的值,【到不了运行期】------效率高
//相当于System.out.println(5);
System.out.println(Aoo.COUNT);//常量,类名打点访问,不能被改变
}
}
class Aoo{
public static final double PI = 3.14159;
//public static final int NUM;//编译错误,----1.常量必须声明同时初始化
public static int NUM = 5;//----------------静态变量NUM
public static final int COUNT = 5;//--------3.常量COUNT,建议:常量名所有字母都大写,多个单词用_分隔
}
(1)抽象类
(1)由abstract修饰
(2)包含抽象方法的类必须是抽象类
(3)抽象类不能被实例化(即:new对象);但是有构造函数
(4)抽象类是需要被派生类继承的,在派生类中:
①要重写所有抽象方法--------------变不完整为完整
或
②要声明为抽象类------------------一般不这么做(这样做就不能new对象了(3))
(5)设计规则:将共有的属性和行为,抽到超类中--------------抽共性
①派生类的行为都一样,则设计为普通方法
②派生类的行为不一样,则设计为抽象方法
抽象类的意义:
(1)封装共有的属性和行为--------------------代码复用
(2)为所有派生类提供统一的类型-----------向上造型—代码复用
(3)可以包含抽象方法,为所有派生类提供统一的入口(目的:能点出来)
(4)派生类的行为不同,但入口是一致的,同时相当于定义了一个标准(强制重写)
(2)特点:
1.abstract 可以修饰方法或者类,可以定义成员变量的
2.被abstarct修饰的类叫做抽象类,被abstract修饰的方法叫做抽象方法
3.包含抽象方法的类必须是抽象类;抽象类中可以没有抽象方法
4.如果类中有抽象方法,那么该类必须定义为一个抽象类
5.子类继承了抽象类以后,要么还是一个抽象类,要么就把父类的所有抽象方法都重写
7.抽象类不能被实例化(即不可以创建 new 对象),所以常用于多态
抽象类中包含构造方法,但是不是为了自己创建对象时使用,而是为了子类的super()
意义:代码复用、向上造型、可以包含抽象方法,为所有派生类提供统一的入口(能点出来),强制必须重写(制定了一个标准)
(3)abstract注意事项
抽象方法要求子类继承后必须重写。
那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中。用是可以用的,只是没有意义了。
1.private:被私有化后,子类无法重写,与abstract相违背。
2.static:静态优先于对象存在,存在加载顺序问题。
3.final:被final修饰后,无法重写,与abstract相违背。
例:
(1)由abstract修饰
(2)包含抽象方法的类必须是抽象类
(3)抽象类不能被实例化(即:new对象)
由abstract修饰
只有方法的定义,没有具体的实现(连{}都没有)
(1)抽象方法的存在意义是什么?
保证当发生向上造型时,通过超类型的引用能点出来那个方法
既然意义只在于能点出来,那为什么不设计为普通方法?
-若设计为普通方法,则派生类可以重写也可以不重写;
-而设计为抽象方法,可以强制派生类必须重写------做了个标准,强制必须重写
/** 海洋对象移动 */
//抽象方法:
//(1)由abstract修饰
//(2)只有方法的定义,没有具体的实现(连{}都没有)
public abstract void move();
综合例子:
public class AnonInnerClassDemo {
public static void main(String[] args) {
//1)创建了Boo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o3
//3)大括号中的为派生类的类体
Boo o3 = new Boo(){ //-----------------匿名内部类!
void show(){
System.out.println("showshow");
//num = 66;//编译错误----匿名内部类中不能修饰外面变量的值!因为在此处默认为final的!
}
};
o3.show();
}
}
/** ----抽象类---- */
abstract class Boo{ //-----------Boo为抽象类(因为由abstract修饰)!Boo不能被实例化(new对象)!
abstract void show(); //抽象类是被继承的派生类!重写所以抽象方法!也声明为抽象类(不常见)!
//由abstract修饰! 只有方法的定义,没有具体的实现(连{}都没有)!
}
一、接口的定义特点:
-是一种引用数据类型
-由interface定义
-只能包含常量和抽象方法------默认权限是public
-接口不能被实例化;但可以向上造型
-接口是需要被实现/继承,实现/派生类:必须重写所有抽象方法
-一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应先继承后实现
-接口可以继承接口
注:接口和抽象类都不能被实例化(new对象)!!!!!!
二、关系:
类和类------------------------继承extends
接口和接口------------------继承extends
类和接口---------------------实现implements
注:
类和类------------继承extends 例:class Boo extends Aoo{}
接口和接口--------继承extends 例:interface Inter5 extends Inter3,Inter4{}
类和接口----------实现implements 例:class Doo implements Inter5{}
类→类→接口--------先继承再实现 例:class Coo extends Boo implements Inter2,Inter3{}
三、接口的意义:
封装部分派生类共有的属性和行为,实现多继承
制定了一个标准,一种规范
四、设计规则:
(1)将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
(2)①派生类的行为都一样,则设计为普通方法
②派生类的行为不一样,则设计为抽象方法
(3)①将部分派生类所共有的属性和行为,抽到接口中
②接口是对继承的单根性的扩展---------------实现多继承
③符合既是也是原则时,应使用接口
练习:
package oopday9.day08;
/**
* 《接口》:
* 一、语法:
* (1)是一种引用数据类型--------可以声明常量
* (2)由interface定义
* 例:class Aoo{} class定义类
* interface Inter{} interface定义接口
* (3)只能包含常量和抽象方法(默认权限只能是public----因为接口里边的都是让别人用的)
* (4)接口不能被实例化(不能被new,一new就报错)-----因为接口完全不完整
* (5)①接口是需要被实现(implements)/继承(extends)的,在实现类/派生类中:必须重写所有抽象方法(即接口中不能有方法体)
* ②类实现接口时--------必须重写爷爷辈和爸爸辈的所有方法
* (6)一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应先继承后实现
* (7)①接口可以继承接口(接口可以继承多个接口)-------接口也可以是超类,只要是继承关系,就能造型为子类的父类
* ②接口可以继承多个接口-----不必重写接口的所有方法!
*
* 二、关系:
* (1)类和类------------------------继承extends
* (2)接口和接口--------------------继承extends
* (3)类和接口---------------------实现implements
*
* 三、接口的意义:
* (1)封装部分派生类共有的属性和行为,实现多继承
* (2)制定了一个标准,一种规范
*
* 四、设计规则:
* (1)将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
* (2)①派生类的行为都一样,则设计为普通方法
* ②派生类的行为不一样,则设计为抽象方法
* (3)①将部分派生类所共有的属性和行为,抽到接口中
* ②接口是对继承的单根性的扩展---------------实现多继承
* ③符合既是也是原则时,应使用接口
*
*/
public class Interface {
public static void main(String[] args) {
// Inter o = new Inter();//编译错误,--(4)接口不能被实例化
Inter5 o2 = new Doo();//向上造型(可以造型为它所实现的接口)
Inter4 o3 = new Doo();//向上造型(爷爷辈也可以为子辈的超类)
}
}
/** 1、演示接口的定义 */
interface Inter{//------------------------(2)由interface定义
public static final int NUM = 5;//----(3)只能包含常量
public abstract void show();//--------(3) 和抽象方法
int COUNT = 6;//----------------------(3)接口中默认权限只能是public static final
void say();//-------------------------(3)接口中默认权限只能是public abstract
// int number;//编译错误,常量必须声明同时初始化----接口中所有的变量默认是常量
int number = 5;//正确,常量必须声明同时初始化----Java中只是建议常量大写;虽然是number小写,也默认为常量所以不报错
// void test(){}//编译错误,抽象方法不能有方法体---Java中的接口内默认都是抽象方法!---所以接口内的方法不能有方法体
}
/** 2、演示接口的实现 */
interface Inter1{
void show();//接口中访问权限默认是public
void test();
}
class Aoo implements Inter1{//Aoo类实现了Inter1接口----(5)《必须重写Inter1中的所有方法!!!》
// void show(){} //编译错误,类中的访问权限是默认的 不符合重写的原则:一大(派生类的访问权限>=超类)
public void show(){} //--------------------------(5)重写接口中的所有抽象方法,访问权限必须是public
public void test(){}
}
/** 3、演示接口的多实现 */
interface Inter2{
void show();
}
interface Inter3{
void test();
}
abstract class Boo{
abstract void say();
}
//------------------------------(6)一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应【先继承后实现】
class Coo extends Boo implements Inter2,Inter3{
public void show(){}//------(5)①接口是需要被实现/继承,实现/派生类中:必须重写所有方法(即接口中不能有方法体)
public void test(){}
void say(){}//--------------(5)②类继承类实现接口时,必须重写父类的抽象方法!
}
/** 4、演示接口继承接口 */
interface Inter4{
void show();
}
interface Inter5 extends Inter4{//----------(7)接口可以继承接口
void test();
}
class Doo implements Inter5{ //-------------注意:类实现接口时--------必须重写爷爷辈和爸爸辈的所有方法
public void test(){}
public void show(){}
}
interface Inter6 extends Inter4,Inter5{//--(7)接口可以继承多个接口-----不必重写接口的所有方法!
}
综合概述:
1.接口不是类,是一种引用数据类型
2.如果一个 类 想要实现接口中定义的规则,需要使用implments与接口建立实现关系
注意:如果有任何一个抽象方法没有实现,那么这个类就是一个抽象子类
3.只能包含常量和抽象方法------默认权限是public
4.接口中只有静态常量,没有普通变量,会自动拼接public static final
5.接口中的方法也可以简写,会自动拼接public abstract
6.接口不能被实例化(即不能new对象!)
7.若又继承又实现时,应先继承后实现
8.接口更多的是规则的制定者,不做具体的实现----------接口的意义
9.接口可以继承接口;一个类可以实现多个接口,用逗号分隔。
10.接口提高了程序的功能拓展,降低了耦合性
11.接口是需要被实现/继承,实现/派生类:必须重写所有抽象方法(Java8中接口里的所有方法都是抽象方法)
12.接口中也没有构造方法,实现类调用的是它自己父类的构造方法,如果没有明确指定父类,那就是Object的
类和类------------继承extends 例:class Boo extends Aoo{}
接口和接口--------继承extends 例:interface Inter5 extends Inter3,Inter4{}
类和接口----------实现implements 例:class Doo implements Inter5{}
类→类→接口--------先继承再实现 例:class Coo extends Boo implements Inter2,Inter3{}
(1)类与类的关系
Java的类只支持单继承,类与类就是继承关系,并且一个子类只能有一个父类
class Son extends Father{ }
(2)接口与接口的关系
Java的接口是不做限制的,可以多继承
interface Inter1 extends Inter2{ } – Inter1是子接口 Inter2 是父接口
interface Inter1 extends Inter2,Inter3{ } – Inter1 是子接口 Inter2 和 Inter3 都是父接口
注意:如果是情况2的话,接口1的实现类需要实现这三个接口(Inter1,2,3)的所有抽象方法
(3)接口与类的关系
Java中的类对于接口而言是多实现的,所以一个类可以实现多个接口
class InterImpl implements Inter1{}
class InterImpl implements Inter1,Inter2{}
(4)、接口与抽象类的区别
①接口是一种用interface定义的类型; 抽象类是一种用class定义的类型
②接口中的方法都是抽象方法,还有默认方法与静态方法; 抽象类中的方法不做限制
③接口中的都是静态常量; 抽象类中可以写普通的成员变量
④接口没有构造方法,不可实例化; 抽象类有构造方法,但是也不可以实例化
⑤接口是先天设计的结果,抽象是后天重构的结果
⑥接口可以多继承; 抽象类只能单继承
(0)
我们可以把内部类看作是外部类的一个特殊的资源
;内部类可以直接使用外部类的所有资源,包括私有资源
;外部类如果想要使用内部类的资源,需要创建内部类的对象才能使用
;对象的普通创建方式:
外部类如果想要使用内部类的资源,需要创建内部类的对象才能使用:
/*外部类名.内部类名 对象名 = 外部类对象.内部类对象*/
Outer.Inner oi = new Outer().new Inner();
成成员内部类的实例化语法为:
外部类实例.new 内部类();
例:
public class Demo{
public static void main(String[] args) {
/* 外部类若想访问内部类:不能直接[new 内部类] */
//Innter innter = new Innter();
Demo demo = new Demo();
/*
那外部类如何访问内部类:
需要:
成员内部类的实例化语法为:
外部类实例.new 内部类();
*/
Innter inner = demo.new Inner();
}
public class Innter{
}
}
(1)内部类概述:
如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。
就是把类定义在类的内部的情况就可以形成内部类的形式。
A类中又定义了B类,B类就是内部类,B类可以当做A类的一个成员看待:
(1)位置:类里方法外
1)被private修饰
被私有化的内部类在main()中是没有办法直接创建其对象的
可以在私有内部类所处的外部类中,创建一个公共的方法供外界调用,这个方法用来返回创建好的私有内部类对象
2) 被static修饰
静态内部类可以不创建外部类对象,直接创建静态内部类对象,格式:Outer3.Inner3 oi = new Outer3.Inner3();
如果静态内部类中还有静态方法,那么我们可以不创建对象
直接通过链式加载的方式调用:Outer3.Inner3.show2();//表示通过外部类名直接找到静态内部类,再找到静态方法
(2)特点:
package oopday9.day07.innerclass;
/**
* 《成员内部类(MemberInnerClass)》:应用率低,了解
* 1、特点:
* (1)类中套类,外面的称为外部类,里面的称为内部类
* (2)内部类通常只服务于外部类,对外不具备可见性
* (3)内部类对象只能在外部类中创建
* (4)内部类中可以直接访问外部类的成员(包括私有的)
* (5)在内部类中有个隐式的引用指向了创建它的外部类对象——————外部类名.this(当前对象的外部类对象)
*
* 补充--------隐式对象:
* (1)this:当前对象
* (2)super:当前对象的超类对象
* (3)外部类名.this:指代当前对象的外部类对象
*
*/
public class MemberInnerClass {
public static void main(String[] args) {
Mama m = new Mama();
//Baby b = new Baby(); //编译错误,------------(2)内部类通常只服务于外部类,对外不具备可见性
}
}
class Mama{ //-----------------外部类!
private String name;
Baby b = new Baby(); //---------------------------(3)内部类对象通常在外部类中创建!
class Baby{ //-------------内部类!
void show(){
//(5)在内部类中有个隐式的引用指向了创建它的外部类对象——————外部类名.this(当前对象的外部类对象)
System.out.println(name);//---------------(4)内部类中可以直接访问外部类的成员(包括私有的)
System.out.println(Mama.this.name); //----(5)Mama.this指代它的外部类对象!
//编译错误 this指当前对象内部类 而name是外部类的成员变量 若想访问name--必须【外部类名.this.当前对象的外部类对象】
//System.out.println(this.name);
}
}
}
这是使用 static 关键字修饰的内部类,静态内部类不持有外部类的引用,可以看作是和外部类平级的类。
我们想在静态内部类中访问外部类的成员只能 new 一个外部类的对象,否则只能访问外部类的静态属性和静态方法。
使用场景:当类 A 需要使用类 B,而 B 不需要直接访问外部类 A 的成员变量和方法时,可以将 B 作为 A 的静态内部类。
位置:方法里
直接创建外部类对象,调用局部内部类所处的方法,并不会触发局部内部类的功能
需要在外部类中创建局部内部类的对象并且进行调用局部内部类的功能,才能触发内部类的功能
创建包: cn.tedu.innerclass
创建类: TestInner4.java
package cn.tedu.innerclass;
/**本类用来测试局部内部类*/
public class TestInner4 {
public static void main(String[] args) {
/**如何使用内部类的资源呢?
* 注意:直接调用外部类的show()是无法触发内部类功能的
* 需要再外部类中创建内部类对象并且进行调用,才能触发内部类的功能
* */
//5.创建外部类对象调用show()
//7.当在外部类show()中创建局部内部类对象并且进行功能调用后,内部类的功能才能被调用
new Outer4().show();
}
}
//1.创建外部类Outer4
class Outer4{
//2.创建外部类的成员方法
public void show() {
//3.创建局部内部类Inner4—不太常用!!!
/**位置:局部内部类的位置在方法里*/
class Inner4{//-----------------类中套方法,方法中套类(局部内部类)!
//4.创建局部内部类的普通属性与方法
String name;
int age;
public void eat() {
System.out.println("我是Inner4的eat()");
}
}
/**如何使用局部内部类的资源?*/----------------------
//6.在show()里创建内部类对象
Inner4 in = new Inner4();
in.eat();
System.out.println(in.name);
System.out.println(in.age);
}
}
--------------------- 若想创建一个类(派生类)的对象,并且对象只被创建一次,此时该类不必命名,称为匿名内部类
位置:可运行代码中,比如 main()中
匿名内部类通常与匿名对象【没有名字的对象】一起使用
格式:
new Inter1(){
//我这个大括号其实是一个匿名内部类,我来实现方法
}.eat();
如果只是想使用一次接口/抽象类的某个功能,可以使用匿名内部类
匿名内部类+匿名对象的功能:创建实现类+实现方法+方法功能的一次调用【功能三合一】
(0)匿名内部类特点:
1.若想创建一个类(派生类)的对象,并且对象只创建一个,此时该类不必命名,称为匿名内部类
2.匿名内部类属于局部内部类,而且是没有名字的局部内部类,通常和匿名对象一起使用
3.匿名内部类中不能修改外面变量的值,因为在此处该变量默认为final的
4.面试题:问:内部类有独立的.class吗? 答:有
package oopday9.day07.anonoinner;
/**
* 《匿名内部类(AnonInnerClass)》:-----应用率高----大大简化代码
* (1)若想创建一个类(派生类)的对象,并且对象只创建一次,此时该类不必命名,称为匿名内部类
* (2)匿名内部类属于局部内部类,而且是没有名字的局部内部类,通常和匿名对象一起使用
* (3)匿名内部类中不能修改外面变量的值,因为在此处该变量默认为final的
*/
public class AnonInnerClass { //---------相当于外部类
public static void main(String[] args) {
/* 创建匿名内部类Aoo的时候系统做了下面三件事:*/
//1)创建了Aoo的一个派生类(o1),但是没有名字【abstract class Aoo{ }】
//2)为该派生类创建了一个对象,名为o1
//3)大括号中的为派生类的类体
Aoo o1 = new Aoo(){ //-----------------匿名内部类!
};
//1)创建了Aoo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o2
//3)【大括号中的为派生类的类体】
Aoo o2 = new Aoo(){ //-----------------匿名内部类!
};
int num = 5;
// num = 55;
//1)创建了Boo的一个派生类,但是没有名字
//2)为该派生类创建了一个对象,名为o3
//3)【大括号中的为派生类的类体】
Boo o3 = new Boo(){ //-------------【匿名内部类! 】
void show(){//派生类的类体:-----【o3默认为超类(Boo)的派生类对象,大括号就是o3派生类的类体!!!所以必须重写Boo的抽象方法】
System.out.println("showshow");
System.out.println(num);//------(3)匿名内部类若想访问外面变量的值,则不能在外面修改num的值(:num=55;)!否则:
// System.out.println(num);//否则编译错误
//num = 66;//编译错误------------(3)匿名内部类中不能修饰外面变量的值!因为在此处外面的变量默认为final的!
}
};
o3.show();//--------由派生类的对象去调用派生类的类体
}
}
abstract class Aoo{ }
/** ----抽象类---- *///--↓所以Boo相当于超类!
abstract class Boo{ //--Boo为抽象类(因为由abstract修饰)!
//抽象类是需要被继承的,在派生类中:要重写所有抽象方法(变不完整为完整) 或 要声明为抽象类
/* 抽象方法:由abstract修饰! 只有方法的定义,没有具体的实现(连{}都没有)! */
abstract void show(); //抽象类是被继承的派生类!所以派生类要重写所有抽象方法!或者声明为抽象类(不常见)!
}
(1)问题:匿名内部类中new的对象是什么?大括号里的是什么?
1.new的对象其实就是一个派生类,只不过没有单独在一个类里继承,而是合为了匿名内部类中实现。
2.new对象后的大括号里:其实就是派生类的类体,里面必须重写超类(即数据类型)的所有方法。
注1:若匿名内部类想调用方法,需要用对象【即引用(即变量o3)】打点去调用大括号中派生类的方法:
(2)问题:那么为什么匿名内部类中使用参数需要声明为final呢?
因为匿名内部类是创建一个对象并返回,这个对象的方法被调用的时机不确定,方法中有修改参数的可能。
如果在匿名内部类中修改了参数,外部类中的参数是否需要同步修改呢?
Java 为了避免这种问题,限制匿名内部类访问的变量需要使用 final 修饰,这样可以保证访问的变量不可变。
而且,除了静态内部类,剩下的成员内部类、局部内部类、匿名内部类 都会默认隐式地持有外部类的引用。
在生成的.class字节码文件里:
·堆区:
1.存储new出来的对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 。
·栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的值和对象以及基础数据的引用
2.每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
·方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
①存储new出来的对象(包括实例变量)
②垃圾:没有任何引用所指向的对象
[垃圾回收器(GC)不定时到内存堆中清扫垃圾,回收的过程中透明的(看不到的),不一定一发现垃圾就立刻回收,通过调用System.gc()
建议虚拟机尽快调度GC来回收]
注:
垃圾回收机器(Garbage Collection),也叫GC,垃圾回收器主要有一下特点:
当对象不再被程序所使用的时候,垃圾回收器将会将其回收
垃圾回收是在后台运行的,我们无法命令垃圾回收器马上回收资源,但是我们可以告诉他可以
尽快回收资源(System.gc()和Runtime.getRuntime().gc())
垃圾回收器在回收某个对象的时候,首先会调用该对象的finalize()方法
GC主要针对堆内存
单例模式的缺点
finalize():
(1)是Object里面的一个方法,当一个堆空间中的对象没有被栈空间变量指向的时候,这个对象会
等待被java回收:jdk里面是这样实现的:protected void finalize() throws Throwable { }
(2)源码中显示的@Deprecated(since="9")的意思就是说在jdk9以后这个方法就过时了;
用有其他的方法代替:
----JDK将在未来(jdk18)移除对象的finalize()方法,
参考JEP 421,建议使用jdk9之后提供的cleaner或try-with-resource来改造带finalize()方法的类。
但是cleaner仍然是由GC调度的,所以并不能解决需要及时释放资源的情况,需要及时释放资源要应使用try-with-resources或提供close()方法由资源使用者释放。
③实例变量的生命周期:创建对象时存储在堆中,对象被回收时一并被回收
④内存泄漏:不再使用的对象没有被及时的回收,严重的泄漏会导致系统的崩溃,建议:不再使用的对象应及时将引用设置为null
1.每个线程包含一个栈区,栈中只保存基础数据类型的值和对象以及基础数据的引用
2.每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
局部变量(包括方法的参数)
-----包含8种基本类型(存的是值)
和 引用类型(即变量,存的是地址)
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的【.class和static变量】。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
.class字节码文件(包括静态变量、所有方法)
方法只有一份,
通过this来区分具体的调用对象package oopday9.day03.StudentReference;
/**
* 基本数据类型(basic data type)默认值为————————0!
* //声明整形数组arr,包含3个元素,每个元素都是int类型,默认值为0
* int[] arr = new int[3];//基本数据类型
* arr[0] = 100;
*
*
* 引用数据类型(reference data type)数组的演示:————————默认值为null!
* //声明Student类型数组s,包含3个元素,每个元素都是Student型,默认值为null
* //Student是自己写的,就是引用数据类型:
* Student[] stus = new Student[3];//创建Student数组对象
*
*/
public class Student {
String name;
int age;
String address;
Student(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
public static void main(String[] args) {
int[] arr = new int[3];//基本数据类型
arr[0] = 100;
System.out.println(arr[0]);
/* 声明Student引用类型数组s,包含3个元素,每个元素都是Student型,默认值为null
Student是自己写的,就是引用数据类型: */
//1.创建Student数组对象
Student[] stus = new Student[3];
System.out.println(stus);//【@28d93b30】----------变量存着是地址!!!
/*
Student stus1 = new Student("张三",25,"河北");
stus[0] = stus1;
上两句同下:
*/
//2.创建Student对象(即给变量传参赋值的时候)-------注1:只要是引用类型,就得new一个构造方法来传参:Student();
stus[0] = new Student("张三",25,"河北");
stus[1] = new Student("李四",26,"佳木斯");
stus[2] = new Student("王五",27,"山东");
//注1:【引用类型直接访问数组的元素输出的是地址!】
System.out.println(stus[0]);//@28d93b30----------数组的元素存着是地址!!!
//(1)输出第一个学生的名字----------------------注2:【引用类型想访问数组的元素需要通过数组元素打点】
System.out.println(stus[0].name);//张三
//(2)修改第2个学生的年龄为24------------------注3:【若想访问具体元素的特有属性(name/age...)先取到想访问的元素,再去打点】
stus[1].age = 24;
//(3)第3个学生跟大家问好
stus[2].sayHi();
//(4)遍历所有学生
for(int i=0;i<stus.length;i++){
System.out.println(stus[i].name);//①输出每个学生的名字
stus[i].sayHi();//②跟所有学生问好
}
//内存过程:
//1.创建了一个Student数组对象:
// 【变量】保存在【栈中】并且【有自己的地址】-------------------------指向堆中的Student数组对象;
// 【Student数组对象保存在堆中】,在堆中就有了Student数组对象,【包含数组的所有元素(stus[0]/stus[1]...)】,引用数据类型默认值为null
//2.创建两个Student对象的同时在堆中又产生了两个,因为是引用类型,
// 所以在堆中元素保存的是地址【@28d93b30,即内存图中的0x2222】-------指向了Student对象
//3.当修改第2个学生的age为24时,又因为数组是连续的,基于地址就找到了Student对象(保存着是属性name/age...)中的三个参数!
}
}
引用类型内存图:
package apiday.z_homework.h07;
import java.util.ArrayList;
import java.util.Collection;
/**
* 局部变量在栈中
* 对象在堆里
* 属性在对象中(在堆里)
*
* 基本类型保存的值就是值本身
* 引用类型保存的值是对象在堆里的地址
*
*/
public class Demo {
public static void main(String[] args) {
String s = "hello";
int a = 1;
Point p = new Point(1,2);
Collection c = new ArrayList();
c.add(p);
test(s,a,p,c);
System.out.println("s:"+s);//? s:hello
System.out.println("a:"+a);//? a:1
System.out.println("p:"+p);//? p:(3,2)
System.out.println("c:"+c);//? c:[(7,5)]
}
public static void test(String s,int a,Point p,Collection c) {
s = s + "world";
a++;
p.setX(3);
p = new Point(4,5);
c.clear();
c.add(p);
c = new ArrayList();
p.setX(7);
c.add(p);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。