JAVA基础复习资料必备(二)
Java诞生于SUN(Stanford University Network),09年被Oracle(甲骨文)收购。
Java之父是詹姆斯.高斯林(James Gosling)。
1996年发布JDK1.0版
Java开发人员需要安装JDK。如果只是运行Java程序那么只需要安装JRE就可以了。
JDK(Java Developement kits):Java开发工具包
JRE(Java Runtime Environment):Java运行环境
JVM (Java Virtual Machine):Java虚拟机
JDK = JRE + 开发工具(javac.exe,java.exe,javadoc.exe等)
JRE = JVM + Java核心类库(常用类:String(字符串),Date(日期时间),Math(数字),IO(输入输出流),net(网络),集合,多线程等)
1、安装JDK
2、配置JDK的开发工具集(bin)目录到path环境变量中
例如:D:\ApacheSoftwareFoundation\Java\jdk1.8.0_221;(注意:以自己的安装目录为准)
3、如何配置环境变量:【计算机】右键【属性】,选择【高级系统设置】,选择【高级】,选择【环境变量】
4、配置path系统变量的作用:%PATH%代表可执行文件的搜索路径,默认为 Windows 目录和系统目录,配置后可以直接打开可执行文件
5、为什么要配置环境变量:希望在命令行使用javac.exe等工具时,任意目录文件都可以找到这个工具所在的目录。
class HelloWorld{
public static void main(String[] args){
System.out.println("Hello world!");
}
}
1、编写:编写.java文件
2、编译:通过javac.exe编译成.class字节码文件(因为JVM只认识字节码)
javac 源文件名称.java
3、运行:通过java.exe文件运行
结构:
类{
方法{
语句;
}
}
格式:
(1)每一级缩进一个Tab键;
(2){}的左半部分在行尾,右半部分单独一行,与和它成对的"{"的行首对齐,{}成对出现;
Java程序的入口是main方法
public void void main(String[] args){
}
1、单行注释
//注释内容
2、多行注释
/*
注释内容
*/
3、文档注释
/**
注释内容(可以使用注释注解)
*/
1、字符编码格式问题
使用cmd命令行的字符编码格式与源文件.java的字符编码不一致如何解决(原因:大部分cmd命令行默认的字符编码格式和系统相同为"GBK",而.java文件的编码格式为其他,通过命令行的方式去解码不为"GBK"编码的文件时,就会导致乱码)
解决方案一:使用javac -encoding指定源文件的字符编码(格式:javac -encoding 字符编码 类文件.java)
解决方法二:使用Notepad++等编辑器修改字符编码
2、大小写问题:Java语言严格区分大小写
(1)源文件名称不区分大小写(建议区分);
(2)字节码文件、类名、代码中都区分大小写;
3、源文件名称和类名一致问题
(1)如果这个类不是public修饰的,源文件名称可以和类名不同;
(2)一个文件中可以存在多个非public修饰的类;
(3)如果这个类是public修饰的,源文件名称必须和public修饰的类名相同;
(4)一个文件中最多只能存在一个public修饰的类(不包括内部类);
建议:不管是不是public修饰的类,都与源文件的名称相同,且一个源文件只写一个类(原因:好维护);
(5)main方法必须在public修饰的类中?
可以在其他类中,由于代码习惯,基本上写在public修饰的类中。
凡是可以自己命名的地方都可以称作标识符
例如:类名,变量名,方法名等
1、标识符的命名规则
(1)Java中的标识符由26个英文字母大小写,数字0-9,美元符号&和下划线_组成;
(2)不可以使用Java的关键字,特殊值和保留字;
(3)不可以以数字开头;
(4)不可以包含空格;
(5)严格区分大小写;
2、标识符的命名规范
(1)见名知意;
(2)类名,接口名等:采用大驼峰命名法也就是单词首字母大写(XxxYyyZzz);
(3)变量名,方法名等:采用小驼峰命名法也就是第二个单词的首字母开始大写(xxxYyyZzz);
(4)包名:全部字母小写,单词之间使用.隔开(xxx.yyy.zzz);
(5)常量名:全部字母大写,单词之间使用_隔开(XXX_YYY_ZZZ);
变量的作用:用来存储数据,代表内存中的一块存储空间,变量中的值是可以改变的
(1)类型名称
(2)变量名
(3)变量值
(1)先声明后使用
(2)变量名有作用域
(3)同一个作用域下不能重名
(4)使用前必须先初始化
声明:数据类型 变量名 = 变量值;
例如:int num = 10;
1、基本数据类型(8种):byte,short,char,int,long,float,double,boolean;
2、引用数据类型:String、enum、数组、接口等;
1、整数系列
(1)byte(字节型):1个字节(取值范围:-27~27-1)
(2)short(短整型):2个字节(取值范围:-215~215-1)
(3)int(整型):4个字节(取值范围-231~231-1)
(4)long(长整型):8个字节(取值范围-263~263-1)
注意:如果要表示某个常量数字它是long类型,那么需要在数字后面加L或l
2、浮点系列
(1)float(单精度浮点):4个字节(精度:科学记数法的小数点后6~7位)
注意:如果要表示某个常量数字是float类型,那么需要在数字后面加F或f
(2)double(双精度浮点):8个字节(精度:科学记数法的小数点后15~16位)
3、字符类型:char(字符):2个字节
(1)Java中使用的字符集:Unicode编码集
(2)字符的三种使用方式
(1)表示单个字符
例如 :‘A’;
(2)转义字符
例如:\t,\n等;
Unicode的十六进制表示
x4E2D代表’中’;
4、布尔类型:boolean:只能存储true或false
(1)进制类型:
二进制:逢二进一,由数字0-1组成
八进制:逢八进一,由数字0-7组成
十进制:逢十进一,由数字0-9组成
十六进制:逢十六进一,由数字0-9,A-F组成
(2)为什么byte的取值范围是-128~127?
1个字节:8bit
0000 0001 ~ 0111 1111 ===> 1 ~ 127;
1000 0001 ~ 1111 1111 ===> -127 ~ -1;
0000 0000 = 0;
1000 0000 = -128;(特殊规定)
计算机中的数据存储
计算机数据的存储使用二进制补码形式存储,并且最高位是符号位,1是负
数,0是正数。
规定:正数的补码与反码、原码一样,称为三码合一;负数的补码与反码、原码不一样:
(1)负数的原码:把十进制转为二进制,然后最高位设置为1
(2)负数的反码:在原码的基础上,最高位不变,其余位取反(0变1,1变0)
(3) 负数的补码:反码+1
例如:byte类型(1个字节,8位)
25 ==> 原码 0001 1001 ==> 反码 0001 1001 ==> 补码 0001 1001
-25 ==> 原码 1001 1001 ==> 反码 1110 0110 ==> 补码 1110 0111
底层使用加法代替减法:-128 ==> -127 - 1 ==> -127 + (-1) ==> 1111 1111 + 1000 0001 ==> 1000 0000 ==> -128
(3)为什么float(4个字节)的存储范围比long(8个字节)的存储范围大
因为float、double底层也是二进制,先把小数转为二进制,然后把二进制表示为科学记数法,然后只保存:
浮点类型的存储由符号位、指数、尾数部分组成
格式
float:SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
double:SEEE EEEE EEEE MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM
S:符号位
E:指数,十进制指数加上127后的值得二进制数据
M:底数
③尾数部分:尾数部分
任何一个数都的科学计数法表示都为1.xxxx*2n。尾数部分就可以表示为xxxx,第一位都是1,可以省略
float的存储是科学计数法(指数的增长为2n),而long存储是普通的二进制
(4)为什么double(8个字节)比float(4个字节)精度范围大?
float 可以保证十进制科学计数法小数点后6位有效精度和第7位的部分精度
double 可以保证十进制科学计数法小数点后15位有效精度和第16位的部分精度。
1、自动类型转换
(1)当把存储范围小的值(常量值,变量值,表达式中的计算结果)赋值给存储范围大的变量时会发生自动类型转换
byte -> short -> int -> long ->float ->double
char ->
(2)当存储范围小的数据类型和存储范围大的数据类型进行混合运算时,会按照其中最大的类型运算
(3)当byte,short,char数据类型进行算术运行时会按照intl类型处理
2、强制类型转换
(1)当把存储范围大的值(常量值,变量值,表达式中的计算结果)赋值给存储范围小的变量时,需要进行强制类型转换(注意:可能会损失精度或者溢出)
dounble -> float -> long -> int -> short -> byte
-> char
(2)当某个值想要提升数据类型时也可以使用强制类型转换
(3)boolean类型不参与
1、任意数据类型的数据和String类型进行"+"运算时,结果一定是String类型
2、String类型不可以通过强制类型转换,转为其他类型
1、按照操作符的个数分类:
(1)一元运算符:操作数只有一个
例如:正号(+),负号(-),自增(++),自减(–),逻辑非(!),按位取反(~)
(2)二元操作符:操作数有两个
例如:加(+),减(-),乘(),除(/),取模(%),大于(>),小于(<),大于等于(>=),小于等于(<=),等于(==),不等于(!=)
赋值(=,+=,-=,=,/=,%=,>>=,<<=)
逻辑与(&),逻辑或(|),逻辑异或(^),短路与(&&),短路或(||)
左移(<<),右移(>>),无符号右移(>>>),按位与(&),按位或(|),按位异或(^)
(3)三元运算符:操作数三个
例如: ? :
2、Java基本数据类型的运算符:
说明:数字和单个字符可以使用算术运算符。其中+,当用于字符串时,表示拼接。
加法:+
减法:-
乘法:*
除法:/
注意:整数与整数相除,只保留整数部分
取模:% 取余
注意:取模结果的正负号只看被模数
正号:+
负号:-
自增:++
自减:–
原则:自增与自减
++/–在前的,就先自增/自减,后取值
++/–在后的,就先取值,后自增/自减
整个表达式的扫描,是从左往右扫描,如果后面的先计算的,那么前面的就暂时先放到“操作数栈”中
说明:右边的常量值、表达式的值、变量的值的类型必须与左边的变量一致或兼容(可以实现自动类型转换)或使用强制类型转换可以成功。
基本赋值运算符:=
扩展赋值运算符:+=,-=,*=,/=,%=…
注意:
所有的赋值运算符的=左边一定是一个变量
扩展赋值运算符=右边的计算结果的类型如果比左边的大的话会强制类型转换,所以结果可能有风险。
扩展赋值运算符的计算:
(1)赋值最后算
(2)加载数据的顺序是把左边的变量的值先加载,再去与右边的表达式进行计算
说明:其他的比较运算符都是只能用于8种基本数据类型。其中的==和!=可以用于引用数据类型的比较,用于比较对象的地址
大于:>
小于:<
大于等于:>=
小于等于:<=
等于:== 注意区分赋值运算符的=
不等于:!=
注意:
比较表达式的运算结果一定只有true/false
比较表达式可以作为(1)条件(2)逻辑运算符的操作数
说明:逻辑运算符的操作数必须是布尔值,结果也是布尔值
逻辑与:&
运算规则:只有左右两边都为true,结果才为true。
例如:true & true 结果为true
false & true 结果为false
true & false 结果为false
false & false 结果为false
逻辑或:|
运算规则:只要左右两边有一个为true,结果就为true。
例如:true | true 结果为true
false | true 结果为true
true | false 结果为true
false | false 结果为false
逻辑异或:^
运算规则:只有左右两边不同,结果才为true。
例如:true ^ true 结果为false
false ^ true 结果为true
true ^ false 结果为true
false ^ false 结果为false
逻辑非:!
运算规则:布尔值取反
例如:!true 为false
!false 为true
短路与:&&
运算规则:只有左右两边都为true,结果才为true。
例如:true & true 结果为true
true & false 结果为false
false & ? 结果就为false
它和逻辑与不同的是当&&左边为false时,右边就不看了。
短路或:||
运算规则:只要左右两边有一个为true,结果就为true。
例如:true | ? 结果为treu
false | true 结果为true
false | false 结果为false
它和逻辑或不同的是当||左边为true时,右边就不看了。
? :
?前面必须是条件,必须是boolean值
语法:
条件表达式 ? 结果表达式1 : 结果表达式2(结果表达式1和结果表达式2要保持类型一致或兼容)
运算规则:整个表达式的结果:当条件表达式为true时,就取结果表达式1的值,否则就取结果表达式2的值
说明:一般用于整数系列
左移:<<
运算规则:左移几位就相当于乘以2的几次方
右移:>>
运算规则:右移几位就相当于除以2的几次方
无符号右移:>>>
运算规则:往右移动后,左边空出来的位直接补0,不看符号位
按位与:&
运算规则:
1 & 1 结果为1
1 & 0 结果为0
0 & 1 结果为0
0 & 0 结果为0
按位或:|
运算规则:
1 | 1 结果为1
1 | 0 结果为1
0 | 1 结果为1
0 & 0 结果为0
按位异或:^
运算规则:
1 ^ 1 结果为0
1 ^ 0 结果为1
0 ^ 1 结果为1
0 ^ 0 结果为0
按位取反:~
运算规则:
~0就是1
~1就是0
如何区分&,|,^是逻辑运算符还是位运算符?
如果操作数是boolean类型,就是逻辑运算符,如果操作数是整数,那么就位运算符。
3、运算符的优先级
说明:
(1)表达式不要太复杂
(2)先算的使用()
流程控制语句结构分为:
1、顺序结构:从上到下依次执行
2、分支结构:多个分支选择其中一个分支执行
3、循环结构:重复执行某些代码
1、单分支结构
语法格式:
if(条件表达式){
当条件表达式成立(true)时需要执行的语句块;
}
执行过程:w条件成立,就执行{}其中的语句块,不成立就不执行。
2、双分支结构
语法格式:
if(条件表达式){
当条件表达式成立(true)时需要执行的语句块1;
}else{
当条件表达式不成立(false)时需要执行的语句块2;
}
执行过程:当条件表达式成立(true)时执行语句块1,否则执行语句块2
3、多分支结构
语法格式:
if(条件表达式1){
当条件表达式1成立的时候,执行的语句块1;
}else if(条件表达式2){
当条件表达式1不成立,
条件表达式2成立的时候,执行的语句块2;
}else if(条件表达式3){
当条件表达式1不成立,
条件表达式2也不成立,
条件表达式3成立的时候,执行的语句块3;
}
。。。
【else{
当以上所有的条件表达式都不成立,需要执行的语句块n+1;
}】
执行过程:
(1)多个条件顺序往下判断,如果上面有一个条件成立了,下面的条件就不看了
(2)多个分支也只会执行其中的一个
注意:
(1)每一个条件表达式都必须是boolean值
(2)当{}中的语句只有一个语句(简单的语句,也可以是一个复合语句)时,可以省略{},但是我们不建议省略
(3)当多个条件是“互斥”关系(没有重叠部分),顺序可以随意;
当多个条件是“包含”关系(有重叠部分),顺序不能随意,小的在上,大的在下面
4、嵌套
执行过程:
(1)当嵌套在if中,当外面的if成立时,才会看里面的条件判断
(2)当嵌套在else中,当外面的else满足时,才会看里面的条件判断
语法格式:
switch(表达式){
case 常量值1:
语句块1;
【break;】
case 常量值2:
语句块2;
【break;】
。。。
【default:
语句块n+1;
【break;】
】
}
执行过程:
(1)入口
①当switch(表达式)的值和case后面的某个常量匹配时,就会从这个case进入
②当switch(表达式)的值和case后面的所有常量值不匹配时,就会寻找default分支进入
(2)一旦从“入口”进入switch,就会顺序往下执行,知道遇到“出口”
(3)出口
①自然出口:遇到了对于switch的结束}
②中断出口:遇到了break等
注意:
(1)switch(表达式)的值的类型只能是4种数据类型(byte,short,char,int),两种引用数据类型(枚举,String)
(2)case后面的值必须是常量值且不能重复
循环结构:重复执行某段代码
循环结构的分类:
1、for循环
2、while循环
3、do…while循环
语法格式:
for(;;){
循环体语句块;
if(条件表达式){
break;
}
}
for(初始化表达式; 循环条件; 迭代表达式){
循环体语句块;(需要重复执行的代码)
}
执行过程:
(1)初始化表达式;
(2)判断循环条件
(3)如果循环条件成立,那么执行循环体语句,然后执行迭代表达式再回到(2)…
(4)如果循环条件不成立,结束for循环(或者当前循环中遇到了break语句也会结束当前for循环)
注意:
(1)for(;;)中的两个;一个也不能多也不能少
(2)循环条件必须是boolean类型
语法格式:
while(循环条件){
循环体语句块;
}
经典的形式
while(true){
循环体语句块
if(条件表达式){
break;
}
}
执行过程:
(1)先判断循环条件
(2)若循环条件成立,则执行循环体语句块;然后回到(1)…
(3)若循环条件不成立(循环体语句块中遇到了break),则结束while循环
注意:
while(循环条件)中循环条件必须是boolean类型
语法格式:
do{
循环体语句块;
}while(循环条件);
执行过程:
(1)先执行循环体语句块;
(2)判断循环条件
(3)若循环成立回到(1)…
(4)若循环条件不成立(遇到break),结束do…while循环
注意:
(1)while(循环条件)中循环条件必须是boolean类型
(2)do{}while();最后有一个分号
(3)do…while的循环体语句块至少会执行一次
原则:三种循环之间是可以互相转换的,都能实现循环功能
建议:当次数比较明显时,优先考虑for结构;
当循环体语句块至少执行一次时,优先考虑do…while结构;
当循环条件比较明显但是次数不明显时,优先考虑while结构
三种循环结构都要具有的四要素:
(1)循环变量的初始化表达式;
(2)循环条件
(3)迭代条件
(4)循环体语句块
1、break:结束当前循环,一般用于switch结构和循环结构;
2、continue:跳出当此循环,继续下一次循环,只用于循环结构;
3、return:当前方法的返回值类型为void时,表示结束当前方法;
1、数组(array):一组具有相同数据类型的数据按照一定顺序排列的集合。
2、数组名
(1)代表的是一组数
(2)这个数组名存储的是整个数组的首地址
3、下标(index)
我们使用索引、下标、编号来区别一组数中的某一个
范围[0,array.length)
4、元素(element)
(1)数组中的每一个数据都是一个元素
(2)如何表示数组中的元素(数组名[下标])
5、数组的长度(length)
(1)数组中元素的总个数
(2)如何获取数组的总长度(数组名.length)
语法格式:
元素数据类型[] 数组名称;
数组初始化目的:
(1)确认数组的长度
(2)为数组中的元素赋值
1、动态初始化
语法格式:
//初始化
元素数据类型[] 数组名称 = new 元素数据类型[长度];
//为元素赋值
数组名称[下标] = 值;
//循环赋值
for(int i = 0;i < 长度;i++){
数组名[下标] = 值;
}
当数组只动态初始化,没有进行赋值,那么元素都有默认值:
(1)基本数据类型
①整数类型:byte,short,int,long:默认值为0
②小数类型:float,double:默认值为0.0
③布尔类型:boolean:默认值为false
④字符类型:char:默认值为\u0000
(2)引用数据类型:默认值为null
2、静态初始化
语法结构:
元素数据类型[] 数组名称 = new 元素数据类型[]{值列表};
//简化
元素数据类型[] 数组名称 = {值列表};
使用场景:当数组的元素个数是有限时,可以使用静态初始化。
for循环遍历数组:
for(int i=0; i<数组名.lenght; i++){
//赋值
数组名[i] = 值;
//显示
System.out.println(数组名[i]);
//其他操作
//例如:判断是否是偶数
if(数组名[i]%2==0){
//...
}
}
增强for循环(foreach)
for(数组元素类型 值:数组名){
System.out.println(值);
}
元素是基本数据类型的一维数组解析:
int[] arr = {1,2,3,4,5};
实现思路:
1、数组中找最值
(1)假设第一个元素最大/最小
(2)设置一个变量max/min与后面的元素比较
2、数组中找最值及其下标
(1)(2)设置一个变量用来记录当前最值得下标
(1)求总和
示例代码:
int[] arr = {4,5,6,1,9};
//求总和、均值
int sum = 0;//因为0加上任何数都不影响结果
for(int i=0; i<arr.length; i++){
sum += arr[i];
}
double avg = (double)sum/arr.length;
(2)求均值
示例代码:
int[] arr = {4,5,6,1,9};
//求总乘积
long result = 1;//因为1乘以任何数都不影响结果
for(int i=0; i<arr.length; i++){
result *= arr[i];
}
(3)统计偶数个数
示例代码:
int[] arr = {4,5,6,1,9};
//统计偶数个数
int even = 0;
for(int i=0; i<arr.length; i++){
if(arr[i]%2==0){
even++;
}
}
思路:
(1)借助一个新数组
示例代码:
int[] arr = {1,2,3,4,5,6,7,8,9};
//(1)先创建一个新数组
int[] newArr = new int[arr.length];
//(2)复制元素
int len = arr.length;
for(int i=0; i<newArr.length; i++){
newArr[i] = arr[len -1 - i];
}
//(3)舍弃旧的,让arr指向新数组
arr = newArr;//这里把新数组的首地址赋值给了arr
//(4)遍历显示
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
(2)首尾对应位置交换
示例代码:
int[] arr = {1,2,3,4,5,6,7,8,9};
//(1)计算要交换的次数:次数 = arr.length/2
//(2)首尾交换
for(int i=0; i<arr.length/2; i++){//循环的次数就是交换的次数
//首与尾交换
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
//(3)遍历显示
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
示例代码:扩容
int[] arr = {1,2,3,4,5,6,7,8,9};
//如果要把arr数组扩容,增加1个位置
//(1)先创建一个新数组,它的长度 = 旧数组的长度+1
int[] newArr = new int[arr.length + 1];
//(2)复制元素
//注意:i
for(int i=0; i<arr.length; i++){
newArr[i] = arr[i];
}
//(3)把新元素添加到newArr的最后
newArr[newArr.length-1] = 新值;
//(4)如果下面继续使用arr,可以让arr指向新数组
arr = newArr;
//(5)遍历显示
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
示例代码:备份
int[] arr = {1,2,3,4,5,6,7,8,9};
//1、创建一个长度和原来的数组一样的新数组
int[] newArr = new int[arr.length];
//2、复制元素
for(int i=0; i<arr.length; i++){
newArr[i] = arr[i];
}
//3、遍历显示
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
示例代码:截取
int[] arr = {1,2,3,4,5,6,7,8,9};
int start = 2;
int end = 5;
//1、创建一个新数组,新数组的长度 = end-start + 1;
int[] newArr = new int[end-start+1];
//2、赋值元素
for(int i=0; i<newArr.length; i++){
newArr[i] = arr[start + i];
}
//3、遍历显示
for(int i=0; i<newArr.length; i++){
System.out.println(newArr[i]);
}
1、顺序查找:对数组没要求(原理:依次对比)
示例代码:
int[] arr = {4,5,6,1,9};
int value = 1;
int index = -1;
for(int i = 0,i < arr.length; i++){
if(value == arr[i]){
index = i;
break;
}
}
if(index==-1){
System.out.println(value + "不存在");
}else{
System.out.println(value + "的下标是" + index);
}
2、二分查找:对数组有要求,元素必须有大小顺序的(原理:对折对折再对折,比较中间值)
示例代码:
/*
2、编写代码,使用二分查找法在数组中查找 int value = 2;是否存在,如果存在显示下标,不存在显示不存在。
已知数组:int[] arr = {1,2,3,4,5,6,7,8,9,10};
*/
class Exam2{
public static void main(String[] args){
int[] arr = {1,2,3,4,5,6,7,8,9};//数组是有序的
int value = 2;
int index = -1;
int left = 0;
int right = arr.length - 1;
int mid = (left + right)/2;
while(left<=right){
//找到结束
if(value == arr[mid]){
index = mid;
break;
}//没找到
else if(value > arr[mid]){//往右继续查找
//移动左边界,使得mid往右移动
left = mid + 1;
}else if(value < arr[mid]){//往左边继续查找
right = mid - 1;
}
mid = (left + right)/2;
}
if(index==-1){
System.out.println(value + "不存在");
}else{
System.out.println(value + "的下标是" + index);
}
}
}
1、冒泡排序(原理:相邻元素两两比较)
示例代码:
示例代码:冒泡:从小到大,从左到右两两比较
int[] arr = {5,4,6,3,1};
for(int i=1; i<arr.length; i++){//外循环的次数 = 轮数 = 数组的长度-1
/*
第1轮,i=1,从左到右两两比较,arr[0]与arr[1]。。。。。arr[3]与arr[4]
第2轮,i=2,从左到右两两比较,arr[0]与arr[1]。。。。。arr[2]与arr[3]
...
arr[j]与arr[j+1]比较
找两个关键点:(1)j的起始值:0(2)找j的终止值,依次是3,2,1,0,得出j
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;
}
}
}
2、选择排序(原理:每次循环找出本轮未排序元素中的最值)
示例代码:
int[] arr = {3,2,6,1,8};
for(int i=1; i<arr.length; i++){//外循环的次数 = 轮数 = 数组的长度-1
//(1)找出本轮未排序元素中的最值
/*
未排序元素:
第1轮:i=1,未排序,[0,4]
第2轮:i=2,未排序,[1,4]
...
每一轮未排序元素的起始下标:0,1,2,3,正好是i-1的
未排序的后面的元素依次:
第1轮:[1,4] j=1,2,3,4
第2轮:[2,4] j=2,3,4
第3轮:[3,4] j=3,4
第4轮:[4,4] j=4
j的起点是i,终点都是4
*/
int max = arr[i-1];
int index = i-1;
for(int j=i; j<arr.length; j++){
if(arr[j] > max){
max = arr[j];
index = j;
}
}
//(2)如果这个最值没有在它应该在的位置,就与这个位置的元素交换
/*
第1轮,最大值应该在[0]
第2轮,最大值应该在[1]
第3轮,最大值应该在[2]
第4轮,最大值应该在[3]
正好是i-1的值
*/
if(index != i-1){
//交换arr[i-1]与arr[index]
int temp = arr[i-1];
arr[i-1] = arr[index];
arr[index] = temp;
}
}
//显示结果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]);
}
1、二维数组的声明
元素的数据类型[][] 二维数组的名称;
2、二维数组的初始化
(1)静态初始化
二维数组名 = new 元素的数据类型[][]{
{第一行的值列表},
{第二行的值列表},
...
{第n行的值列表}
};
//如果声明与静态初始化一起完成
元素的数据类型[][] 二维数组的名称 = {
{第一行的值列表},
{第二行的值列表},
...
{第n行的值列表}
};
(2)动态初始化(不规则:每一行的列数可能不一样)
//(1)先确定总行数
二维数组名 = new 元素的数据类型[总行数][];
//(2)再确定每一行的列数
二维数组名[行下标] = new 元素的数据类型[该行的总列数];
//(3)再为元素赋值
二维数组名[行下标][列下标] = 值;
(3)动态初始化(规则:每一行的列数是相同的)
//(1)确定行数和列数
二维数组名 = new 元素的数据类型[总行数][每一行的列数];
//(2)再为元素赋值
二维数组名[行下标][列下标] = 值;
for(int i=0; i<二维数组名.length; i++){
for(int j=0; j<二维数组名[i].length; j++){
System.out.print(二维数组名[i][j]);
}
System.out.println();
}
1、类:一类具有相同特征事物的抽象描述
2、对象:类的实例
3、如何声明类
语法:
【修饰符】 class 类名{
类的成员(属性、构造器、方法、内部内、代码块)
}
4、如何创建对象
new 类名();//匿名对象
类名 对象名 = new 类名();//有名对象
1、声明(属性的类型可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等))
【修饰符】 class 类名{
【修饰符】 数据类型 属性名;
}
2、赋值
(1)在声明属性时赋值
【修饰符】 class 类名{
【修饰符】 数据类型 属性名 = 值;
}
(2)在创建对象时赋值
//创建对象
类名 对象名 = new 类名();
//为对象的属性赋值
对象名.属性名 = 值;
3、如何访问属性中的值
(1)在本类中访问示例代码:
class Circle{
double radius;
double getArea(){
return 3.14 * radius * radius;//直接访问
}
}
(2)在其他类的方法中访问
class Circle{
double radius;
}
class TestCircle{
public static void main(String[] args){
Circle c1 = new Circle();
double area = 3.14 * c1.radius * c1.radius;//对象名.属性名
}
}
4、属性的特点
(1)属性有默认值
(2)每个对象的属性是独立的
5、对象属性的内存图
class Student{
String name;
char gender = '男';//显式赋值
}
class TestStudent{
public static void main(String[] args){
Student s1 = new Student();
System.out.println("姓名:" + s1.name);//null
System.out.println("性别:" + s1.gender);//男
s1.name = "小薇";
s1.gender = '女';
System.out.println("姓名:" + s1.name);//小薇
System.out.println("性别:" + s1.gender);//女
Student s2 = new Student();
System.out.println("姓名:" + s2.name);//null
System.out.println("性别:" + s2.gender);//男
}
}
方法:代表一个独立的可复用功能
好处:
(1)代码复用
(2)简化代码
1、声明
【修饰符】 class 类名{
【修饰符】 返回值类型 方法名(【形参列表】){
方法体
}
}
说明:
(1)修饰符:public、protected、缺省、private、static、final、abstract、native、synchronization
(2)返回值类型:
①void:无返回值
②非void:所有的java类型
(3)方法名:可以很好的体现方法的功能(见名知意,第二个单词开始首字母大写)
(4)形参列表:在完成这个方法的功能时,需要一些数据,这些数据要由“调用者”来决定,那我们就可以设计形参。
语法格式:
():无参,空参
(数据类型 形参名):一个形参
(数据类型1 形参名1, ......, 数据类型n 形参名n):n个形参
(数据类型...形参名):可变形参,0个或多个
(5)方法体:实现方法的功能,最好一个方法就完成一个独立的功能。
2、方法的调用格式:
//本类同级别方法调用:直接调用
【变量 = 】 方法名(【实参列表】);
//在其他类的方法中调用
【变量 = 】 对象名.方法名(【实参列表】);
(1)是否传实参:看被调用的方法是否有形参
(2)是否接收返回值:看被调用的方法是否是void,如果是void,就不需要也不能接收,如果不是void,就可以接收。
3、方法声明与调用的原则
(1)方法必须先声明后调用
如果调用方法时,如果方法名写错或调用一个不存在的方法,编译会报错
(2)方法声明的位置必须在类中方法外
类{
方法1(){
}
方法2(){
}
}
(3)方法调用的位置必须在类中方法内
类{
方法1(){
调用方法
}
}
(4)方法的调用格式要与方法的声明格式对应
①是否要加“对象.”:看是否在本类中,还是其他类中
②是否要接收返回值:看被调用方法是否是void
③是否要传实参:看被调用方法是有形参列表
概念:同一个类两个及以上个方法,方法名相同,形参列表不同,与权限修饰符、返回值类型、抛出的异常无关
Java中方法的参数传递机制:值传递
(1)形参是基本数据类型时,实参给形参传递数据值,形参对值的修改不会影响实参
(2)形参是引用数据类型时,实参给形参传递地址值,形参对值得修改会影响对象的属性值,此时形参和实参指向同一个对象
一维数组:
1、元素是基本数据类型
2、元素是引用数据类型,也称为对象数组,即数组的元素是对象
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。
示例代码:
class MyDate{
int year;
int month;
int day;
}
class Test{
public static void main(String[] args){
MyDate[] arr = new MyDate[3];//创建数组对象本身,指定数组的长度
for(int i=0; i<arr.length; i++){
arr[i] = new MyDate();//每一个元素要创建对象
arr[i].year = 1990 + i;
arr[i].month = 1 + i;
arr[i].day = 1 + i;
}
}
}
1、封装
2、继承
3、多态
1、好处:
(1)隐藏实现细节,方便使用者使用
(2)安全,可以控制可见范围
2、如何实现封装
通过权限修饰符
修饰符 | 本类 | 本包 | 其他包的子类 | 任意位置 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
权限修饰符可以修饰的结构:
类(类、接口等)、属性、方法、构造器、内部类
类(外部类):public和缺省
属性:4种
方法:4种
构造器:4种
内部类:4种
3、通常属性封装,是将属性私有化,提供对应的get/set方法
标准的JavaBean写法
【修饰符】 class 类名{
//属性
private String name;
private int age;
private char gender;
//构造器
public 类名(【形参列表】){
}
//方法
public int 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 int getGender(){
return gender;
}
public void setGender(char gender){
this.gender= gender;
}
}
1、构造器的作用:
(1)创建对象
(2)在创建对象时可以为属性赋值
2、构造器的声明及语法格式:
【修饰符】 class 类名{
【修饰符】 类名(【形参列表】){
}
}
3、构造器的特点
(1)所有的类都有构造器
(2)如果一个类没有显示/明确声明一个构造器,那么编译器会自动添加一个无参构造
(3)如果一个类显示/明确声明一个构造器,那么编译器将不会自动添加一个无参构造,如果需要可以手动添加一个无参构造
(4)构造器的名称必须和类名相同
(5)构造器没有返回值类型
(6)多个构造器之间构成重载
1、this关键字
意思:当前对象
(1)用于构造器中:正在创建的对象
(2)用于成员方法中:正在调用当前方法的对象
2、this用法
(1)this.属性:当属性名称和形参列表同名时,用于区分
(2)this.方法:调用当前对象的方法(完全可以省略“this”)
(3)this()或this(实参列表):
①this():调用本类无参构造器
②this(实参列表):调用本类有参构造器
注意:this()或this(实参列表)要么没有要么出现在构造器语句块的首行
3、成员变量和局部变量的区别
(1)生命周期
成员变量:随着对象的创建而创建,随着对象被回收而消亡
局部变量:随着方法的调用而分配,方法执行完毕消亡
(2)声明的位置
成员变量:类中方法外
局部变量:类中方法内或代码块中
①方法形参
②方法体内
③代码块内
(3)修饰符
成员变量:4种权限修饰符、static、final等
局部变量:没有权限修饰符、只能加final
(4)初始化
成员变量:有默认值
局部变量:没有默认值,必须手动初始化
(5)运行时栈堆的位置
成员变量:堆
局部变量:栈
1、包的作用:
(1)分类管理
(2)避免类重名
(3)可以控制某些类型或成员的可见范围
2、声明包的语法格式:
package 包名;
注意:
(1)必须出现在源文件的首行
(2)一个源文件只能有一个
3、包的命名规范和习惯:
(1)所有的单词小写,单词之间用.隔开
(2)习惯使用公司域名倒置
4、使用其他包的类:
前提:其他包的类的权限修饰符>=缺省
(1)使用类型的全名称
(2)使用import语句,代码中使用简名称
5、import语句
语法:
import 包.类名;
注意:当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。一个使用全名称,一个使用简名称
1、为什么要继承?继承的好处?
(1)提高代码的复用性
(2)延伸代码的扩展
2、如何实现继承?
语法格式:
【修饰符】 class 子类 extends 父类{
}
3、继承的特点
(1)子类会继承父类的所有特征(属性,方法),但是权限私有化的特征不能在子类中之间使用。
(2)子类不会继承父类的构造器(因为父类的构造器用于创建父类的对象)
(3)子类的构造器必须调用父类的构造器(在创建子类对象时,为从父类继承的属性进行初始化用,可以通过父类的构造器中的代码进行初始化)
(4)Java只支持单继承,一个子类只能有一个直接父类
(5)Java支持多层继承,一个子类可以有多个间接父类
(6)一个父类可以拥有多个子类
super关键字:引用父类的xx
(1)super.属性:当子类中创建了和父类同名的属性时,可以使用super.属性名调用父类的属性
(2)super.方法:当子类重写了父类的方法时,可以使用super.方法名调用父类被重写的方法
(3)super()或super(实参列表)
super():表示调用父类的无参实例初始化方法
super(实参列表):表示调用父类的有参实例初始化方法
注意:
(1)如果要写super()或super(实参列表),必须写在子类构造器的首行
(2)如果子类的构造器中没有写:super()或super(实参列表),那么默认会有 super()
(3)如果父类没有无参构造,那么在子类的构造器的首行“必须”写super(实参列表)
1、概念:当子类继承了父类的方法时,父类的方法实现不满足子类的需求,那么子类可以重写父类的方法
2、方法的重写的要求
(1)修饰符>=父类的权限修饰符,其他修饰符:static,final,private不能被重写
(2)返回值类型:
①基本数据类型和void必须相同
②引用数据类型<=父类的返回值类型
(3)方法名:必须相同
(4)形参列表:必须相同
(5)抛出的异常类型<=父类抛出的异常
3、重载和重写的区别
(1)重载:同一个类、方法相同、形参列表不同、与返回值类型、权限修饰符无关;
(2)重写:见上↑方法的重写的要求
语法格式:
【修饰符】 class 类名{
{
非静态代码块
}
}
作用:在创建过程中为对象的属性赋值,协助完成实例初始化过程
执行条件:
(1)每次创建对象都会执行
(2)优先于构造器执行
1、概念描述
注意:
①和②按顺序执行,从上往下
③在①和②的后面
因此一个类有几个构造器,就有几个实例初始化方法。
2、父子类的实例初始化
结论:
(1)执行顺序是先父类实例初始化方法,再子类实例初始化方法
(2)如果子类重写了方法,通过子类对象调用,一定是执行重写过的方法
语法格式:
父类 引用/变量 = 子类的对象;
前提:
(1)继承
(2)方法重写
(3)多态引用
3、现象:
(1)编译看‘左’,运行看‘右’;
(2)编译按照父类类型编译,运行执行子类重写父类的方法,子类独有的方法无法执行
4、应用:
(1)多态数组:形参是父类,实参是子类对象
(2)多态参数:数组元素类型是父类,存储元素类型为子类对象
5、向上转型与向下转型:父子类之间的转换
(1)向上转型:自动类型转换
将子类的对象赋值给父类的变量(多态),无法执行子类独有的方法
(2)向下转型:强制转换。有风险,可能会报ClassCastException异常。
当需要把父类的变量赋值给一个子类的变量时,就需要向下转型。
注意:要想转型成功,必须保证该变量中保存的对象的运行时类型是<=强转的类型
6、instanceof
语法格式:
对象/变量 instanceof 类型
运算结果:true 或 false
作用:用于判断这个对象是否属于这个类型,或者判断是否是这个类型的对象或者这个类型的子类对象
final:最终的
1、用法:
(1)修饰类(外部类和内部类):表示这个类无法被继承,没有子类
(2)修饰变量(成员变量(类变量、实例变量),局部变量):表示这个变量的值不能被更改
(3)修饰方法:表示这个方法不能被重写
注意:如果某个成员变量用final修饰后,也得手动赋值,而且这个值一旦赋完,就不能修改了,即没有set方法
native:本地的
1、用法:只能修饰方法,表示这个方法体不是由Java语言实现的, 但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。
2、JVM内存的管理:
static:静态的
1、用法:
(1)修饰成员变量:一般称之为类变量,静态变量
①静态变量的值是该类所有对象共享的
②静态变量存储在方法区
③静态变量对应的get/set方法也是静态的
④静态变量和局部变量同名时,可以使用类名.静态变量名进行区分
(2)修饰成员方法:一般称之为类方法,静态方法
①静态方法不能被重写
②在本类中其他方法可以直接使用,其他类可以使用类名.方法名进行调用
③在静态方法中,不能出现:this,super等非静态成员
(3) 修饰代码块:静态代码块
(4) 修饰内部类:静态内部类
(5) 静态导入(JDK1.5引入)
语法格式:
【修饰符】 class 类名{
static{
静态代码块;
}
}
作用:帮助完成类初始化过程,可以为类变量进行赋值
1、类初始化:
①静态属性的显示赋值
②静态代码块中的代码
注意:①和②按顺序执行,类初始化方法一个类只有一个
2、类的初始化的执行特点:
(1)一个类只执行一次
(2)如果子类在类初始化时发现父类没有类初始化会先初始化父类
(3)如果既要类初始化又要实例初始化,先类初始化在完成实例初始化
1、按照数据类型分类
(1)基本数据类型:存储数据值
(2)引用数据类型:存储对象的地址值
2、按照声明位置分类
(1)成员变量
(2)局部变量
3、成员变量与局部变量的区别
(1)声明的位置不同:
成员变量:类中方法外
局部变量:类中方法内、代码块内、形参
(2)作用域不同:
成员变量:
①静态变量:在本类中直接使用,在其他类中使用类名.静态变量名使用
②非静态变量:只能被本类中的非静态成员中使用,在其他类中使用对象名.非静态变量名
局部变量:作用域{}内
1、java.lang.Object类是类层次结构的根父类,包括数组对象。
(1)Object中所有声明的方法都会被子类继承,即子类拥有Object中的所有方法
(2)每当对象创建,都会调用Object类的实例初始化方法
(3)Object类型变量、形参、数组,可以存储任意类型的对象
2、Object类的常用方法:
(1)public String toString():
①默认情况下,返回是对象的运行时类型@对象的hashCode值的十六进制
②通常建议重写
③直接使用System.out.println(对象),会默认调用这个对象的toString();
(2)public final Class> getClass():获取当前对象的运行时类型
(3)protected void finalize():当对象被GC确认为要回收的垃圾,在回收之间由GC帮你调用这个方法,而且这个方法只会被调用一次,子类可以选择重写
(4)public int hashCode():返回当前对象的hash值
①如果两个对象的hash值相同,那么这个两个对象不一定相等
②如果两个镀锡的hash值不同,那么这两个对象一定不相等
(5)public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否相等
①默认情况下,equals方法的实现等价于“==”,比较的是对象的地址值
②可以选择重写equals,重写的要求:
1、什么时候会用到抽象方法和抽象类:当声明父类的时候,在父类的某些方法的方法体的实现不能确定时,只能由子类决定。但是父类中又要体现子类的相同特性,即他要包含这个方法,为了统一管理子类的对象,即为多态的应用,那么可以把这样的方法声明为抽象方法,那么一个类包含了抽象方法,这个类就必须是一个抽象类
2、抽象类的语法格式
【修饰符】 abstract class 类名{
}
3、抽象方法的语法格式
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
4、抽象类的特点
(1)抽象方法不能直接实例化,即不能直接new对象
(2)抽象类被子类继承后,子类需要重写所有的抽象方法,否则这个子类也是一个抽象类
(3)抽象类也有构造器,这个构造器是用于给子类实例初始化时调用的,不能创建抽象类自己的对象
(4)抽象类也可以没有抽象方法,目的是不让你创建对象,让你创建子类对象
(5)抽象类的变量和它子类的对象构成多态引用
5、不能和abstract一起使用的修饰符
(1)final
(2)static
(3)native
(4)private
1、接口的概念:接口是一种标准。注意关注行为标准,面向对象的开发原则中有一条:面向接口编程
2、接口的声明格式
【修饰符】 interface 接口名{
接口的成员列表;
}
3、类实现接口的格式
【修饰符】 class 实现类 implements 父接口们{
}
【修饰符】 class 实现类 extends 父类 implements 父接口们{
}
4、接口继承接口的格式
【修饰符】 interface 接口 extends 父接口们{
接口的成员列表;
}
5、接口的特点
(1)不能直接实例化,即不能直接new对象
(2)只能创建接口的实现类对象,那么接口和它实现类对象之间构成多态引用
(3)实现类在实现接口时,必须重写所有的抽象方法,否则这个类也是抽象类
(4)Java规定类与类之间只能单继承,类与接口之间可以多实现,即一个类可以有多个父接口
(5)Java支持接口与接口之间的多继承
6、接口的成员
JDK1.8之前:
(1)全局的静态常量:public static fianl 修饰的变量(这些修饰符可以省略)
(2)公共的抽象方法:public abstract 修饰的方法(这些修饰符可以省略)
JDK1.8之后:
(3)公共的静态方法:public static 修饰的方法
(4)公共的默认方法:public default 修饰的方法
7、默认方法冲突问题
(1) 当一个实现类同时实现了两个或多个接口,这个多个接口的默认方法的签名相同。
方案一:选择保留其中一个
接口名.super.方法名(【实参列表】);
方案二:完全重写
(2)当一个实现类同时继承父类,又实现接口,父类中有一个方法与接口的默认方法签名相同
方案一:默认方案,保留父类的
方案二:选择保留接口的
接口名.super.方法名(【实参列表】);
方案三:完全重写
8、常用的接口
(1)java.lang.Comparable接口:自然排序
抽象方法:int compareTo(Object obj)
(2)java.util.Comparator接口:定制排序
抽象方法:int compare(Object o1,Object o2)
1、内部类的概念:声明在一个类里面的类就是内部类
2、内部类的4种形式:
(1)静态内部类
(2)非静态内部类
(3)匿名内部类
(4)非匿名内部类
1、语法格式
【修饰符】 class 外部类{
【其他修饰符】 static class 内部类 【extends 静态内部类自己的父类】 【implements 静态内部类的父接口们】{
}
外部类的其他成员列表;
}
2、使用注意事项
(1)包含成员的要求:可以包含类的所有成员
(2)修饰符的要求:4种权限修饰符、abstrac、final
(3)使用外部类的成员列表的要求:只能使用外部类的静态成员
(4)在外部类中使用内部类的成员要求:正常使用
(5)在外部类外面使用内部类成员的要求:
①如果使用的是静态内部类的静态成员:外部类.静态内部类名.静态成员
②如果使用的是静态内部类的非静态成员:
1、语法格式
【修饰符】 class 外部类名 【extends 父类 implements 父接口们】{
【修饰符】 class 非静态内部类【extends 非静态内部类自己的父类】 【implements 非静态内部类的父接口们】{
非静态内部类的成员列表;
}
外部类的成员列表
}
2、 使用注意事项
(1)包含的成员要求:不允许出现静态成员
(2)修饰符要求:4中权限修饰符、abstract、final
(3)使用外部类成员的要求:都可以使用
(4)外部类中使用非静态内部类成员的要求:外部类的静态成员不能使用非静态内部类成员
(5)在外部类的外面使用非静态内部类的要求
①方式一:使用创建内部类对象
1、语法格式
【修饰符】 class 外部类名 【extends 父类 implements 父接口们】{
【修饰符】 返回值类型 方法名(【参数列表】){
【修饰符】 class 局部内部类名 【extends 局部内部类的父类 implements 局部内部类的父接口们】{
局部内部类的成员;
}
}
类的其他成员;
}
2、使用注意事项
(1)包含的成员要求:不允许出现静态成员
(2)修饰符要求:没有权限修饰符,abstract,final
(3)使用外部类的成员等上是否有要求:
①外部类的静态成员:随便使用
②外部类的非静态成员:看所在方法是否是静态的
③所在方法的局部变量:必须使用final修饰
(4)外部类中使用局部内部类成员的要求:有作用域
(5)外部类外使用局部内部类成员的要求:无法使用,外部类不可见
(6)字节码文件形式:外部类名$编号局部内部类名.class
3、示例代码
class Outer{
private static int i = 10;
private int j = 20;
public void outMethod(){
class Inner{
public void method(){
//...
System.out.println(i);//可以
System.out.println(j);//可以
}
}
Inner in = new Inner();
in.method();
}
public static void outTest(){
final int k = 30;
class Inner{
public void method(){
//...
System.out.println(i);//可以
System.out.println(j);//不可以
System.out.println(k);//可以
}
}
Inner in = new Inner();
in.method();
}
}
1、语法格式:
//在匿名子类中调用父类的无参构造
new 父类(){
内部类的成员列表
}
//在匿名子类中调用父类的有参构造
new 父类(实参列表){
内部类的成员列表
}
//接口没有构造器,那么这里表示匿名子类调用自己的无参构造,调用默认父类Object的无参构造
new 父接口名(){
}
2、匿名内部类、匿名对象的区别?
System.out.println(new Student("张三"));//匿名对象
Student stu = new Student("张三");//这个对象有名字,stu
//既有匿名内部类,又是一个匿名的对象
new Object(){
public void test(){
.....
}
}.test();
//这个匿名内部类的对象,使用obj这个名字引用它,既对象有名字,但是这个Object的子类没有名字
Object obj = new Object(){
public void test(){
.....
}
};
3、使用的形式
(1)示例代码:继承式
abstract class Father{
public abstract void test();
}
class Test{
public static void main(String[] args){
//用父类与匿名内部类的对象构成多态引用
Father f = new Father(){
public void test(){
System.out.println("用匿名内部类继承了Father这个抽象类,重写了test抽象方法")
}
};
f.test();
}
}
(2)示例代码:实现式
interface Flyable{
void fly();
}
class Test{
public static void main(String[] args){
//用父接口与匿名内部类的对象构成了多态引用
Flyable f = new Flyable(){
public void fly(){
System.out.println("用匿名内部类实现了Flyable这个接口,重写了抽象方法");
}
};
f.fly();
}
}
(3)示例代码:用匿名内部类的匿名对象直接调用方法
new Object(){
public void test(){
System.out.println("用匿名内部类的匿名对象直接调用方法")
}
}.test();
(4)示例代码:用匿名内部类的匿名对象直接作为实参
Student[] all = new Student[3];
all[0] = new Student("张三",23);
all[1] = new Student("李四",22);
all[2] = new Student("王五",20);
//用匿名内部类的匿名对象直接作为实参
//这个匿名内部类实现了Comparator接口
//这个匿名内部类的对象,是定制比较器的对象
Arrays.sort(all, new Comparator(){
public int compare(Obeject o1, Object o2){
Student s1 = (Student)o1;
Student s2 = (Student)o2;
return s1.getAge() - s2.getAge();
}
});
1、概念:枚举(JDK1.5引入),枚举类型的对象是有限固定的几个常量对象
2、语法格式:
//形式一:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{
常量对象列表
}
//形式二:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{
常量对象列表;
其他成员列表;
}
说明:常量对象列表必须在枚举类型的首行
3、在其他类如何获取枚举的常量对象
//获取一个常量对象
枚举类型名.常量对象名;
//获取一个常量对象
枚举类型名.valueOf("常量名");
//获取所有常量对象
枚举类型名[] all = 枚举类型名.values();
4、枚举类型的特点
(1)枚举类型由一个公共基本的父类java.lang.Enum类型,所以不能继承其他类型
(2)枚举的构造器必须私有化
(3)枚举可以实现接口
instanceof MyRunnable{
void run();
}
enum gender implements MyRunnable{
NAN,NV;
public void run(){
//...
}
}
//或者
enum Gender implements MyRunnable{
NAN{
public void run(){
//...
}
},NV{
public void run(){
//...
}
};
}
5、父类java.lang.Enum类型
(1)构造器:protected Enum(String name, int ordinal):由编译器调用
(2)public final String name():枚举常量的名称
(3)public final int ordinal():枚举常量的序数
(4)public String toString():枚举常量的名称
(5)public final int compareTo(E o):按照常量对象的顺序比较,负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。
1、注解:它是代码级别的注释
2、标记符号:@
3、系统预定义的三个最基本的注解:
(1)@Override:表示某个方法是重写的方法
(2)@SuppressWarnings(xx):抑制警告
(3)@Deprecated:表示xx已过时
4、和文档注释相关的注解
(1)文档注释
/**
文档注释
*/
(2)常见的文档注释
@author:作者
@since:从xx版本加入的
@see:另请参考
@param:形参
@return:返回值
@throws或@exception:异常
5、JUnit相关的几个注解
@Test:单元测试方法,这个方法需要是public void xxx(){}
@Before:在每个单元测试之前执行,这个方法需要是:public void xxx(){}
@After:在每个单元测试之后执行,这个方法需要是:public void xxx(){}
@BeforeClass:表示在类初始化阶段执行,而且只执行一次,这个方法需要是:public static void xxx(){}
@AfterClass:表示在类卸载阶段执行,而且只执行一次,这个方法需要是:public static void xxx(){}
6、元注解(注解的注解)
(1)@Target(xx):用于标记注解使用xx位置(例如:TYPE、METHOD、FIELD等)
(2)@Retention(xx):用于标记注解可以滞留到xx阶段(例如:SOURCE、CLASS、RUNTIME)
注意:唯有RUNTIME阶段的注解才能被反射读取到
(3)@Documentd:用它标记的注解可以读取到API中
(4)@Inherited:用它标记的注解可以被子类继承
7、自定义注解
(1)元注解的语法格式:
@元注解
【修饰符】 @interface 注解名{
}
@元注解
【修饰符】 @interface 注解名{
配置参数列表
}
(2)配置参数的语法格式:
数据类型 配置参数名();
或
数据类型 配置参数名() default 默认值;
(3)关于配置参数:
①配置参数的类型有要求:八种基本数据类型、String、枚举、Class类型、注解、它们的数组
②如果自定义注解声明了配置参数,那么在使用这个注解时必须为配置参数赋值,除非它有默认值
@自定义注解名(配置参数名1=值,配置参数名2=值。。。)
//如果配置参数类型是数组,那么赋值时,可以用{}表示数组
@自定义注解名(配置参数名1={值},配置参数名2=值。。。)
③如果配置参数只有一个,并且名称是value,那么赋值时可以省略value=
④如果读取这个注解时,要获取配置参数的值的话,可以当成方法一样来访问
自定义注解对象.配置参数();