1.基础概述
1. 什么是跨平台?
- 由Java编写的程序可以在不同的操作系统上运行:一次编写,多处运行
2. 原理
- 编译之后的字节码文件和平台无关,需要在不同的操作系统上安装一个对应版本的虚拟机(JVM)
1.
计算机基础知识
1.
二进制
-
计算机中的数据不同于人们生活中的数据,人们生活采用十进制数,而计算机中全部采用二进制数表示,它只包含0、1两个数,逢二进一,1+1=10.每一个0或者每一个1,叫做一个bit(比特,位)
-
十进制数据转成二进制数据:使用除以2获取余数的方式
-
二进制数据转成十进制数据
2.
字节
-
位(bit):一个数字0或者一个数字1,代表一位,我们又称之为 “比特位”,通常用小写的字母b表示
-
字节 ( Byte): 每逢8位是一个字节,这是数据存储的最小单位,通常用大些字母 “B” 表示
1Byte=8bit
1KB=1024Byte
1MB=1024KB
1GB=1024MB
1TB=1024GB
3.
命令提示符
-
d: 回车 盘符切换
-
dir(directory):列出当前目录下的文件以及文件夹
-
cd (change directory)改变指定目录(进入指定目录)
-
cd 文件夹\文件夹2\文件夹3
-
cd… : 退回到上一级目录
-
cd \:
退回到根目录 -
cls : (clear screen)清屏
-
exit : 退出dos命令行
这里面的了解即可,上面的要
md (make directory) : 创建目录
rd (remove directory): 删除目录
4 .
什么是JDK,JRE
-
JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。其中的开发工具:编译工具(javac.exe) 打包工具(jar.exe)等
-
JRE(Java Runtime Environment Java运行环境)
包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
5.第一个HelloWorld
-
步骤:
-
将 Java 代码编写到扩展名为 .java 的文件中。
-
通过 javac 命令对该 java 文件进行编译。
-
通过 java 命令对生成的 class 文件 [字节码文件] 进行运行。
6. 注释
-
单行注释 //注释文字
-
多行注释 /* 注释文字 */
-
文档注释(java特有)
格式:/**
* @author 指定java程序的作者
*@version 指定源文件的版本
*@param 方法的参数说明信息
*/
2.
基本语法
1. 关键字和标识符
-
关键字:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
-
所谓保留字,即它们在Java现有版本中没有特殊含义,以后版本可能会作为有特殊含义的词
-
①.完全小写的纯字母
-
②.在增强版的记事本中有特殊颜色
-
标识符:给类、变量、方法、接口等起名字用的符号
[掌握]
→
命名的规则:(一定要遵守,不遵守就会报编译的错误)
-
①.由26个英文字母大小写,0-9 ,_或 $ 组成
-
②.数字不可以开头
-
③.不可以使用关键字和保留字,但能包含关键字和保留字
-
④.Java中严格区分大小写,长度无限制
-
⑤.标识符不能包含空格
2.Java中的名称命名规范 [掌握]
-
包名:多单词组成时所有字母都小写:xxxyyyzzz
-
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
-
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
-
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
3.
常量
1.常量的定义
- 在程序运行期间,固定不定的量
2. 常量的分类:
-
分为自定义常量和 字面值常量 [ 以下的6种属于这种类型 ]:
-
①.字符串常量:凡是用双引号引起来的部分,叫做字符串常量。例如:“abc”、“hello”、“123”
-
②.整数常量:直接写上的数字,没有小数点,例如 100、200、0
-
③.浮点数常量:直接写上的数字,有小数点,例如:2.5,-3.14
-
④.字符常量:凡是用单引号引起来的单个字符,只能单个字母、数字、中文=,就做字符常量。例如:‘A’、‘b’ 、‘中’
-
⑤.布尔常量:只有两种取值: true 和 false
-
⑥.空常量:null。代表没有任何数据,不能直接输出
-
null值不能直接输出,在引用类型中,被赋值了null表示引用类型不存在了
@Test
public void fun5(){
//字符串常量
System.out.println("ABC");
System.out.println("");//字符串两个双引号中间的内容为空
System.out.println("XYZ");
//整数常量
System.out.println(30);
System.out.println(-500);
//浮点数常量(小数)
System.out.println(3.14);
System.out.println(-2.5);
//字符常量
System.out.println('A');
System.out.println('6');
//System.out.println('');两个单引号中间必须有且仅有一个字符,没有不行
//System.out.println('AB');有两个不行
//布尔常量
System.out.println(true);
System.out.println(false);
//空常量。空常量不能直接用来打印输出
//System.out.println(null);
}
4 .
变量
1. 变量的定义
- 在一定的范围内可以发生改变的量
2.
变量的定义
- 定义变量的格式:
数据类型 变量名 = 初始化值
3.
变量的分类 →
按数据类型
-
整数类型默认是 int
-
浮点类型默认是 double
-
布尔类型默认是 false
- 整数类型:byte、short、int、long
→
java的整型常量默认为 int 型,声明long型常量须后加‘l’或‘L’
-
浮点类型:float、double
-
计算机是无法运算小数的,只能无限接近这个小数
→
Java 的浮点型常量默认为double型,声明float型常量,须后加‘f’或‘F’
- 字符串:char 只能表示一个字符=2个字节
- boolean类型数据只允许取值true和false,无null [ 内存占用1字节 ]
4.
变量的注意事项
-
①. 名字不能重复
-
②. 变量未赋值,不能使用
-
③. long类型的变量定义的时候,为了防止整数过大,后面要加L或l
-
long l=10000000000 (×)
-
④. floa类型的变量定义的时候,为了防止不兼容的类型,后面要加F或f
-
float f=13.14 (×)
int a;//不报错
int a;
System.out.println(a);//报错
5.
变量的自动类型转换和强制类型转换
1.
自动类型转换
自动类型转换:
容量小的类型自动转换为容量大的数据类型。数据类型按容量大小排序为:
@Test
public void fun1(){
int i1=12;
short s2=2;
int i2=i1+s2;
System.out.println(i2);//14
float f1=12.3F;
float f2=f1=i2;
System.out.println(f2);
long l=12;//不加f默认12是int;int型赋值给long可以转换
}
2.
强制数据类型
-
强制类型转换:
容量大转换为容量小的,要使用强制类型转换符
强制类型转换的问题:导致精度的损失 -
通常,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型
-
格式:
范围小的类型 范围小的变量名=(范围小的类型)原本范围大的数据
-
如: String a = “43”; int i = Integer.parseInt(a);
-
boolean类型不可以转换为其它的数据类型。
int num=(int) 100L;
System.out.println(num);
3.
数据类型转换 [ 注意事项 ]
-
①. 强制类型转换一般不推荐使用,因为有可能发生精度损失、数据溢出
-
②. byte、short、char这三个类型都可以发生数学运算,例如加法 “+”
-
③. byte、short、char这三个类型在运算的时候,都会被首先提升为int类型,然后再进行计算
-
④. boolean类型不能发生数据类型转换
char c1='A';//这是一个字符型变量,里面是大写字母A
System.out.println(c1+1);//66,也就是大写字母A被当成65进行处理
//计算机的底层会用一个数字(二进制) 来代表字符A,就是65
//一旦char类型进行了数据运算,那么字符就会按照一定的规则翻译成一个数字
byte b1=40;//注意!右侧的数值大小不能超过左侧的类型范围
byte b2=50;
//byte + byte---> int + int =int
int result1=b1+b2;
System.out.println(result1);
short s1=60;
//byte + short -->int + int
int result2=b1+s1;
//int 强制转换为short: 注意必须保证逻辑上真实大小
//本来就没有超过short范围,否则会发生数据溢出
//short result3=(short)(b1+s1);
6. ASCII编码表 和 unicode
-
ASCII:美国信息交换标准代码
-
Unicode码表:万国表,也是数字和字符的对照关系,开头0-127部分和ASCII 完全一样,但是从128开始包含有更多字符
ASCII:记住如下
48 代表0
65 代表A
97 代表小写a
7.
运算符
- 运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
1.
算术运算符:+ - * / % ++ – +
-
/
和%
的区别:两个数据做除法,/
取结果的商,%
取结果的余数 -
整数除以整数,要想得到小数,必须有浮点数参与运算
double d=3.4;
System.out.println(3.6/5);//0.72
System.out.println(3.0/2);//1.5
-
i++或++i ①.单独使用的时候,++或-- 无论是在变量的前边还是后边,结果都是一样的
-
②. 参与操作的时候,如果放在变量的后边,先拿变量参与操作,后拿变量做++或–;参与操作的时候,如果放在变量的前边,先拿变量做++或–,后拿变量参与操作;
int i=1,a=0;
i++ 先赋值再运算,例如 a=i++,先运算a=i,后运算i=i+1,所以结果是a==1
++i 先运算再赋值,例如 a=++i,先运算i=i+1,后运算a=i,所以结果是a==2
int a=5;
System.out.println(++a);//6
System.out.println(a++);//6
/ 除法运算
% 取模运算,两个数字相除取余数
//% : 符号左边的数字如果大于右边,那么先进行除法运算,求余数
System.out.println(7%6); //1
System.out.println(8%6); //2
//% :符号左边的数字如果是右边的倍数的时候,余数是0
System.out.println(12%6); //0
System.out.println(24%6); //0
// % 符号左边的数字如果<右边,那么余数就是左边
System.out.println(4%6); // 4
System.out.println(5%6); // 5
System.out.println(3%6); // 3
-
四则运算当中的加号 " + " 有常见的三种用法
-
①. 对于数值来说,那就是加法
-
②. 对于字符char类型来说,在计算之前,char会被提升为int,然后再计算
[ 重点 ]
-
③. 对于字符串String(首字母大写,并不是关键字),加号代表字符串链接串。任何数据类型和字符串进行连接的时候,结果都会变成字符串
[ 重点 ]
-
④. 一旦运算当中有不同的类型的数据,那么结果将会使数据类型范围大的那种
[ 重点 ]
//小的会提升为大的,在进行运算
int a=10;
double b=10.0;
double result=a+b;
//字符首先会被提升为int,在进行加法运算
System.out.println('你'+0);//20320
System.out.println('最'+0);//26368
System.out.println('近'+0);//36817
System.out.println('还'+0);//36824
System.out.println('好'+0);//22909
System.out.println('吗'+0);//21527
-
当 " + " 操作中出现字符串时,这个 " + "是字符串连接符,而不是算术运算
-
在 " + " 操作中,如果 出现了字符串,就是连接运算符,否则就是算术运算。当连续进行 " + " 操作时,
从左到右逐个执行
System.out.println(1 + 99 + "年黑马"); // 输出:100年黑马
System.out.println(1 + 2 + "itheima" + 3 + 4); // 输出:3itheima34
// 可以使用小括号改变运算的优先级
System.out.println(1 + 2 + "itheima" + (3 + 4)); // 输出:3itheima7
int x=10;
int y=20;
// 11+20
int result=++x + y --;
System.out.println(result);//31
System.out.println(x);//11
System.out.println(y);//19
2.
赋值运算符
-
基本赋值运算符:就是一个等号 ‘=’, 代表将右侧的数据交给左侧的变量
-
复合赋值运算符
-
+=: a+=1 相当于 a=a+1;
-
-= *= /= %=
-
注意事项:
-
①.只有变量才能使用赋值运算符,常量不能进行赋值
-
②. 复合赋值运算符其中隐含了一个强制类型的转换
int i= 12;
i = i * 5;
i *= 5;//与上一行代码同样的意思
【特别地】
short s = 10;
s = s + 5;//报编译的异常
s = (short)(s + 5);
s += 5;//s = s + 5,但是结果不会改变s的数据类型。
3.
比较 [ 关系 ]运算符:[ == > < >= <= ]
-
①.比较运算符的结果一定是一个
boolean
值,成立就是true,不成立就是false -
②.如果进行多次判断,不能连着写
-
数学当中的写法,例如:3<x<6, 程序当中不允许这种写法
4.
逻辑运算符
1. &、|、!、^
-
&: 有:false则false
-
|:有true则true
-
^
相同为false,不同为true
int a=10;
int b=20;
int c=30;
System.out.println((a>b)^(a>c));// false ^ false =false
System.out.println((a<b)^(a>c));// ture ^ false = true
System.out.println((a>b)^(a<c));// false ^ true =true
System.out.println((a<b)^(a<c));// true ^ true =false
- 逻辑运算符用于连接布尔型表达式,在Java中不可以写成3<x<6,应该写成x>3 & x<6
2.
短路逻辑运算符 [ 重点 ]
-
“&”和“&&”的区别:
-
单&时,左边无论真假,右边都进行运算;
-
双&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算
-
“ | ”和“|| ”的区别同理,||表示:当左边为真,右边不参与运算。
-
异或( ^ )与或( | )的不同之处是:当左右都为true时,结果为false。
5.
三元运算符:(条件表达式)? 表达式1 : 表达式2;
-
①. 既然是运算符,一定会返回一个结果,并且结果的数据类型与表达式1,2 的类型一致
-
②. 表达式 1 与表达式 2 的数据类型一致。
-
③. 使用三元运算符的,一定可以转换为if-else。反之不一定成立。
注意:
-
①.必须同时满足表达式A和表达式B都符合左侧数据类型的要求
-
②.三元运算符的结果必须使用
int a=10;
int b=20;
//判断a>b是否成立,如果成立将a的值赋值给max;
//如果成立将a的值赋值给max;如果不成立将b的值赋值给max
int max=a>b?a:b;
System.out.println(max);
System.out.println(a>b?a:b);//正确写法
//3>4?3:4;错误写法
程序流程控制
8.
程序流程控制
-
顺序结构:程序从上到下逐行地执行,中间没有任何判断和跳转
-
分支结构:根据条件,选择性地执行某段代码,有if…else和switch两种分支语句
-
循环结构
-
根据循环条件,重复性的执行某段代码
-
有while、do…while、for三种循环语句
-
注:JDK1.5提供了foreach循环,方便的遍历集合、数组元素
1.
顺序结构
Java中定义成员变量时采用合法的前向引用。如:
public class Test{
int num1 = 12;
int num2 = num1 + 2;
}
错误形式:
public class Test{
int num2 = num1 + 2;
int num1 = 12;
}
2.
分支结构
1. 条件判断 : if
①if(条件表达式){ }
②if(条件表达式){
//执行的语句1;
}else{
//执行的语句2;
}
③if(条件表达式1){
//执行的语句1;
}else if(条件表达式2){
//执行的语句2;
}else if( 条件表达式3){
//执行的语句3;
}...
}else{
//执行的语句;
}
-
注意:
-
①. 一旦满足某个条件表达式,则进入其执行语句块执行,执行完毕,跳出当前的条件判断结构,不会执行其以下的条件结构语句。
-
②. 如果诸多个条件表达式之间为“互斥”关系,多个结构可以上下调换顺序
-
③. 如果诸多个条件表达式之间为“包含”关系,要求条件表达式范围小的写在范围大的上面。
2.
选择结构
- 执行的流程
[ 掌握 ]
:首先会先计算出switch表达式中的值,然后和第一个case进行匹配。如果匹配成功,就执行语句体,接着就遇到break,这个时候整个switch 全部结束!!如果所有的case都不匹配的情况下,才会执行 default
switch(变量){
case 值1:
//break;
case 值2:
//break;
...
default:
break;
}
-
注意:
-
①. 变量可以为如下的数据类型:byte short int char 枚举 String
[ 掌握 ]
-
②. 多个case后面的数值不可以重复,最后一个break可以省略,但是通常不要省略
-
③. switch语句格式可以很灵活:前后顺序可以颠倒,而且break语句还可以省略。“匹配哪一个case就从哪一个位置向下执行,直到遇到了break或整体结束为止”
int num=1;
switch(num){
case 1:
System.out.println("今天是星期一");
break;
case 2:
System.out.println("今天是星期二");
break;
default:
System.out.println("随时");
break;
}
- case穿透
[ 掌握 ]
:在switch语句中,如果case控制的语句体后不写break,将出现穿透现象,在不判断下一个case值的情况下,向下运行,直到遇到break,或者整体switch语句结束
//如果这个时候输入一个月份是1,会运行 冬季
Scanner sc=new Scanner(System.in);
System.out.println("请输入月份");
int month=sc.nextInt();
switch (month){
case 1:
case 2:
case 12:
System.out.println("冬季");
break;
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
default:
System.out.println("你输入的月份有误!");
break;
}
@Test
public void fun2() {
int x = 3;
int y = 2;
/*
①. x=3 会在case中查找有没有为3的,这里没有,则运行default中的语句
②. y++ 后 y的值变成3,这时 default 中没有 break 语句,所以会发生穿透现象
③. 在case 2 中 y=4 ;
④. 之后遇到了 break ,循环结束 y=4
* */
switch (x) {
case 1:
y++;
default:
y++;
case 2:
y++;
break;
case 5:
y++;
break;
}
System.out.println(y);//4
}
@Test
public void fun1() {
int x = 3;
int y = 2;
/*
①. x=3 会在case中查找有没有为3的,这里没有,则运行default中的语句
②. y++ 后 y的值变成3,这时 default 中没有 break 语句,所以会发生穿透现象
③. 在case 1 中 y=4 ; 在 case 2 中 y=5
④. 之后遇到了 switch 中结束的 括号,循环结束 y=5
* */
switch (x) {
default:
y++;
case 1:
y++;
case 2:
y++;
}
System.out.println(y);//5
}
3.
循环结构
1.
循环结构的分类
1. for循环
表达式:for(初始化;布尔表达式;更新){
程序代码.......
}
2. do--while
do{
}while()
3. while(){ }
4.死循环:永远停不下来的循环,叫做死循环
for( ; ; ){}
while(ture){
循环体;
}
do{
}while(true)
int i=0;
for(;i<5;){
System.out.println("了解这里就可以了");
i++;
}
int j=0;
while(j<5){
System.out.println("了解这里就可以了");
j++;
}
2.
获取任意数字位的数字
- 个位:i %10; 十位: i / 10 % 10 ; 百位:i /10/10%10
//获取3这个数字
int x=1234567/10000%10;
System.out.println(x);
水仙花:个位的 ³ +十位的 ³ +百位的 ³ =这个数字 称为水仙花数
@Test
public void fun6() {
int count = 0;
for (int i = 100; i < 1000; i++) {
//在计算之前获取三位数中每个位上的值
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 10 / 10 % 10;
//判断条件水仙花数的过程中,满足条件不再输出,更改为修改count的值,使count+1
if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
count++;
}
}
System.out.println("水仙花数是:" + count + "个");
}
3. 三种循环的区别
-
①. 如果条件判断从来没有满足过,那么for循环和while循环将会执行0次,但是do-wh ile循环会执行至少一次
-
②. for循环的变量在小括号当中定义,只有循环内部才可以使用。while循环和do-whi le循环初始化语句本来就在外面,所以出来循环之后还可以继续使用
4.
break 和 continue
-
①.break
-
使用switch-case结构或者循环结构中
-
在循环结构中,一旦执行到break,就跳出当前循环。
-
②.continue
-
使用在循环结构中
-
一旦执行到continue,就跳出当次循环
//break:用在循环中,基于条件控制,终止循环内容的执行,也就是说结束当前整个循环
//continue:用在循环中,基于条件控制的,跳过某次循环体内容的执行,继续下一次的执行
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
//break; //123
continue; //123567910
}
System.out.print(i);
}
5.循环嵌套
- 外循环执行一次,内循环控制分钟的范围
//外循环执行一次,内循环控制分钟的范围
for (int hour = 0; hour < 24; hour++) {
for(int minute=0;minute<60;minute++){
System.out.println(hour+"时"+minute+"分");
}
}
API
9.
API
- 应用程序编程接口 [ 产品说说明 ]
1>.
Scanner 类
1. Scanner类的功能:
- 可以实现键盘输入数据,到程序当中
2. 引用类型的一般使用步骤:
-
①. 导包
-
import 包路径.类名称; 如果需要使用的目标类,和当前类位于同一个包下,则可以省略导包语句不写。 只有java.lang包下的内容不需要导包,其他的包都需要使用import语句
-
②. 创建
-
类名称 对象名=new 类名称();
-
③ .使用
-
对象名.成员方法名();
-
获取键盘输入的一个int数字:
sc.nextInt();
-
获取键盘输入的一个字符串:
sc.next();[ 是以空格结束的 ]
-
sc.nextLine()
:是以/r/n结束的
import java.util.Scanner;//1.导包
public class Demo1Scanner {
public static void main(String[] args) {
//2.创建
//备注:System.in :从键盘进行输入
Scanner sc=new Scanner(System.in);
System.out.println("请从键盘输入一个数字");
int num1=sc.nextInt();
System.out.println("请从键盘输入一个数字:"+num1);
//快捷键 ctrl+alt+v
String str=sc.next();
System.out.println("输入的字符串是"+str);
}
}
//求出键盘输入的最大值
import java.util.Scanner;//1.导包
public class Demo1Scanner {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入第一个数字: ");
int num1=sc.nextInt();
System.out.println("请输入第二个数字: ");
int num2=sc.nextInt();
System.out.println("请输入第三个数字: ");
int num3=sc.nextInt();
//首先得到前两个数字当中的最大值
int temp=num1>num2?num1:num2;
int max=temp>num3? temp:num3;
System.out.println("最大值是:"+max);
}
}
2>.
Random 类
-
①. 导包 :import java.util.Random;
-
②. 创建:Random random=new Random();
-
③. 使用:
-
int num=r.nextInt():
获取一个随机的int 数字 [ 范围是int所有的范围,有正负两种 ] -
int num2=r.nextInt(10):
获取一个随机的int数字 [ 参数代表了范围,左闭右开区间 ],实际上代表的含义是[0,10) ,也就是0-9;
public static void main(String[] args) {
Random r=new Random();
int num=r.nextInt();
System.out.println("随机数是:"+num);
int num2=r.nextInt(3);
System.out.println("随机数0-3之间的是:"+num2);
}
//根据int变量n的值,来获取随机数字,范围是:[1,n]
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个随机数:");
int n=sc.nextInt();
Random r=new Random();
int num=r.nextInt(n)+1;// n-1+1 后面的1就是前面的取值[1
System.out.println("生成的随机数1-n是:"+num);
//猜数字小游戏
/*
1.首先需要产生一个随机数字,并且一旦产生不再变化,用Random的nextInt()方法
2.需要键盘输入,所以用到了Scanner
3.获取键盘输入的数字,用Scanner当中的nextInt()方法
4.已经得到了两个数字,判断(if)一下:
如果太大了,提示太大,并且重试
如果太小了,提示太小,并且重试
如果猜中了,游戏结束
5.重试就是再来一次,循环次数不确定,用while(true)
* */
public static void main(String[] args) {
Random r=new Random();
int randomNum=r.nextInt(100)+1;//[1-100]
System.out.println("随机生成的这个数字是:"+randomNum);
Scanner sc=new Scanner(System.in);
System.out.println("请猜下这个数是");
while(true){
int gussNum=sc.nextInt();//键盘输入猜测的数字
if(gussNum>randomNum){
System.out.println("太大了,请重试");
}else if(gussNum<randomNum){
System.out.println("太小了,请重试");
}else{
System.out.println("恭喜你,猜中啦!");
break;
}
}
}
3>.
Arrays [ static ]
-
java.util.Arrays:是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作,构造方法用private修饰
-
public static String toString(数组)
:将参数数组变成字符串(按默认格式:[ 元素1 , 元素2 ,元素3 ]) -
public static void sort( 数组 )
:按照默认升序(从小到大)对数组的元素进行排序 -
备注:
①.如果是数值,sort默认按照升序从小到大 -
②. 如果是字符串,sort默认按照字母升序
-
③. 如果是自定义类型,那么这个自定义的类需要有Comparable或 Comparator 接口的支持
int []intArray={10,20,30};
String instr= Arrays.toString(intArray);
System.out.println(instr);//[10, 20, 30]
int [] array1={1,2,66,99,77,55,21};
Arrays.sort(array1);
System.out.println(Arrays.toString(array1));//[1, 2, 21, 55, 66, 77, 99]
//需求:将一个随机字符串中得到所有字符升序排序,并倒序打印
//自定义随机的字符串
String line="adxcvewgjgn";
//转换成字符数组
char[]ch=line.toCharArray();
//升序排序
Arrays.sort(ch);
//倒序打印
System.out.print("升序后倒序打印如下:");
for(int i=ch.length-1;i>=0;i--){
System.out.print(ch[i]+"\t");
}
4>.
Math [ 静态 ]
-
注意:Math 类有构造方法,构造方法被私有化了
-
public static double abs(double num):获取绝对值,有多种取整
-
public static double ceil(double num):向上取整
-
public static double floor(double num):向下取整
-
public static long round(double num):
四舍五入,不带小数点 [ 常用 ] -
Math.PI:
代表近似的圆周率常量
public static void main(String[] args) {
//获取绝对值
double dou=Math.abs(-3.14);
System.out.println(dou);//3.14
//向上取整
System.out.println(Math.ceil(3.8));//4.0
//向下取整
System.out.println(Math.floor(3.9));//3.0
//四舍五入
System.out.println(Math.round(3.8));//4
}
5>.
System
1. System类的概述
- System 类包含了一些有用的字段和方法,它不能被实例化
2.
方法
-
public static void exit (int status)
:终止当前运行的Java虚拟机,该参数作为状态代码,按照惯例,非零状态码标识异常终止。 -
public static long currentTimeMillis()
:返回的是当前时间,以毫秒为单位:从1970年到现在的毫秒值
System.exit(0);//非0状态是异常终止,退出了Jvm,后面那句话不执行
System.out.println("1111111");
long start=System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
System.out.println("*");
}
long end=System.currentTimeMillis();//获取当前时间的毫秒值
System.out.println("程序所产生的时间是"+end);
6>.
Object
1.
toString( )方法
-
①. 当我们打印一个对象的引用时,实际上默认调用的就是这个对象的toString()方法
-
②. 当我们打印的对象所在类没有重写Object中的toString()方法时,那么调用的就是Object中定义的toString()方法。返回此对象所在的类及对应的堆空间对象实体的首地址值
-
③. 当我们打印的对象所在的类重写了toString()方法时,调用的就是我们自己重写的toString()方法,建议所有子类重写此方法:toString()
-
注意:
像String类、包装类、File类、Date类等,已经实现了Object类中toString()方法
//重点是看Object中toString() 方法的源码
public class ObjectDemo {
public static void main(String[] args) {
Student s = new Student();
s.setName("林青霞");
s.setAge(30);
//输出语句直接打印对象名,在源码的底层会调用该类的toString()
//因此,我们曾经看到的都是对象的地址
System.out.println(s); //com.itheima_01.Student@3f3afe78
System.out.println(s.toString()); //com.itheima_01.Student@3f3afe78
/*
public void println(Object x) { //x = s;
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
public static String valueOf(Object obj) { //obj = x;
return (obj == null) ? "null" : obj.toString();
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
}
}
2.
Object类中的equals()方法
2.1.
==
-
①. 基本数据类型:根据基本数据类型的值判断是否相等。相等返回true,反之返回false
-
注:
两端数据类型可以不同,在不同的情况下,也可以返回true -
②. 引用数据类型:比较引用类型变量的地址值是否相等
2.2.
equals()
-
①. 只能处理引用类型的变量
-
②. 比较对象是否相等。默认比较地址,重写后比较的是内容
// ==
int x = 10;
int y = 10;
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(x == y); // 输出true
// 输出false,str1和str2是两个不同的引用类型变量,指向两个不同的地址。
System.out.println(str1 == str2);
//equals
person person1=new person();
person person2=new person();
//没有重写前是比较的是地址值
System.out.println(person1.equals(person2));//false
Object obj1 = new Object();
Object obj2 = new Object();
没有重写前是比较的是地址值
System.out.println(obj1.equals(obj2));//false
//String类是重写了equals的,所以比较的是内容
String str1=new String("123");
String str2=new String("123");
System.out.println(str1.equals(str2));//true重写了equal方法
2.3
equals 重点理解
7>.
Date类
1.Date类的概述
-
Date 类的概述 是util 包下的,不能导入sql包下的
-
类Date 表示特定的瞬间,精确到毫秒。
2.构造方法
-
public Date():空参构造,是当前时间
-
public Date(long l):有参构造,从1970开始指定的毫秒数
//如果没有传递参数表示的是当前时间
Date date1=new Date();
System.out.println("date1 = " + date1);//date1 = Tue Jul 02 16:21:31 CST 2019
//如果构造方法中传递的参数是0,代表的1970年1月1日
Date date2=new Date(0);
System.out.println("date2 = " + date2);//date2 = Thu Jan 01 08:00:00 CST 1970
3.成员方法
-
public long getTime():获得的是日期对象从1970年1月1日00:00:00到现在的毫秒值
-
public void setTime():设置时间,给的是毫秒值
Date date1=new Date();
long time = date1.getTime();
long systemTime = System.currentTimeMillis();
//大多数情况下time 和 systemTime 是一样的,相差的时间间隙太小了
System.out.println("通过时间对象获取毫秒值"+time);//1562763852244
System.out.println("通过系统方法获取毫秒值"+systemTime);//1562763852245
System.out.println("--------");
Date dat2=new Date();
dat2.setTime(1000);//设置豪秒值,改变时间对象
System.out.println(dat2);// Thu Jan 01 08:00:01 CST 1970
8>.
SimpleDateFormat类实现日期和字符串的相互转换
1.DateFormat 类的概述
- DateFormat: 是一个抽象类,不能被被实例化。是SimpleDateFormat的父类
2.
SimpleDateFormat
-
构造函数:①. SimpleDateFormat() ②. SimpleDateFormat(String pattern)
-
方法:
public final String format(String pattern):
将日期对象转成字符串 -
public Date parse(String source):
将字符串转成日期对象
//获取当前时间对象
Date d1=new Date();
//创建日期格式化类对象
SimpleDateFormat sdf=new SimpleDateFormat();
String format = sdf.format(d1);
System.out.println(format);//2019/7/10 下午9:14
//将日期对象转换成字符串
//获取当前时间对象
Date d1=new Date();
//创建日期格式化类对象
//SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd HH:mm:ss");//2019年07月10 21:18:48
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//2019/07/10 21:19:28
System.out.println(sdf.format(d1));
//将时间字符串转成日期对象
String str="2000年08月08日08:08:08";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
//将字符串转成日期对象
Date d1=sdf.parse(str);
System.out.println(d1);//Tue Aug 08 08:08:08 CST 2000
//计算出你活了多少天
//1.将生日字符串和今天字符串存在String类型的变量中
String strBir="1996年02月11日08:08:08";
String strNow="2019年07月10日08:08:08";
//2.定义日期格式化对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
//3.将日期字符串转成日期对象
Date dBir=sdf.parse(strBir);
Date dNow=sdf.parse(strNow);
//4.通过日期对象获取毫秒值
long time=dNow.getTime()-dBir.getTime();
System.out.println("你再世界上活了"+time/(1000*60*60*24));
3>.
关于工具类
- 工具类: ①.构造方法私有 ②.成员方法静态
public class DateUtils {
private DateUtils(){}
//将日期转化为指定格式的字符串
public static String dateToString(Date date,String format){
SimpleDateFormat sdf=new SimpleDateFormat(format);
String s=sdf.format(date);
return s;
}
//把字符串解析为指定格式的日期
public static Date stringToDate(String str,String format) throws ParseException {
SimpleDateFormat sdf=new SimpleDateFormat(format);
Date date=sdf.parse(str);
return date;
}
}
public class DateDemo {
public static void main(String[] args)throws Exception {
//1.测试将日期转成字符串
Date d=new Date();
String format="yyyy年MM月dd日 HH:mm:ss";
String string=DateUtils.dateToString(d,format);
System.out.println(string);//2019年07月16日 15:07:05
//2.测试将字符串转成日期
String str="2019-7-16 08-08-08";
String format2="yyyy-MM-dd HH-mm-ss";
Date date=DateUtils.stringToDate(str,format2);
System.out.println(date);//Tue Jul 16 08:08:08 CST 2019
}
}
9>.
Calendar类
-
public static Calendar getInstance():
创建Calendar对象 -
public int get(int field) :
获取年、月、日等 -
public abstract void add(int field,int amount):
根据日历的规则,将制定的时间量添加或减去给定的日历字段 -
public final void set(int year,int month,int date) :
设置当前日历的年月日
//获取对象
Calendar c=Calendar.getInstance();//多态的方式
//System.out.println(c);
//public int get(int field);
int year=c.get(Calendar.YEAR);
//月份是从0开始的
int month=c.get(Calendar.MONTH)+1;
int day=c.get(Calendar.DATE);
System.out.println(year+"年"+month+"月"+day+"日");
/*
需求:
获取任意一年的二月有多少天
思路:
1:键盘录入任意的年份
2:设置日历对象的年、月、日
年:来自于键盘录入
月:设置为3月,月份是从0开始的,所以设置的值是2
日:设置为1日
3:3月1日往前推一天,就是2月的最后一天
4:获取这一天输出即可
*/
public class CalendarTest {
public static void main(String[] args) {
//键盘录入任意的年份
Scanner sc = new Scanner(System.in);
System.out.println("请输入年:");
int year = sc.nextInt();
//设置日历对象的年、月、日
Calendar c = Calendar.getInstance();
c.set(year, 2, 1);
//3月1日往前推一天,就是2月的最后一天
c.add(Calendar.DATE, -1);
//获取这一天输出即可
int date = c.get(Calendar.DATE);
System.out.println(year + "年的2月份有" + date + "天");
}
}
数组
10.
数组
1>.
数组的说明
-
①. 是一种用于存储多个相同类型数据的存储模型
-
② .数组属引用类型,数组型数据是对象(object),数组中的每个元素相当于该对象的成员变量
-
③. 数组的长度在程序运行期间不可改变
2>.
数组的使用
1.
数组的初始化和赋值
-
静态初始化:在创建的时候直接指定数组的元素,长度由数组中元素的个数决定
-
①.静态初始化,初始化数组与给数组元素赋值同时进行
a .String names=new String[ ] {"唐智","杨幸","唐洋"} [ 标准 ]: b.String names= { "唐智","杨幸","唐洋" };[ 省略 ]
-
动态:在创建数组的时候只指定长度,系统会根据数组的数据类型分配默认值
-
②. 动态初始化,初始化数组与给数组元素赋值分开进行
int [ ]scores=new int[ 3 ];
2. 数组的长度
-
通过数组的length属性
int namesLength=names.length;
-
注意:
数组一旦初始化,程序运行期间,其长度就不可变
3.
如何遍历数组元素
-
①. for循环的方式
-
②. 增强for循环
4.
数组的初始化值 [ 掌握 ]
-
使用动态初始化数组的时候,其中的元素将会自动拥有一个默认值。规则如下:
-
①. 如果是整数类型,那么默认是0
-
②. 如果是浮点类型,那么默认是0.0
-
③. 如果是字符类型,那么默认是’\u0000’, 它是一个不可见的字符,不是空格
-
④. 如果是布尔类型,那么默认是false;
-
⑤. 如果是引用类型,那么默认是null
@Test
public void fun1(){
//省略格式的静态初始化
//int []arrayA = int arrayA[];
int[]arrayA={10,20,30};
//静态初始化的标准格式,可以成分为两个步骤
int[]arrayB;
arrayB=new int[]{10,20,30};
//动态初始化也可以拆分为两个步骤
int[] arrayC;
arrayC=new int[5];
//静态初始化的省略格式,不能坼分为两个步骤
/*int [] arrayD;
arrayD={10,20,30};*/
//直接打印数组的名称,得到的是数组对应的,内存地址哈希值
System.out.println(arrayD);
@Test
public void fun1() {
//1.如果定义一个数组
//1.1数组的声明
String[]names;
int[]scores;
//1.2初始化
//第一种:静态初始化,初始化数组与给数组元素赋值同时进行
names=new String[]{"唐智","杨幸","唐洋"};
//第二种:动态初始化,初始化数组与给数组元素赋值分开进行
scores=new int[3];
//2.如何调用相应的数组元素,通过数组元素的下角标的方式来调用
//下表从0开始,到n-1结束。其中n表示的数组的长度
scores[0]=60;
scores[1]=70;
scores[2]=80;
//3.数组的长度:通过数组的length属性
int namesLength=names.length;
System.out.println("数组的长度是:"+namesLength);
//4.如何遍历数组元素
for(int i=0;i<namesLength;i++){
System.out.println(names[i]);
}
for(String name:names){
System.out.println(name);
}
}
}
3>.
Java中的内存划分
-
①.
栈(Stack)
:存放的都是方法中的局部变量。方法的运行一定要在栈当中运行
,使用完毕以后会立即消失 -
局部变量:方法的参数,或者是方法{ }内部的变量
-
作用域:一旦超出作用域,立刻从栈内存当中消失
-
②.
堆(Heap)
:凡是new出来的东西,成员变量,都在堆当中
,使用完毕,会被垃圾回收器空闲时回收 -
堆内存里面都有一个地址值:16进制
-
堆内存里面的数据,都有一个默认值。规则:
如果是整数 默认是0
如果是浮点数 默认是0.0
如果是字符 默认为' \u0000',
//在unicode中16进制的都是字符,是一个不可见字符,但是并不是空格
如果是布尔 默认是false
如果是引用类型 默认是null
-
③.
方法区(Method Area)
:储存.class相关信息,包含方法的信息 -
④.本地方法栈(Native Method Stack):与操作系统相关
-
⑤. 寄存器(pc Register):与CPU相关,保证方法的调用
4>.
一维数组的内存图
5>.
数组常见的异常
-
①. 数组索引越界异常:
ArrayIndexOutOfBoundsException
-
②. 空指针异常 [
NullPointerException
]:所有的引用类型变量,都可以赋值一个null值。但是代表其中什么都没有。 数组必须进行new初始化才能使用其中的元素,如果只有赋值一个null,没有进行new创建 那么将会发生:空指针异常 NullPointerException -
访问的数组已经不再指向堆内存的数据,造成空指针异常
-
③. null:空值,引用数据类型的默认值,表示不指向任何有效对象
//索引越界:访问了数组中不存在的索引对应的元素,造成索引越界问题
int [ ] arr=new int[ 2 ];
System.out.print(arr[3]);
// 原因:忘了new
int []arr=null;
System.out.println(arr[0]);
6>.
数组的反转
- 重点是一个元素中各个元素的值不同了,发生了反转
数组元素的反转
本来的样子:[1,2,3,4]
之后的样子:[4,3,2,1]
1.数组元素的反转,其实就是对称位置的元素交换
2.通常遍历数组用的是一个索引
int i=0;
现在表示对称位置需要两个索引
int min=0;
int max=array.length-1;
3.如何交换两个变量的值?
用第三个变量倒手
4.什么时候停止交换?
①.min=max
②.min<max
什么时候应该交换?
min<max
int [] arr={10,6,5,55,99,66};
//遍历打印数组本来的样子
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+"\t");
}
System.out.println("");
System.out.println("==========");
for(int min=0,max=arr.length-1;min<max;min++,max--){
int temp=arr[min];
arr[min]=arr[max];
arr[max]=temp;
}
//再次打印输出数组后来的样子
for (int i = 0; i < arr.length; i7.7.++) {
System.out.p.rint(arr[i]+"\t");
}
7>.
数组内容相同 [ 标记的方法很重要 ]
public class Demo4 {
public static void main(String[] args) {
//定义两个数组,分别使用静态初始化完成数组元素的初始化
int [] arr1 ={11,22,33};
int [] arr2 ={11,22,33};
boolean compare1 = compare(arr1, arr2);
System.out.println("两个数组是否相同?"+compare1);
}
public static boolean compare(int[]arr1,int[]arr2){
//首先比较数组长度,如果长度不相同,数组内容肯定不相同,返回false
if(arr1.length!=arr2.length){
return false;
}
//其次遍历,比较两个数组中的每一个元素,只要有元素不相同,返回false
for(int i=0;i<arr1.length;i++){
if(arr1[i]!=arr2[i]){
return false;
}
}
//最后
return true;
}
}
- 标记的思想
[ 重要 ]
public class Test01 {
public static void main(String[] args) {
int[]arr={11,22,33,44};
int[]arr2={11,22,33,44};
//调用方法,用变量接收
boolean flag=compare(arr,arr2);
System.out.println("flag = " + flag);
}
public static boolean compare(int[] arr, int[] arr2) {
//首先比较长度,如果长度不相同,数组的内容肯定不痛
if(arr.length!=arr2.length){
return false;
}
//标记
boolean flag=true;
//遍历数组
for (int i = 0; i < arr.length; i++) {
if(arr[i]!=arr2[i]){
//重新对标记赋值
flag=false;
}
}
//当循环结束之后,不管flag里的值是什么,直接返回即可
return flag;
}
}
8>.
数组作为方法参数_传递地址 [ 掌握 ]
-
①. 数组可以作为方法的参数,当调用方法的时候,向方法的小括号进行传参,传递进去的其实是数组的地址值
-
②. 数组作为方法的返回值,返回的其实也就是数组的地址值
public class Demo4 {
public static void main(String[] args) {
int a=10;
int b=5;
int c=6;
int []arr=calculate(a,b,c);
System.out.println("数组的返回值其实是数组的地址值"+arr);
System.out.println("三个数只和是:"+arr[0]);
System.out.println("三个数的平均数是:"+arr[1]);
}
public static int[] calculate(int a,int b,int c){
int sum=a+b+c;
int avg=sum/3;
int []arr={sum,avg};
return arr;
}
}
9>.
练习 [ 重点掌握其中的思路 ]
/*
1.键盘录入6个int类型的数据存数数组arr中
2.将arr数组中的内容反转
3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推
4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换
5.打印最终的数组(实现了1-4步之后的数组)
6.如:用户输入的6个int数字为[1,2,3,4,5,6],最后输出的结果为[6, 5, 4, 1, 2, 3]
* */
public class HomeWord3 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int[]arr=new int[6];
//1.键盘录入6个int类型的数据存数数组arr中
for (int i = 0; i < arr.length; i++) {
System.out.println("请键盘输入第"+(i+1)+"个数");
arr[i]=sc.nextInt();
}
System.out.println("原来的数组是:");
print(arr);
System.out.println("");
//2.将arr数组中的内容反转
System.out.println("反转后的数组是");
arrReverse(arr);
print(arr);
System.out.println("");
//3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推
System.out.println("将翻转后的数组角标为奇数的互相交换");
arrChange(arr);
print(arr);
System.out.println();
//4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换
System.out.println("最终的数组是:");
arrchangeFirstLast(arr);
}
//4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换
public static void arrchangeFirstLast(int[]arr){
//如果数组长度是偶数
if(arr.length%2==0){
int temp=arr[1];
arr[1]=arr[arr.length-1];
arr[arr.length-1]=arr[1];
}else {
int temp=arr[1];
arr[1]=arr[arr.length-2];
arr[arr.length-2]=arr[1];
}
}
//3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推
public static void arrChange(int[] arr) {
for (int i = 1; i < arr.length-2; i++) {
// 判断一下索引是否是奇数
if(i%2!=0){
int temp=arr[i];
arr[i]=arr[i+2];
arr[i+2]=temp;
}
}
}
//2.将arr数组中的内容反转
public static void arrReverse(int[] arr) {
for (int i = 0,j=arr.length-1; i < j; i++,j--) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
public static void print(int[]arr){
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
System.out.print(arr[i]+"]");
}else{
System.out.print(arr[i]+",");
}
}
}
}
//要求掌握第二个和第四个
public class HomeWork4 {
/*
1.键盘录入10个整数存入数组中
2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧
3.定义一个方法打印原数组和处理后的数组
4.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数
*/
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int[]arr=new int[6];
//1.键盘录入6个int类型的数据存数数组arr中
for (int i = 0; i < arr.length; i++) {
System.out.println("请键盘输入第"+(i+1)+"个数");
arr[i]=sc.nextInt();
}
System.out.println("原来的数组是:");
print(arr);
System.out.println("");
//2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧
System.out.println("左侧奇数,偶数右侧");
swap(arr);
print(arr);
System.out.println("");
//3.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数
findFirstCount(arr);
}
//3.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数
public static void findFirstCount(int[]arr){
int count=0;
System.out.print("元素中只出现一次的元素是:");
for (int i = 0; i < arr.length; i++) {
int arrCount=0;
for (int j = 0; j < arr.length; j++) {
if(arr[i]==arr[j]){
arrCount++;
}
}
if(arrCount==1){
System.out.print(arr[i]+" ");
count++;
}
}
System.out.println("");
System.out.println("元素中只出现一次的总个数是"+count);
}
/*
思路:
定义一个变量,这个变量将来作为奇数元素存放的索引位置
遍历数组
判断数组中的元素是否是奇数
如果是奇数,那么这个奇数就存放在数组的arr[index]位置上
切记每次交换之后index一定要++
* */
//2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧
public static void swap(int[] arr) {
int index=0;
for (int i = 0; i < arr.length; i++) {
//如果是奇数
if(arr[i]%2!=0){
if(i==0){
//只要进行了交换,index一定要进行++
index++;
}else{
//交换位置
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
//只要进行了交换,index一定要进行++
index++;
}
}
}
}
//打印输出
public static void print(int[]arr){
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
System.out.print(arr[i]+"]");
}else{
System.out.print(arr[i]+",");
}
}
}
}
10>.
关于数组的常见几种算法
1.
冒泡排序
- 原理:两个相邻位置比较,如果前面的元素比后面的元素大就换位置
public class DemoArr {
public static void main(String[] args) {
int []arr={1,5,88,99,11};
bubbleSort(arr);
print(arr);
}
/*
冒泡排序 :
1. 返回值类型,void
2. 参数列表 ,int[]arr
第一次: arr[0]与arr[1], arr[1]与arr[2], arr[2]与 arr[3], arr[3]与 arr[4] 比较 4次
第二次: arr[0]与arr[1], arr[1]与arr[2], arr[2]与 arr[3] 比较 3 次
第三次: arr[0]与arr[1], arr[1]与arr[2] 比较 2次
第四次: arr[0]与arr[1] 比较 1 次
* */
public static void bubbleSort(int[]arr){
//如果有n个数据进行排序,总共需要比较n-1次
//外循环只需要比较arr.length-1次就可以了
for (int i = 0; i < arr.length-1; i++) {
//内循环
//-1为了防止索引越界 -i 是为了提高效率,每次完后就少判断一次
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
//打印数组
public static void print(int[]arr){
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
System.out.print(arr[i]+"]");
}else{
System.out.print(arr[i]+", ");
}
}
}
}
2.
二分查找 [ 折半查找 ]
-
二分查找:查找元素对应的索引
-
前提:数组元素有序
public class Demo1{
public static void main(String[] args) {
int[]arr={11,22,33,44,55,66,77};
int index=getIndex(arr,45);
System.out.println("index = " + index);
}
//二分查找: 返回值类型int,参数列表arr[] ,value
public static int getIndex(int[]arr,int value){
int min=0;
int max=arr.length-1;
int mid=(min+max)/2;
//当中间值不等于要找的值,就开始循环查找
while(arr[mid]!=value){
//当中间值小于了要找的值,让最小的索引改变
if(arr[mid]<value){
min=mid+1;
//当中间值大于了要找的值,让最大的索引改变
}else if(arr[mid]>value){
max=mid-1;
}
//无论最大还是最小改变,中间索引都会随之改变
mid=(min+max)/2;
//如果最小索引大于了最大索引,就没有查找到返回-1
if(min>max){
return -1;
}
}
return mid;
}
}
11 .
二维数组的使用[ 主要看代码 ]
1. 二维数组的初始化
-
①. 静态初始化:
String[][]names=new int[][]{{1,2,3},{3,4,5},{6}};
-
②. 动态初始化:
a. names=new String[6][5]; b.ames=new String[6][]
2. 如何来引用具体的某一个元素
int[ ][ ]i=new int[3][2];
i[1][0]=90;
i[2][1]=100;
3.数组的长度
-
整个二维数组的长度:
int length=i.length; System.out.println("整个二维数组的长度:"+length);
-
二维数组中元素的长度
System.out.println("二维数组的第一行有多少元素"+i[0].length);
4. 遍历二维数组
//4.如何遍历二维数组scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化
for(int j=0;j<scores.length;j++){//控制行数
for(int k=0;k<scores[j].length;k++){
System.out.print(scores[j][k]+" ");
}
System.out.println();
}
5. 二维数组内存结构
注意:
特殊写法情况:int[ ] x,y[ ]; x是一维数组,y是二维数组。
一维数组:int[] x 或者int x[]
二维数组:int[][] y 或者 int[] y[] 或者 int y[][]
int[]x,y[];
//int [] x;一维
//int [] y[];二维
@Test
public void fun1(){
int[][]scores;
String[][]names;
//1.二维数组的初始化
scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化
names=new String[6][5];//2.动态初始化的方式一:6个小组5个人
names=new String[6][];//动态初始化的方式二:有6个小组,每个小组人数不定
names[0]=new String[5];
names[0]=new String[6];
names[0]=new String[7];
names[0]=new String[8];
//错误的初始化方式
// names=new String[][];
// names=new String[][5];
//2.如何来引用具体的某一个元素
int[][]i=new int[3][2];
i[1][0]=90;
i[2][1]=100;
//3.数组的长度
//整个二维数组的长度
int length=i.length;
System.out.println("整个二维数组的长度:"+length);
//二维数组中元素的长度
System.out.println("二维数组的第一行有多少元素"+i[0].length);
//4.如何遍历二维数组scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化
for(int j=0;j<scores.length;j++){//控制行数
for(int k=0;k<scores[j].length;k++){
System.out.print(scores[j][k]+" ");
}
System.out.println();
}
}
6. 二维数组的练习
@Test
public void fun2(){
int[][]arr=new int[][]{{3,8,2},{2,7},{9,0,1,6}};
int sum=0;
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
sum=sum+arr[i][j];
}
}
System.out.println("数组的和是 :"+sum);
}
9.
Arrays
-
java.util.Arrays:是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作
-
public static String toString(数组)
:将参数数组变成字符串(按默认格式:[ 元素1 , 元素2 ,元素3 ]) -
public static void sort( 数组)
:按照默认升序(从小到大)对数组的元素进行排序 -
备注:
①.如果是数值,sort默认按照升序从小到大 -
②. 如果是字符串,sort默认按照字母升序
-
③. 如果是自定义类型,那么这个自定义的类需要有Comparable或 Comparator 接口的支持
int []intArray={10,20,30};
String instr= Arrays.toString(intArray);
System.out.println(instr);//[10, 20, 30]
int [] array1={1,2,66,99,77,55,21};
Arrays.sort(array1);
System.out.println(Arrays.toString(array1));//[1, 2, 21, 55, 66, 77, 99]
//需求:将一个随机字符串中得到所有字符升序排序,并倒序打印
//自定义随机的字符串
String line="adxcvewgjgn";
//转换成字符数组
char[]ch=line.toCharArray();
//升序排序
Arrays.sort(ch);
//倒序打印
System.out.print("升序后倒序打印如下:");
for(int i=ch.length-1;i>=0;i--){
System.out.print(ch[i]+"\t");
}
11.
方法
1.方法的概述
-
方法是将具有 [ 独立功能的代码块 ] 组织成为一个整体,使其具有特殊功能的代码集
-
注意:①. 方法必须先创建才能使用,该过程称为方法定义
-
②. 方法创建后并不是直接运行的,需要手动使用后才能执行,该过程称为方法调用
2.
方法的调用
-
①.单独调用: 方法名称(参数)
-
②.打印调用: System.out.println(方法名称(参数));
-
③.赋值调用: 数据类型 变量名称=方法名称(参数)
public class MethodDefine {
public static void main(String[] args) {
sum(10, 20);//①.单独调用: 方法名称(参数)
System.out.println(sum(10, 20));//②.打印调用: System.out.println(方法名称(参数));
int num1=sum(10, 20);//③.赋值调用: 数据类型 变量名称=方法名称(参数)
}
public static int sum(int a,int b){
System.out.println("11");
int result=a+b;
return result;
}
}
3.对比有参数和无参数
-
方法的参数就是定义一些变量,当方法被调用的时候,用来接收数据使用的
[ 掌握 ]
-
有参数:小括号当中有内容,当一个方法需要一些数据条件,才能完成任务的时候,就是有参数
-
例如两个数字相加,必须知道两个数字各是多少,才能相加
-
无参数:小括号当中留空。一个方法不需要任何数据条件,自己就能独立完成任务,就是无参数
-
例如定义一个方法,打印固定10次Helloword
4.对比有返回值和没有返回值
-
有返回值: 定义一个方法,用来 [求出] 两个数字之和(你帮我算,算完之后把结果告诉我)
-
无返回值: 定义一个方法,用来 [打印] 两个数字之和(你来计算,算完之后你自己负责显示结果,不用告诉我)
-
注意事项:
-
①. 对于有返回值的方法,可以使用单独调用、打印调用、或者赋值调用
-
②. 对于没有返回值的方法,只能使用单独调用,不能使用 打印调用、或者赋值调用
public class MethodDefine {
public static void main(String[] args) {
}
//我是一个方法,我负责两个数字相加
//我有返回值int,谁负责调用我,我就把结果告诉谁
public static int getSum(int a, int b){
int result=a+b;
return result;
}
//我是一个方法,我负责两个数字相加。
//我没有返回值,不会把结果告诉任何人,而是我自己进行打印输出
public static void getSum2(int a, int b){
int result=a+b;
System.out.println("结果是:"+result);
}
}
5.
类的方法:提供某种功能的实现
- ①. 实例: 格式:
权限修饰符 返回值类型(void:无返回值/具体的返回值) 方法名(形参){ }
public void eat(){//方法体}
public String getName(){ }
public void setName(String n){ }
-
②. 关于返回值类型:void:表明此方法不需要返回值,有返回值的方法:在方法的最后一定有return + 返回值类型对应的变量[ 记忆:void 与return不可以同时出现一个方法内。像一对“冤家”。]
-
③. 方法内可以调用本类的其他方法或属性,但是不能在方法内再定义方法!
public void info(){
eat();//可以调用本类的其他方法
sleep();//可以调用本类的其他方法
public void breath(){//错误的写法
}
}
- ④.如下写法是正确的
[ 掌握 ]
public static void method(){
return;
}
6. 方法中传参的特点 [ 重点 ]
- ①. 如果方法的参数是基本数据类型,那么形参的改变,不会影响实参 [ 传递的是具体的数据 ]
- ②. 对于引用类型的参数,形参参数的改变,影响实际参数的值 [ 传递的是地址值 ]
12.
方法的重载 [ 重载的要求 ]
-
①. 在同一个类中
-
②. 方法名必须相同
-
③. 方法的参数列表不同 [ 参数的个数不同、参数的类型不同、参数的顺序 ]
-
补充:①. 方法的重载与方法的返回值类型没有关系 ②. 方法的重载与参数的名称名称无关
13.
面向对象编程
面向对象编程的三条主线:
-
①. 类及类的构成成分:属性、方法、构造器、代码块、内部类
-
②. 面向对象编程的特征:封装性、继承性、多肽性(抽象类)
-
③. 其他的关键字:this、super、package、import、static、final、abstract、interface…
-
面向对象:当需要实现一个功能的时候,不关心具体的步骤,而是找一个已经具有该功能的人,来帮我做事儿
1.
java中的类的概念与设计
-
类
和对象
是面向对象的核心概念 -
类 是对一类事物描述,是抽象的、概念上的定义
-
对象 是实际存在的该类事务的每个个体,因此也称实例
2.
面向对象思想的落地法则一:
-
①. 设计类,并设计类的成员(成员变量&成员方法)
-
②. 通过类,来创建类的对象(也称作类的实例化)
-
③. 通过“
对象.属性
” 或“对象.方法
”来调用,完成相应的功能
//1.属性
String name;
int age;
String sex;
//2.方法
public void eat(){
System.out.println("人吃东西");
}
3.
创建的多个对象,彼此各自拥有一套类的属性,当对其中一个对象的属性的属性进行修改时,不会影响到其他对象的属性值
4.
类的属性(成员变量 v 局部变量)
1.
变量和局部变量的相同点
- ①. 遵循变量声明的格式 ②. 都有作用域
2.
不同点
-
①. 定义的位置不一样
-
局部变量:在方法的内部,参数的形参部分
-
成员变量: 在方法的外部,直接写在类中
-
②. 作用范围不一样
-
局部变量:只有方法当中才可以使用,出了方法就不能再用
-
成员变量:整个类全部可以通用
-
③. 默认值不一样
-
局部变量:没有默认值,如果要使用,必须手动进行赋值
-
成员变量:如果没有赋值,会有默认值,规则和数组一样
-
④. 内存的位置不一样
-
局部变量:位于栈内存中
-
成员变量: 位于堆内存中
-
⑤. 生命周期不一样
-
局部变量:随着方法进栈而诞生,随着方法出栈而消失
-
成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
byte short int long ==>0
float double ==>0.0
char ==>空格
boolean ==>false
引用类型变量==>null
3.
变量的分类
-
①. 按照数据类型的不同:基本数据类型(8种)&引用数据类型
-
②. 按照声明的位置的不同:成员变量&局部变量
5. 一个对象的内存图 [ 掌握 ]
-
两个对象使用同一个方法的内存图 [ 原理都类似 ]
-
两个引用指向同一个对象的内存图 [ 原理都类似 ]
6. 使用对象类型作为方法的参数
- 当一个对象作为一个参数,传递到方法中时,实际上传递进去的是对象的地址值
7.
面向对象三大特征之封装性
1.
封装的思想
-
问题: 当创建了类的对象以后,如果直接通过了"对象.属性"的方式对相对的对象赋值的话,可能会出现不满足实际情况的意外,我们考虑不让对象来直接作用属性,而是通过"对象.方法"的形式,来控制对象对属性的访问。实际情况中,对属性的要求就可以通过方法来实现。
-
解决的方法(封装性的思想):
-
①. 将类的属性私有化
-
②. 提供公共的方法(setter &getter)来实现
-
private 修饰的属性只能在本类中被调用,出了此类就不能被调用
//private 修饰的属性只能在本类中被调用,出了此类就不能被调用
private String name;
private int legs;
public int getLegs() {
return legs;
}
public void setLegs(int legs) {
this.legs = legs;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//练习封装
public class TestPerson {
public static void main(String[] args) {
person tp=new person();
tp.setAge(30);
System.out.println(tp.getAge());
}
}
class person{
private int age;
public void setAge(int ages){
if(ages>0&&ages<130){
age=ages;
}else{
System.out.println("你输入有误!");
}
}
public int getAge(){
return age;
}
}
2.
Java权限修饰符[ public、protected、缺省、private ]
8.
类的成员之三:构造器(构造方法)
1.
构造器的作用 [ 掌握 ]
- ①. 创建对象
Person p=new Person();
②. 给创建对象的属性赋值 [ 列如那些默认值 ]
2.
构造器的创建
-
格 式:权限修饰符 类名(形参){}
-
①. 构造方法不能return一个具体的返回值
-
②. 构造方法不要写返回值类型,连void都不写
3.
构造器的说明
-
①. 设置类时,若不显示声明类的构造器的话,程序会默认提供一个空参的构造器
-
②. 一旦显示的定义类的构造器,那么默认的构造器就不在提供
-
③. 类的多个构造器之间构成重载
public class TestPerson {
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person("小智");
System.out.println(p2.getName());
}
}
class Person{
//属性
private String name;
private int age;
//构造器
public Person(String n){
name=n;
}
public Person(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
9.
this关键字
1.
this关键字作用修饰属性,方法,构造器
-
①. 当局部变量 [ 形参 ]与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表名该变量时类成员
-
②. 在任意方法内,如果使用当前类的成员变量或成员方法可以在前面添加this,增强程序的阅读性
-
③. this可以作为一个类中,构造器相互调用的特殊格式
2.
this关键字的注意事项
-
①. 在构造器内部必须声明在首行
-
②. 若在一个雷中有n个构造器,那么最多有n-1个构造器中使用了this();
-
this表示哪个对象调用,this就代表哪个对象
10.
定义一个标准的类
-
①. 所有的成员变量都要使用private关键字修饰
-
②. 为每一个成员变量编写一对儿Getter/setter方法
-
③. 编写一个无参数的构造方法
-
④. 编写一个全参的构造方法
//这样标准的类叫做JavaBean
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
11.
匿名类对象
1.
匿名类对象的使用
-
当我们只需要一次调用类的对象时,我们就可以考虑使用匿名方式创建类的对象
-
特点:创建的匿名类的对象只能够调用一次
-
匿名对象只能使用唯一的一次,下载再用不得不再创建一个新对象
2.
匿名类对象作为方法的参数和返回值
public class StudentT {
public static void main(String[] args) {
//普通使用方式
/* Scanner sc=new Scanner(System.in);
int num=sc.nextInt();*/
//使用匿名对象进行传参
methodParam(new Scanner(System.in));
System.out.println("=========");
Scanner sc=methodReturn();
int num2=sc.nextInt();
System.out.println("输入的是:++"+num2);
}
//使用匿名对象作为参数
public static void methodParam(Scanner sc){
int num=sc.nextInt();
System.out.println("输入的是:"+num);
}
//使用匿名对象作为返回值
public static Scanner methodReturn(){
return new Scanner(System.in);
}
}
12.
继承
- 继承的好处:提高代码的复用性,提高代码的维护性。缺点:耦合性太强
1>.
继承的概述
-
①. 继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法
-
继承性在java中的体现有 extends 或 implements
-
②. 格式:
public class 子类名 [ 派生类 ] extends 父类名[ 基类、超类 ]{ }
-
③. 继承的作用:共性的抽取
-
④. 继承中子类的特点:①.子类可以有父类的内容 ②.子类还可以有自己特有的内容
2>.
继承的注意事项
- ①.Java中只支持单继承,不支持多继承
[ 类和类之间是单继承 ]
- ②. Java中类支持多级继承
- ③.一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
3>.
什么时候使用继承
-
继承体现的关系:is a
-
假设法:我们有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就考虑使用继承来体现,否则就不能滥用继承
-
举例:苹果和水果、猫和动物、猫和狗
4>.
继承访问的特点 [ 掌握 ]
1.
继承中在子类方法中访问一个变量特点
-
①. 子类局部范围找
-
②. 子类成员范围找
-
③. 父类成员范围找
-
④. 如果都没有就报错(不考虑父亲的父亲…)
2.
继承中成员方法的访问特点 [ 通过子类对象访问一个方法 ]
-
①. 子类成员范围找
-
②. 父类成员范围找
-
③. 如果都没有就报错(不考虑父亲的父亲)
3.
继承中构造方法的访问特点
-
①. 子类中所有的构造方法默认都会访问父类中的无惨构造方法
-
②. 每一个子类构造方法的第一条语句默认都是:super()
-
③. 如果父类中没有无参构造方法,只有带参构造方法,该怎么办?
-
a. 通过使用super关键字去显示的调用父类的带参构造方法
-
b. 在父类中自己提供一个无参构造方法
[ 推荐 ]
- 疑问:在普通的成员方法中是否可以调用父类的构造方法?
[ 掌握 ]
不管是无参还是有参,都不可以,构造方法的调用只能在子类创建对象的时候才能初始化父类,
父类的构造方法一定是在子类创建对象的时候就要执行,初始化的动作
5>.
super关键字 [ 修饰属性、方法、构造器 ]
this: 代表本类对象的引用
super:代表父类储存空间的标识(可以理解为父类对象的引用)
super 可以用来修饰属性、方法、构造器*
-
①. 当子类与父类有同名的属性时,可以通过
super.此属性
显示的调用父类中声明的属性,若想调用子类的同名的属性this.此属性
-
②. 当子类重写父类的方法以后,在子类中若想再显示父类的被重写的方法,就需要使用
"super.方法"
-
③. super修饰构造器。通过在子类中使用"super()形参列表"来显示的调用父类中的构造器
注意:
-
a. 在构造器内部,super(形参列表)必须要声明在首行。
-
b. 在构造器内部,this(形参列表)或super(形参列表)只能出现一个
-
c. 当构造器中,不显示的调用 "this(形参列表)"或super(形参列表)其中任何一个,默认调用是父类空惨构造器
6>.
this和super的内存分配图
7>.
重写 [ 覆盖 ]
1.
前提条件
- 有子类继承父类
2.
为什么要重写的方法
- 子类继承父类以后,若父类的方法对子类不适用,那么子类可以对父类的方法重写、覆盖、覆写
3.
重写有一定的规则(两同两小一大原则)
-
①. 要求子类方法的方法名、参数列表必须和父类的一样
-
②. 子类方法的修饰符>=父类方法的修饰符
-
③. 若父类方法抛异常,那么子类方法抛的异常类型<父类
-
④. 子父类的方法必须同为static或同为非static的
-
⑤. 父类的返回值类型小于子类的返回值类型(同一类型下比较)
java疯狂讲义中说重写返回值类型要比父类小,在eclipse中验证,重写返回值类型必须一致,那么Java重写返回值类型必须一样吗?
java 5或者以前,必须一样,java 7 java 8可以不同,但是必须是父类返回值的派生类。
4.
重写要注意的事项
- 私有方法不能被重写(父类私有成员子类是不能被继承的)
8.
继承的练习
13.
package 和 import
1>.
package
- 声明源文件所在的包,写在程序的第一行,每‘.’一次,表示一层文件目录,包名都要小写
格式:package 包名(多级包用.分开);
规范:package com.itheima;
2>.
import
-
①. 显式导入指定包下的类和接口
-
②. 写在包的声明和源文件之间
-
③. 如果需要引用多个类或接口,那么就并列写出
-
④. 如果导入的类时java.lang 包下的,如:System.out.println(),就不需要显式的声明
14.
修饰符
1>.
权限修饰符
2>.
状态修饰符
1.
final [ 用来修饰类、属性、方法 ]
-
在 Java中声明类、属性、方法时,可使用关键字final来修饰,表明"最终"
-
①. final标记的类不能被继承(如:String、StringBuffer 、System)
-
②. final标记的方法不能被重写 (如:object类中的getClass)
-
③. final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次
-
a. final标记的成员变量必须在声明的同时或每个构造器方法中或代码块中显示赋值,然后才能使用
-
b. final double PI=3.14;
-
注意:变量用static和final修饰,称为全部常量
2.
final修饰局部变量 [ 了解 ]
-
变量是基本数据类型:final修饰指的是基本类型的
数据值
不能发生改变 -
变量是引用类型:final修饰指的是引用类型的
地址值
不能发生改变,但是地址里面的内容是可以发生改变的
public class Student {
public int age=20;
}
public class FinalDemo {
public static void main(String[] args) {
//修饰基本类型变量,值不能变
final int age=20;
//age=100;报错
System.out.println(age);//20
//final修饰引用类型变量:地址值不能变,但是变量可以变
final Student student=new Student();
student.age=100;
System.out.println(student.age);//100
//student=new Student(); 报错
}
}
3.
static
1. static的概述
-
一旦使用了static关键字,那么这样的内容不再属于对象自己,而是属于类的,所有凡是本类的对象都共享同一份
-
static,静态的,可以用来修饰属性,方法,代码块(或初始化 ),内部类
2.
static修饰属性(类变量):
-
①. 由类创建的所有对象,都共用这一属性
[ 这也是我们判断是否使用静态关键字的条件 ]
-
②. 当其中一个对象对此属性进行修改,会导致其他对象对属性的一个调用。vs 实例对象(非static修饰的属性,各自对象拥有一套剧本)
-
③. 类变量时随着类的加载而加载的,而且独一份
-
④. 静态的变量可以直接通过"类.类变量"的形式来调用
-
⑤. 类变量的加载要早于对象(全局变量(实例变量)随着对象的创建而被加载的)。所以当有了对象以后,可以"对象.类变量"
-
⑥. 类变量存在于静态域中 [ 方法区中 ]
-
类变量的标准用法:
public static final String coutry="中国"
;
2.
static修饰方法(类方法)
-
①随着类的加载而加载,在内存中也是独一份
-
②可以直接通过"类.类方法"的方式调用
-
③内部可以调用静态的属性或静态的方法,而不能调用非静态的属性或方法。非静态的方法时可以调用静态的属性或方法的
注意:
-
①静态结构(static的属性、方法、代码块、内部类)的生命周期是要早于非静态的结构,同时被回收也要晚于非静态的结果
-
②静态的方法时不能有this、super关键字
15.
类的成员四 →
始化快(代码块)
- 代码块如果有修饰的话,那么只能使用static
1.
非静态代码块
-
①. 可以对类的属性进行初始化操作,可以也可以调用本类声明的方法(静态&非静态的)
-
②. 里面可以有输出语句
-
③. 一个类中可以有多个非静态的代码块,多个代码块按照顺序执行
-
④. 每创建一个类的对象,非静态代码块就加载一次
-
⑤. 非静态代码块的执行要早于执行构造器
2.
静态代码块
-
①. 里面可以有输出语句
-
②. 随着类的加载而加载,而且只能加载一次
-
③. 多个静态代码块之间,按照顺序 构执行
-
④. 静态的代码块中只能执行静态的结构(类属性、类方法)
-
⑤. 静态内容总是优先于非静态,所以静态代码块比构造器方法先执行
3.
static的典型用途
- 用来一次性对静态成员变量进行赋值
public class Demo6 {
static String name;
static{
name="小智";
System.out.println("22");
}
public static void main(String[] args) {
Demo6 demo6=new Demo6();
System.out.println("name");
}
}
父类静态代变量、
父类静态代码块、
子类静态变量、
子类静态代码块、
父类实例成员变量、
父类非静态代码块
父类构造函数、
子类实例成员变量
子类非静态代码块
子类构造函数。
class test{
public test(){
System.out.println("我是测试方法");
}
}
class test2{
public test2(){
System.out.println("我是测试01方法");
}
}
class test3{
public test3(){
System.out.println("我是测试02方法");
}
}
class A{
private static test t = new test();
private test2 t2 = new test2();
{
System.out.println("A类的代码");
}
static {
System.out.println("A类静态");
}
public A(){
System.out.println("A类的构造方法");
}
public A(String s){
System.out.println("A类传值的构造方法");
}
}
class B extends A{
private static test t = new test();
private test3 t2 = new test3();
{
System.out.println("B类的代码");
}
static {
System.out.println("B类静态");
}
public B(){
System.out.println("B类的构造方法");
}
public B(String s){
//super();
System.out.println("B类传值的构造方法");
}
}
public class DemoTest {
public static void main(String[] args) {
new B("xiaozhi");
}
}
16.
面向对象的第三大特性 →
多态性
1>.
面向对象的多态性指的是什么?
-
可以理解为一个事务的多种表现形态
-
extends继承 或者 implements 实现,是多态的前提
[ 掌握 ]
2>.
下面是多态存在的三个必要条件
-
①. 要有类的继承或实现关系
-
②. 要有子类方法对父类方法的重写
-
③. 父类引用指向子类对象
3>.
多态的格式与使用
-
代码当中体现多态性,其实就是一句话: 父类引用指向子类对象
-
格式:
-
①. 父类名称 对象名=new 子类名称();
-
②. 接口名称 对象名=new 实现类名称();
public class Fu {
public void method(){
System.out.println("父类方法");
}
}
public class Zi extends Fu {
public void method(){
System.out.println("子类方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
//使用多态的写法
//左侧父类的饮用,指向了右侧子类的对象
Fu obj=new Zi();
obj.method();//运行时看右边输出: 子类方法
}
}
4>.
多态的好处和弊端
-
①. 好处:提高了程序的扩展性 [ 具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类类型参与操作 ]
-
②. 多态的弊端:不能使用子类的特有功能
5>.
多态中成员变量的访问特点 [ 编译看左边,运行还看左边 ]
-
①. 成员变量:编译看左边,运行还看左边
-
②. 成员方法:编译看左边,运行看右边 [ 先到子类中寻找看是否有重写的方法,要是子类中没有,会用继承对成员方法的访问特点,去父类中找
(理解)
] -
为什么成员变量和成员方法的访问不一样呢?因为成员方法有重写,而成员变量没有
public class Fu {
int num=10;
public void showNum(){
System.out.println(num);
}
}
public class Zi extends Fu {
int num=20;
int age=10;
public void showNum(){
System.out.println(num);
}
}
public class Demo01Multi {
public static void main(String[] args) {
Fu obj=new Zi();
System.out.println(obj.num);//父类的10
//System.out.println(obj.age);报错,没有向下找
System.out.println("====");
//obj.showNum();//子类没有覆盖重写就是父,10
obj.showNum(); //子类如果覆盖重写就是子,20
}
}
6>.
多态中成员方法的使用特点 [ 编译看左边,运行还看右边 ]
-
在多肽的代码当中,成员方法的访问规则如下:看new的是谁,就优先用谁,没有则向上找
-
口诀:
编译看左边,运行看右边
-
①. 成员变量:编译看左边,运行还看左边
-
②. 成员方法: 编译看左边,运行还看右边边
public class Zi extends Fu {
public void showNum(){
System.out.println("父类showNum");
}
public void method(){
System.out.println("子类method");
}
public void methodFu(){
System.out.println("子类特有方法");
}
}
public class Fu {
public void showNum(){
System.out.println("父类showNum");
}
public void method(){
System.out.println("父类method");
}
public void methodFu(){
System.out.println("父类特有方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
Fu obj=new Zi();
obj.method(); //父子都有,优先用子
obj.methodFu();//子类没有,父类有,向上找到父类
//编译看左边,左边是Fu,Fu当中没有metho堕胎dZi方法,所以编译报错
//obj.methodZi();错误写法
}
}
7>.
对象的向上转型
-
对象的向上转型:父类名称 对象名=new 子类名称();
-
Animal animal=new Cat();
-
含义:父类引用指向子类
-
注意事项:向上转型一定是安全的
8>.
对象的向下转型
-
其实是一个还原的动作:格式:
子类名称 对象名=(子类名称)父类的对象
-
含义:父类引用
转为
子类对象 -
注意事项:
①. 必须保证对象本来创建的时候,就是猫,才能向下转型成猫 -
②. 如果对象创建的时候本来不是猫,现在非要向下转型成为猫,就会报错
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void eatCat(){
System.out.println("猫特有方法");
}
}
public class AnimalDemo {
public static void main(String[] args) {
Animal animal=new Cat();//本来是猫。向上转型成了动物
Cat cat=(Cat) animal;//本来是猫,已经被当成了动物,还原回来成为猫
cat.eatCat();
Dog d=(Dog)aniaml;//现在已经是一直猫,猫和狗没有关系,会报错ClassCastException
animal=new Dog();//这是不会报错的,把狗的地址值重新赋值给animal
}
}
9>.
instanceof
- 格式:
对象 instanceof 类型
: 这将会得到一个boolean 值结果,也就是判断前面的对象能不能当做后面类型的实列
public static void main(String[] args) {
Animal animal=new Cat();//本来是猫
//如果希望调用子类特有方法,需要向下转型
//判断一下父类引用animal 本来是不是Dog
if(animal instanceof Dog){
Dog dog=(Dog)animal;
dog.watchHouse();
}
if(animal instanceof Cat){
Cat cat=(Cat)animal;
cat.eatCat();
}
}
10>.
题目中得到的新知识点
1.
多态前提条件中:
- 必须要有继承或者实现关系。 要有方法的重写,如果没有则没有任何意义,但是语法不会报错
2.
用多态方式创建对象时,可以使用以下三种方式:
-
①.父类引用指向子类对象。
-
②.间接父类引用指向子类对象。
-
③.接口类型引用指向实现类对象
3.
多态的向上转型有以下几种常见的格式:
-
①.父类类型 对象名 = 子类对象;
-
②.父类类型 对象名 = (父类类型)子类对象;
-
③.接口类型 对象名 = 实现类对象;
-
④.接口类型 对象名 = (接口类型)实现类对象;
17.
抽象
1>.抽象方法和抽象类的格式
-
抽象方法: 就是加上abstract关键字,然后去掉大括号,直接分号结束
public abstract void method();
-
抽象类: 抽象方法所在类,必须是抽象类才行。 在class之前写上abstract即可
public abstract class Animal{ }
2>.
如何使用抽象类和抽象方法 [ 掌握 ]
-
①. 不能直接new创建抽象类对象
-
②. 必须用一个子类继承抽象父类
-
③. 子类必须覆盖重写抽象父类当中所有的抽象方法覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号
-
④. 创建子类对象进行使用
3>.
抽象方法和抽象类的注意事项:
-
①. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象
-
②. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
-
理解: 子类的构造方法中,有默认的super(),需要访问父类构造方法
[ 重点 ]
-
③. 抽象类中,不一定包含抽象方法,但是抽象方法的类必定是抽象类
-
④. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。
除非该子类也是抽象类
18.
发红包案列
1.群主发普通红包
-
群主发普通红包,某群中有多名成员,群主给成员发普通红包,让成员领取
-
①. 群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取
-
②. 成员领取红包后,保存到成员余额中
发红包的逻辑,三要素
返回值类型:ArrayList<Integer>
方法名称:send
参数列表:
1.总共发多少钱 int totalMoney
2.分成多少份:int count
public ArrayList<Integer> send(int totalMoney,int count){
//方法体
}
收红包:
返回值类型:void
方法名称:receive
参数列表:ArrayList<Integer>
public void receive(ArrayList<Integer> list){
//...
}
19.
接口
1>. 接口概述与生活举例
-
接口就是一种公共的规范标准,只要符合规范标准,就可以大家通用
-
接口是一种引用数据类型,最重要的内容就是其中的 抽象方法
2>.
接口的定义基本格式
-
public interface 接口名称{ //接口的内容 }
-
备注:换成了关键字interface之后,编译生成的字节码文件依然是 .java→.class
-
如果是Java7,那么接口中可以包含的内容有:①. 常量 ②. 抽象方法
-
如果是Java8,还可以额外包含有:③. 默认方法 ④.静态方法
-
如果是Java9,还可以额外包含有:⑤.私有方法
3>.
接口的抽象方法
-
在任何版本的java中,接口都能定义抽象方法
-
public abstract 返回值类型 方法名称(参数列表);
-
①. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
-
②. 这两个关键字修饰,可以选择性地省略
public interface MyInterfaceAbstract {
//这是一个抽象方法
public abstract void method1();
//这是一个抽象方法
public void method2();
//这是一个抽象方法
abstract void method3();
//这是一个抽象方法
void method4();
}
4>. 接口的抽象方法使用
MyInterfaceAbstract
public interface MyInterfaceAbstract {
public abstract void method1();
}
MyInterfaceAbstractImpl
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract{
public void method1() {
System.out.println("这是method1");
}
}
MyInterfaceDemo
public class MyInterfaceDemo {
public static void main(String[] args) {
//MyInterfaceAbstractImpl my=new MyInterfaceAbstractImpl();
MyInterfaceAbstract my=new MyInterfaceAbstractImpl();
my.method1();
}
}
5>.
接口的默认方法
1. 接口的默认方法的定义
-
从Java 8 开始,接口中允许定义默认方法
-
格式:
public default 返回值类型 方法名称(参数列表){ //方法体 }
2. 接口的默认方法的使用
InterfaceDefault
public interface InterfaceDefault {
public abstract void method1();
public default void method(){
System.out.println("这是一个默认的方法");
}
}
public class InterfaceDefaultImpl implements InterfaceDefault {
@Override
public void method1() {
}
//2.接口的默认方法,也可以被接口实现类进行覆盖重写
public void method(){
System.out.println("接口实现类覆盖了默认方法");
}
}
public class InterfaceDefaultImpl2 implements InterfaceDefault {
@Override
public void method1() {
System.out.println("接口实现类2");
}
}
public class DemoT {
public static void main(String[] args) {
InterfaceDefaultImpl2 interfaceDefault1=new InterfaceDefaultImpl2();
//调用默认方法,如果实现类中没有,会向上找
interfaceDefault1.method();//这是一个默认的方法
InterfaceDefault interfaceDefault2=new InterfaceDefaultImpl();
//调用默认方法,如果实现类中没有,会向上找
interfaceDefault2.method();//接口实现类覆盖了默认方法
}
}
6>.
接口的静态方法
1.
接口中的静态方法的定义
-
从Java 8开始,接口当中允许定义静态方法
-
格式:
public static 返回值类型 方法名称(参数列表){ //方法体 }
-
提示:就是将abstract 或者 default 换成 static 即可,带上方法体
2.
接口中静态方法的使用
-
注意:不能通过接口实现类的对象来调用接口当中的静态方法
-
正确用法:通过接口名称,直接调用其中的静态方法
-
格式:
接口名称.静态方法名(参数)
public interface InterfaceStatic {
public static void methodStatic(){
System.out.println("这是一个静态方法在接口中");
}
}
public class DemoStaticT {
public static void main(String[] args) {
/* InterfaceStatic stactic=new InterfaceStaticImpl();
错误写法:stactic.methodStatic();*/
InterfaceStatic.methodStatic();//这是一个静态方法在接口中
}
}
7>.
接口的私有方法定义
-
这个公共方法不应该让实现类使用,应该是私有化的,解决方案 : 从Java 9 开始,接口当中允许定义私有方法
-
①. 普通私有方法,解决多个默认方法之间重复代码的问题
-
格式 : private 返回值类型 方法名称(){ }
-
②. 静态私有方法: 解决多个静态方法之间重复代码的问题
-
public static 返回值类型 方法名称(参数列表){ //方法体 }
public interface MyInterfacePrivateA {
private void methodCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
public default void methodDefault1(){
System.out.println("默认方法1");
methodCommon();
}
public default void methodDefault2(){
System.out.println("默认方法2");
methodCommon();
}
}
public interface MyInterfacePrivateB {
private static void methodCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
public static void methodDefault1(){
System.out.println("静态方法1");
methodCommon();
}
public static void methodDefault2(){
System.out.println("静态方法2");
methodCommon();
}
}
8>.
接口的常量定义和使用
-
接口当中也可以定义 " 成员变量 ",但是必须使用public static final 三个关键字进行修饰。从效果上看,这其实就是接口的常量
-
格式:
public static final 数据类型 常量名称=数据值;
-
①. 接口当中的常量,可以用省略public static final ,注意不写照样是这样
-
②. 接口当中的常量,必须进行赋值,不可以修改
-
③.接口中常量名称,使用完全大写的字母,用下划线进行分割。[ 推荐 ]
public interface MyInterfaceConstant {
//这其实就是一个常量,一旦赋值,不可以修饰
//public:是都能使用的
//static: 和对象没有关系了
//final : 不可变
public static final int NUM=10;
}
public class MyInterfaceConstantT {
public static void main(String[] args) {
System.out.println(MyInterfaceConstant.NUM);//10
}
}
9>.
接口的内容小结
- 在Java 9 +版本中,接口的内容可以有:
1.
成员变量其实是常量:
-
格式:
[ public ] [ staitc ] [ final ] 数据类型 常量名称=数据值;
-
①.常量必须进行赋值,而且一旦赋值不能改变
-
②.常量名称完全大写,用下划线进行分割
2.
接口中最重要的就是抽象方法
-
格式:
[ public ] [ abstract ] 返回值类型 方法名称(参数列表);
-
实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
3.
从Java 8开始,接口里允许定义默认方法
-
格式:
[ public ] default 返回值类型 方法名称(参数列表){ 方法体 }
-
注意:默认方法也可以覆盖重写
4.
从Java8 开始,接口里允许定义静态方法
-
格式:
[ public ] staitc 返回值类型 方法名称(参数列表){ 方法体 }
-
注意:应该用过接口名称进行调用,不能通过实现类对象调用接口静态方法
5.
从Java 9 开始,接口里允许定义私有方法
-
格式:
普通私有方法:private 返回值类型 方法名称(参数列表){ 方法体 }
-
普通私有方法:
private static 返回值类型 方法名称(参数列表){ 方法体 }
-
注意:private的方法只有接口自己才能调用,不能被实现类或别人使用
10>.
继承父类并实现多个接口
-
①. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
-
public class MyInterfaceImplA implements MyInterfaceA,MyInterfaceB{ }
-
②. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么值需要覆盖重写一次即可
-
③. 实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
-
④. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法
public class Fu {
public void method(){
System.out.println("父类方法");
}
}
public interface MyInterface {
public default void method(){
System.out.println("接口的默认方法");
}
}
public class Zi extends Fu implements MyInterface{
}
public class DemoT {
public static void main(String[] args) {
Zi zi=new Zi();
zi.method();//父类方法
}
}
11>.
接口之间的多继承 [ 掌握 ]
-
①. 类与类之间是单继承的,直接父类只有一个
[ class and class ] ⇢ extends
-
②. 类与接口之间是多实现的,一个类可以实现多个接口
[class and interface] ⇢implements
-
③. 接口与接口之间是多继承的
[ interface and interface ] ⇢extends
-
注意事项:
①. 多个父接口当中的抽象方法如果重复,没关系 -
②. 多个接口当中的默认 方法如果重复,那么子接口必须进行默认方法的重写,而且要带着default的关键字
public interface MyInterfaceA {
public abstract void methodA();
public abstract void methodCommon();
public default void methodDefault(){
System.out.println("AAA");
}
}
public interface MyInterfaceB {
public abstract void methodB();
public abstract void methodCommon();
public default void methodDefault(){
System.out.println("BBB");
}
}
public interface MyInterface extends MyInterfaceA,MyInterfaceB {
public abstract void method();
//2. 多个接口当中的默认 方法如果重复,那么子接口必须进行默认方法的重写,而且要带着default的关键字
default void methodDefault() {
}
}
- 接口案列
[ 掌握 ]
创建电脑对象,依次调用开机方法,使用USB设备, 关机方法
打印效果如下:
笔记本开机
连接鼠标的USB
断开鼠标的USB
连接键盘的USB
断开键盘的USB
笔记本关机
//USB
public interface USB {
public abstract void openDevice();
public abstract void closeDevice();
}
//Mouse
public class Mouse implements USB{
@Override
public void openDevice() {
System.out.println("连接鼠标的USB");
}
@Override
public void closeDevice() {
System.out.println("断开鼠标的USB");
}
}
//keyBoarder
public class keyBoarder implements USB {
@Override
public void openDevice() {
System.out.println("连接键盘的USB");
}
@Override
public void closeDevice() {
System.out.println("断开键盘的USB");
}
}
//noteCom
public class noteCom {
public void open(){
System.out.println("笔记本开机");
}
public void close(){
System.out.println("笔记本关机");
}
public void useDevice(USB usb){
usb.openDevice();
usb.closeDevice();
}
}
//Test
public class Demo2 {
public static void main(String[] args) {
//笔记本
noteCom compu=new noteCom();
compu.open();
//鼠标
Mouse s=new Mouse();
compu.useDevice(s);
//键盘
keyBoarder j=new keyBoarder();
compu.useDevice(j);
compu.close();
}
}
20.
类的成员 五→
内部类
1>.内部类的概述
- 如果一个事物的内部包含了另一个事物,那么这就是一个类内部包含另一个类
2>. 分类
- ①.成员内部类 ②. 局部内部类(包含匿名内部类)
3>.
成员内部类
3.1
. 成员内部类的定义格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{
}
}
3.2
. 如何使用成员内部类?有两种方式
-
①. 间接方式: 在外部类的方法当中,使用内部类,然后main只是调用外部类的方法[private
推荐使用
] -
②. 直接方式:公式:[ public ]
-
外部类名称.内部类名称 对象名=new 外部类名称().new 内部类名称();
public class Body {//外部类
//外部类的属性
private String name;
public class Heart{//成员内部类
//内部类的方法
public void beat(){
System.out.println("成员内部类心脏跳动");
//内用外,随意访问
System.out.println("我叫"+name);
}
}
//外部类的方法
public void methodBody(){
System.out.println("外部类的方法");
//间接方式:
new Heart().beat();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Demo01InnerClass {
public static void main(String[] args) {
Body body=new Body();
System.out.println("==========");
//通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
body.methodBody();
System.out.println("==========");
//按照公式写
Body.Heart bHeart=new Body().new Heart();
bHeart.beat();
}
}
- static和非static
public class TestInnerClass {
public static void main(String[] args) {
// 创建静态内部类的对象,可以直接通过外部类调用静态内部类的构造器
Person.Dog d = new Person.Dog();
// 创建非静态的内容类的对象,必须先创建外部类对象,通过外部类对象调用内部类的构造器
Person.Bird b =new Person().new Bird();
b.setName("洋洋");
}
}
class Person {
String name = "小幸";
int age;
// 成员内部类(非static)
class Bird {
String name = "小智";
int age;
public Bird() {
}
public void setName(String name) {
System.out.println(name);//洋洋
System.out.println(this.name);//小智
System.out.println(Person.this.name);//小幸
}
public void info() {
show();
}
}
// 成员内部类(静态内部类)
static class Dog {
}
public void show() {
System.out.println("我是show方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
4>.
内部类的同名变量访问
- 如果出现了重名现象,那么格式是:
外部类名称.this.外部类成员变量名
public class Outer {
int num=10;//外部类的成员变量
public class Inner{
int num=20;//内部类的成员变量
public void methodInner(){
int num=30;//内部类方法的局部变量
System.out.println("内部类方法的局部变量30:"+num);
System.out.println("内部类的成员变量"+this.num);
System.out.println("外部类的成员变量"+Outer.this.num);
}
}
}
public class DemoTTT5 {
public static void main(String[] args) {
Outer.Inner oInner=new Outer().new Inner();
oInner.methodInner();
}
}
5>.
局部内部类
5.1. 局部内部类概述
-
如果一个类定义在一个方法内部的,那么这就是一个局部内部类
-
“局部”:只有当前属性的方法才能使用它,出了这个方法外面就不能用了
5.2
. 定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
//....
}
}
}
public class OuterT {
public void methodOuter(){
//局部内部类
class Inner{
int num=10;
public void methodInner(){
System.out.println(num);
}
}
//在这里调用
Inner inner=new Inner();
inner.methodInner();
}
}
public class DemoTd {
public static void main(String[] args) {
OuterT obj=new OuterT();
obj.methodOuter();
}
}
5.3.
小节一下类的权限修饰符
-
public>protect>(default)>private
-
定义一个类的时候,权限修饰符规则
-
①. 外部类:public /(default)
-
②. 成员内部类 : public /protect /(default) /private
-
③. 局部内部类: 什么都不能写
5.4.
局部内部类的final问题
-
局部内部类:如果希望访问所在方法的局部变量,那么这个局部变量必须是 有效的final的
-
备注:从java 8+开始,只要局部变量保持不变,那么final关键字可以省略
-
原因
-
①. new出来的对象在堆内存当中
-
②. 局部变量是跟着方法走的,在栈内存当中
-
③. 方法运行结束之后,立刻出栈,局部变量就会立刻消失
-
④. 但是new出来的对象会持续存在,知道垃圾回收消失
public class MyOuter {
public static void main(String[] args) {
MyOuter myOuter=new MyOuter();
myOuter.methodOuter();
}
public void methodOuter(){
int i=10;
class Inner{
public void innerMethod(){
System.out.println("ddd"+i);
}
}
Inner inner1=new Inner();
inner1.innerMethod();
}
}
6>.
匿名内部类 [重点掌握]
-
本质是: 是一个继承了该类 或者 实现了该接口的 子类匿名对象
-
前提:有一个类/接口,同时该类/接口 ,有且只有一个抽象方法 [ 多个抽象方法也可以,但是这样就没意义了 ]
6.1.
匿名内部类的概述
-
如果接口的实现类 (或者父类的子类) 只需要使用唯一的一次
-
那么这种情况就可以省略掉该类的定义,而改为使用[匿名内部类]
-
匿名内部类的定义格式:
-
接口名称 对象名=new 接口名称 ( ){ };
public interface MyInterface {
void method();//抽象方法
}
//需求:请创建接口的对象,然后调用接口的方法
public class DemoMain {
public static void main(String[] args) {
/* MyInterface obj=new MyInterfaceImpl();
obj.method();*/
//使用匿名内部类,这得到的是对象
MyInterface obj=new MyInterface() {
public void method() {
System.out.println("使用匿名内部类实现了方法1");
}
};
//对象调用里面的方法
obj.method();
}
}
6.2.
对格式"new 接口名称(){…}"进行解析:
-
①. new代表创建对象的动作
-
②. 接口名称就是匿名内部类需要实现哪个接口
-
③. {…}这才是匿名内部类的内容
6.3.
注意几点问题
-
①.
匿名内部类
,在创建对象的时候,只能使用唯一的一次。如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了 -
②.
匿名对象
在调用方法的时候只能调用唯一一次,如果希望同一对象调用多个方法,那么必须给对象起个名字 -
③. 匿名内部类是省略了 [ 实现类/子类名称],但是匿名对象是省略了 [ 对象名称 ],强调:匿名内部类和匿名对象不是一回事
-
题目
第一题:分析以下需求,并用代码实现
interface Inter {
void show();
}
class Outer {
//补齐代码
public void method(){
new Inter(){
public void show(){
System.out.println("HelloWorld");
}
};
}
}
public class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
21.
基本类型包装类
1>.包装类的概述
-
将基本数据类型封装成对象的好处在于对象中定义更多的功能方法操作该数据
-
常见的操作:用于基本数据类型和字符串之间的转换
-
基本类和包装类之间的对应关系
[ 掌握 ]
2>.
Integer的概述和使用
1.构造方法
-
public Integer(int value):根据int值创建Integer对象
-
public Integer(String str):根据String值创建Integer对象
2.静态方法获取对象
-
public static Integer valueOf(int i):
返回一个指定的int值的Integer的实例 -
public static Integer valueOf(String s):
返回一个保存指定值的Integer对象String [ s如果不是数字,就会报错NumberFormatException ]
//Integer i1=Integer.valueOf(11);-->Integer I2=new Integer(11)
/*
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
* */
Integer i1=Integer.valueOf(11);
System.out.println(i1);//11
Integer i2=Integer.valueOf("11");
System.out.println(i2);//11
3>.
Integer 包装类
1.
int →
String
-
①. 和" "进行拼接
-
②.
public static String valueOf(int i):
返回int参数的字符串表示形式,该方法是String类中的对象
//①.和" "进行拼接
int i=100;
String s1=i+"";
System.out.println("字符串进行拼接:"+s1);
//②. public static String valueOf(int i);
String s2=String.valueOf(i);
System.out.println("public static String valueOf(int i):"+s2);
2.
String →
int
-
①.parseInt: 基本数据类型包装类有8种,其中七种都有parseXXX()方法,可以将这七种的字符串表现形式转化成基本数据类型
-
char的包装类 Character中没有pareseXXX()的方法,字符串转换成String 可以通过toCharA rray()
-
②.String ⇢ Integer ⇢ int [ 了解 ]
//①. String--->Integer----->i
//Integer i1=new Integer(s);过时了
Integer i2=Integer.valueOf(s);
int x = i1.intValue();
System.out.println(x);
//②. 将String转成int [推荐使用]
//public static int parseInt(String str):该方法是Integer中的
int num=Integer.parseInt("123");
System.out.println(num);
String str2="true";
boolean b=Boolean.parseBoolean(str2);
System.out.println(b);
String str="as";
char[]ch=str.toCharArray();
for(char obj:ch){
System.out.print(obj);
}
4>.
JDK5 的新特性自动装箱和拆箱
-
①. 自动装箱:把基本数据类型转换成包装类型
-
②. 自动拆箱:把包装类型转换为基本数据类型
-
注意事项: 在使用时,Integer x=null; 代码会出现NullPointerException,建议先判断是否为null,然后使用
//装箱:把基本数据类型转成对应的包装类类型
Integer i = Integer.valueOf(100);
//自动装箱 : 底层也做了Integer.valueOf()这个动作
Integer i2=100;
//拆箱:把包装类类型转成基本数据类型
int i3=i.intValue()+200;
//自动拆箱
i2+=200;
//i=i+200 自动拆箱 i=i+200 自动装箱
//注意: 在使用包装类型的时候,如果做操作,最好先判断是否为null
//我们推荐是,只要是对象,在使用前就必须进行不为null的判断
Integer i4=null;
if(i4!=null){
//NullPointerException[ 在这个过程中它会自动拆箱,null.intValue();会抛出异常]
i4+=300;
}
5>.
Integer的面试题
- 只有自动装箱才发生这个过程,-128 – 127 是byte的取值范围,如果在这个范围内,自动装箱就不会创建对象,自动装箱就不会新创建对象;而是在常量池中获取; 如果超过了byte取值范围就会再创建对象
[ 掌握 ]
//包装类都实现了toStirng()方法和equals()方法
//-128 -- 127 是byte的取值范围,如果在这个范围内,自动装箱就不会创建对象,自动装箱就不会新创建对象;
//而是在常量池中获取; 如果超过了byte取值范围就会再创建对象
Integer i3 = new Integer(1222);
Integer i4 = new Integer(1222);
System.out.println(i3 == i4);//false
System.out.println(i3.equals(i4));//true
System.out.println("------------------");
Integer i1 = 9527;
Integer i2 = 9527;
System.out.println(i1 == i2);//false
System.out.println(i1.equals(i2));//true 这里还是比较的内容是否相等
System.out.println("------------------");
Integer i5 = 10;
Integer i6 = 10;
System.out.println(i5 == i6);//true
System.out.println(i5.equals(i6));//true
System.out.println("------------------");
Character c1 = '中';
Character c2 = '中';
System.out.println(c1 == c2);//false
System.out.println(c1.equals(c2));//true
System.out.println("------------------");
Character c3 = 'a';
Character c4 = 'a';
System.out.println(c3 == c4);//true
System.out.println(c3.equals(c4));//true
6.
练习
- 字符串总数据排序 91 27 46 38 50,请写程序实现的最终输入结果是27 38 46 50 91
//1.定义一个字符串
String str="91 27 46 38 50";
//2.得到字符串中每个数据
String[]strArray=str.split(" ");
//3. 定义一个int数组,把String[] 数组中每一个元素存储到int数组中
int[]arr=new int[strArray.length];
for (int i = 0; i < strArray.length; i++) {
/* Integer x=Integer.valueOf(strArray[i]);
arr[i]=x;//这个用到了自动拆箱*/
int x=Integer.parseInt(strArray[i]);
arr[i]=x;
}
//4.对int进行排序
Arrays.sort(arr);
//5.创建一个StringBulider进行排序
StringBuilder sb=new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
sb.append(arr[i]);
}else{
sb.append(arr[i]+" ");44
}
}
//6.将StringBuilder变成了String
String sortStr=sb.toString();
System.out.println("sortStr = " + sortStr);
集合
- 虚线是实现,实现是继承
1.
Collection接口
- ①. 为什么集合使用什么样的泛型,迭代器就用什么泛型 ?
[ 理解 ]
- ②.迭代器用什么泛型,next()方法就用什么泛型
[ 理解 ]
1>.
Collection、Map 接口的概述
-
是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
-
JDK不提供此接口的任何直接实现,它提供更具体的子接口 (如 Set 和 List)实现
-
set:
元素无序、不可重复的集合- - 类似高中集合 -
List:
元素有序,可重复的集合- - "动态"数组 -
Map接口:
具有映射关系"key-value"对的集合 - - 类似于高中的函数 y=f(x)(x1,y1)(x2,y2)
2>.
Collection中常用的方法
-
boolean add(E e):添加元素[ 注意这里的E是泛型类型 ]
-
boolean addAll(Collection<? extends E> c):将形成中包含的c所有元素添加到当前集合中 [ 向下限定, 固定上边界 ]
-
boolean remove(Object obj):从集合中移除指定的元素[ 重写了equals方法]
-
void clear():清除集合中的元素
-
boolean contains(Object o):判断集合中是否存在指定的元素[ 重写了equals方法]
-
boolean isEmpty():判断集合是否为空
-
int size():集合的长度,也就是集合中元素的个数
-
Iterator<E>iterator( ):
返回一个Iterator接口,实现类的对象,进而实现集合的遍历 -
equal(Object obj):判断两个集合中所有元素是否完全相同
-
将数组转成集合:
Collection coll1 = Arrays.asList(1, 2, 3);
-
将集合转成数组:
Object [ ] obj=coll1.toArray();
//创建集合对象
Collection<String> c = new ArrayList<String>();
//boolean add(E e):添加元素
// System.out.println(c.add("hello"));
// System.out.println(c.add("world"));
// System.out.println(c.add("world"));
c.add("hello");
c.add("world");
c.add("java");
//boolean remove(Object o):从集合中移除指定的元素
// System.out.println(c.remove("world"));
// System.out.println(c.remove("javaee"));
//void clear():清空集合中的元素
// c.clear();
//boolean contains(Object o):判断集合中是否存在指定的元素
// System.out.println(c.contains("world"));
// System.out.println(c.contains("javaee"));
//boolean isEmpty():判断集合是否为空
// System.out.println(c.isEmpty());
//int size():集合的长度,也就是集合中元素的个数
System.out.println(c.size());
//输出集合对象
System.out.println(c);
3>.
数组转换成集合 [ 掌握 ]
-
①. 数组换成成集合,虽然不能增加或减少元素,但是可以用集合的思想操作数组,也就是说可以使用其他集合中的方法 [ 除了增加和减少都能使用 ]
-
②. 基本数据类型的数组转换成集合,会将整个数组当成一个对象转换
-
③. 将数组转成集合,数组必须是引用数据类型
String[]arr={"a","b","c"};
List<String> list= Arrays.asList(arr);
System.out.println(list);
//list.add("d");//UnsupportedOperationException
//System.out.println(list);
int[]arr2={11,22,33,44,55};
//List<int[]ar> listInt=Arrays.asList(arr2);
List listInt=Arrays.asList(arr2);
System.out.println(listInt);//输出的是地址值[[I@3578436e]
//基本数据类型的数组转换成集合,会将整个数组当成一个对象转换
Integer[]arr={11,22,33,44,55};
List<Integer>list=Arrays.asList(arr);
System.out.println(list);//[11, 22, 33, 44, 55]
//将数组转成集合,数组必须是引用数据类型
4>.
集合转数组 [ 加泛型的 ]
-
当集合转换数组时,数组的长度如果<=集合的size,转换后的数组长度等于集合的size
-
当集合转换数组时,数组的长度如果>=集合的size,分配的数组长度,就和你指定的长度一样[new String[10]]
//谁大集合长度就等于谁
ArrayList<String>list=new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
String[]str=list.toArray(new String[0]);
for(String str2:str){
System.out.println(str2);
}
5>.
集合的遍历方式 [ 迭代器iterator 和 增强for循环 ].
5.1.
Iterator接口的概述
-
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
-
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合
-
Iterator<E> iterator ()
:返回此集合中元素的迭代器,通过集合的iterator () 方法得到
//迭代器的原理及源码解析
Iterator<String> iterator=coll.iterator();//是通过多肽的方式得到的Iterator的对象
/*
public Iterator<E> iterator() {
return new ArrayList.Itr();
}
private class Itr implements Iterator<E> {
.......
}
*/
5.2.
方法
-
boolean hasNext():
如果迭代具有更多的元素,则返回true -
E next():
返回迭代中的下一个元素,并把指针向下移动一位
while(iterator.hasNext()){
/*
Iterator中的常用方法 :
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多的元素,则返回true
* */
//System.out.println(iterator.next());
String s=iterator.next();
System.out.println(s);//String类实现了toString方法
}
5.3.
增强for循环
-
增强for:简化数组和Collection集合遍历
-
它是JDK5 之后出现的,其内部原理就是一个Iterator迭代器
//使用增强for循环实现数组的便利遍历
@Test
public void test3() {
String[]str=new String[]{"aa","bb","cc"};
for(String s:str){
System.out.println(s);
}
}
// 使用增强for循环实现集合的便利遍历
@Test
public void test2() {
Collection coll = new ArrayList();
coll.add("aa");
coll.add(11);
for(Object i:coll){
System.out.println(i);
}
}
}
5.4>.
普通for循环
for (int i = 0; i <list.size() ; i++) {
Student s=list.get(i);
System.out.println(s.getId()+" "+s.getName());
}
6>.
三种迭代能否删除
-
①. 普通for循环,可以删除,但是索引要 i- -
-
②. 迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
-
③. 增强for循环不能删除
public void fun1(){
ArrayList<String> list=new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("BBB");
list.add("CCC");
list.add("DDD");
//1.普通for循环进行删除
/* for (int i = 0; i < list.size(); i++) {
String str=list.get(i);
if("BBB".equals(str)){
//两个BBB在一起的话,会留住一个BBB
//两个BBB分割的话,两个都会删除
//要想删除两个BBB必须使用i--,每次删除后,指针又回到原来的位置
//list.remove(i);
list.remove(i--);
}
}*/
//2.迭代器删除
/* Iterator<String>it=list.iterator();
while(it.hasNext()){
if("BBB".equals(it.next())){
//ConcurrentModificationException
//list.remove("BBB"):不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常
it.remove();
}
}*/
/* for(Iterator<String>it2=list.iterator();it2.hasNext();){
if("BBB".equals(it2.next())){
//ConcurrentModificationException
//list.remove("BBB"):不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常
it2.remove();
}
}*/
//3.增强for循环
/*
增强for循环不能删除,因为底层是一个iterator
* */
for(String str2:list){
if("BBB".equals(str2)){
// list.remove("BBB");
}
}
System.out.println(list);
}
7>.Collection集合储存学生对象并遍历
2.
List接口
1>.
List集合的概述
-
①. 元素都带有索引
-
②. List集合类中元素
有序、且可重复
,集合中的每个元素都有其对应的顺序索引 -
有序:储存 和 取出 的元素顺序一致
-
可重复:储存元素可以重复
2>.
list集合特有方法
-
①. 增:
void add( int index,E element):
添加一个元素 -
②. 删
E remove(int index)
删除指定索引位置的元素,返回被删除的元素 -
③. 改
E set(int index,Object ele):
设置指定索引位置的元素,返回被修改的元素 -
④. 查
E get(int index):
获取指定索引的元素
//遍历集合
for (int i = 0; i <list.size() ; i++) {
Student s=list.get(i);
System.out.println(s.getId()+" "+s.getName());
}
public class ListDemo02 {
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//void add(int index,E element):在此集合中的指定位置插入指定的元素
// list.add(1,"javaee");
//IndexOutOfBoundsException
// list.add(11,"javaee");
//E remove(int index):删除指定索引处的元素,返回被删除的元素
// System.out.println(list.remove(1));
//IndexOutOfBoundsException
// System.out.println(list.remove(11));
//E set(int index,E element):修改指定索引处的元素,返回被修改的元素
// System.out.println(list.set(1,"javaee"));
//IndexOutOfBoundsException
// System.out.println(list.set(11,"javaee"));
//E get(int index):返回指定索引处的元素
// System.out.println(list.get(1));
//IndexOutOfBoundsException
// System.out.println(list.get(11));
//输出集合对象
// System.out.println(list);
//遍历集合
// System.out.println(list.get(0));
// System.out.println(list.get(1));
// System.out.println(list.get(2));
//用for循环改进遍历
for (int i=0; i<list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
}
}
3>.
List并发修改异常
// 需求
我有一个集合: List<String> list=new ArrayList<>();
里面有三个元素:list.add("hello");list.add("world");list.add("java");
遍历集合,得到每一个元素,看有没有 " world "这个元素,如果有,我就添加一个" javaEE" 元素
- 异常的原因:迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致
[ 重点 ]
- 解决方案:使用for循环进行遍历,list中的get方法不会对 expectedModCount = modCount进行判断,可以添加成功
@Test
public void fun5(){
List<String> list=new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
Iterator<String> iterator=list.iterator();
for (int i = 0; i < list.size(); i++) {
String s=list.get(i);
if(s.equals("world")){
list.add("javaEE");
}
}
/* while(iterator.hasNext()){
String s=iterator.next();
if(s.equals("world")){
list.add("javaEE"); //遍历的同时在修改元素,并发修改ConCurrentModificationException
}
}*/
System.out.println(list);
}
4>.
列表迭代器 ListIterator
4.1.
ListIterator 的概述
-
通过list集合的 listIterator()方法得到,所以说它是list集合中特有的迭代器
-
用于允许程序员沿任一方向遍历列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
4.2.
ListIterator中的常用方法
-
E next():返回迭代中的下一个元素
-
boolean hasNext():如果迭代具有更多的元素,则返回true
-
E previous()
:返回列表中的上一个元素 -
bloolean hasPrevious()
: 如果此列表迭代器在相反方向遍历列表时具有更多的元素,则返回true -
void add(E e):
将制定的元素插入列表[ 重点掌握 ]
-
①. 列表迭代器的add方法 会让 expectedModCount = modCount,不会出现并发修改异常
-
②. 而集合中的add方法,会直接把修改的次数加上1,这样expectedModCount != modCount,下次当再次调用next()的方法的时候,会调用checkForComodification()方法,当ExpectedModCo unt != modCount的时候会抛出修改并发异常
// listIterator接口中的add()方法会 expectedModCount = modCount;
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
- 逆向遍历,必须要有正向遍历在前面才能遍历出来;要是没有正向遍历,直接逆向遍历,逆向遍历的指针从0索引开始,这个时候向后遍历便没有要遍历的元素
@Test
public void fun6(){
List<String>list=new ArrayList<>();
list.add("hello");
list.add("world");
list.add("javeEE");
//通过list集合的listItrator()方法得到
ListIterator<String> lit=list.listIterator();
System.out.println("正向遍历:");
while(lit.hasNext()){
String s=lit.next();
System.out.print(s+"\t");//hello world javeEE
}
System.out.println();
System.out.println("逆向遍历:");//一般很少运用
while(lit.hasPrevious()){
String s=lit.previous();
//要是没有正向遍历直接逆向遍历,指针在0的位置,向上找没有元素,所以出打印空
System.out.print(s+"\t");//javeEE world hello
}
//获取列表迭代器
while(lit.hasNext()){
String s=lit.next();
if(s.equals("world")){
//列表迭代器的add方法 会让 expectedModCount = modCount;
lit.add("xiaozhi");
}
}
System.out.println(list);//[hello, world, xiaozhi, javeEE]
}
5>.
ArrayList
5.1.
什么是ArrayList
ArrayList<E>:
在所有E的地方我们使用引用数据类型替换即可
举例:ArrayList<String>、ArrayList<Student>
-
可调节大小的数组实现
-
< E >: 是一种特殊的数据类型,范型
5.2.
ArrayList构造 和 增删改查
-
public ArrayList():创建一个空的集合对象
-
public boolean add(E e):将指定的元素追加到此集合的末尾
-
public void add(int index , E element):在此集合中指定位置插入指定的元素
-
public boolean remove(Objec o):
删除指定的元素,返回删除是否成功 [ 重写了equals方法 ] -
public E remove(int index):删除指定索引处的元素,返回被删除的元素 [ <> 返回的是什么,E就是什么类型]
-
public E set(int index , E element) :修改指定索引处的元素,返回被修改的元素
-
public E get(int index):返回指定索引处的元素
-
public int size() :返回集合中的元素的个数
//ArrayList<String> array=new ArrayList<>();
ArrayList<String> array=new ArrayList<String>();
//public boolean add(E e)
array.add("AAA");
array.add("BBB");
array.add("CCC");
//public void add(int index,E element)
array.add(0,"BeforeAAA");
System.out.println(array);
5.3.
遍历集合 [ 掌握 ]
@Test
public void fun8(){
List<String>list=new ArrayList<>();
list.add("hello");
list.add("world");
list.add("xiaozhi");
//遍历集合的通用格式
//E get(int i)
for(int i=0;i<list.size();i++){
String s=list.get(i);
System.out.print(s+"\t");
}
5.4.
ArrayList 中去除重复字符串元素方式
1.
基本数据类型的去重
- 分析 :
1.创建新集合
2.根据传入的集合[ 老集合 ] 获取迭代器
3.遍历老集合
4.通过新集合判断是否包含老集合中的元素,如果包含就不添加,如果不包含就添加
public class DemoInteger {
public static void main(String[] args) {
//ArrayList去除集合中字符串的重复值(字符串的内容相同)
//思路: 创建新集合方式
/*
创建新集合将重复的元素去掉
1.明确返回值类型,返回ArrayList
2.明确参数类表ArrayList
* */
ArrayList<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("bb");
list.add("cc");
ArrayList<String>arrayList=getSingle(list);
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t");
}
}
public static ArrayList getSingle(ArrayList list){
ArrayList<String> listNew=new ArrayList<>();
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String str1=iterator.next();
if(!listNew.contains(str1)){
listNew.add(str1);
}
}
return listNew;
}
2.
引用类型的去重
-
①.
contains( )
: 方法判断是否包含,依赖的是equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值 -
②.
remove( )
:方法的底层也是依赖equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值
//Person [ 重点是equals方法的重写 ]
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(){
}
//重点是要重写equals方法
@Override
public boolean equals(Object o) {
Person p=(Person)o;
return this.name.equals(p.name)&&this.age==p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合
ArrayList list=new ArrayList();
//添加元素
list.add(new Person("zhangsan",25));
list.add(new Person("zhangsan",25));
list.add(new Person("lisi",23));
list.add(new Person("lisi",23));
//去除重复元素
ArrayList listNew=getSingle(list);
System.out.println(listNew);
System.out.println("--------");
listNew.remove(new Person("zhangsan",25));
System.out.println(listNew);
}
//contains 方法判断是否包含,依赖的是equals方法
//remove :方法的底层也是依赖equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值
public static ArrayList getSingle(ArrayList list){
ArrayList listNew=new ArrayList();
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object obj=iterator.next();
if(!listNew.contains(obj)){
listNew.add(obj);
}
}
return listNew;
}
}
6>.
Vector [ 了解 ]
Vector v=new Vector();
v.addElement("AAA");
v.addElement("BBB");
v.addElement("CCC");
Enumeration en=v.elements();//获取枚举
while (en.hasMoreElements()){//判断集合中是否有元素
System.out.println(en.nextElement());//获取集合的元素
}
3.
数据结构 [ 栈、队列 ]
1>.
栈 [先进后出的模型 ]
-
压/进栈:数据进入栈模式的过程
-
弹/出栈:数据离开栈模型的过程
2.>
队列 [ 先进先出的模型 ]
-
入队列:数据从后段进入队列模型的过程
-
出队列:数据从前端离开队列模型的过程
3>.数组
- 数组是一种查询快,增删慢的模型
4>. 链表
- 链表的每个元素称为节点,包含:数据和下一个节点的地址
4.
数据结构之数组和链表 [ 掌握 ]
-
数组:查询快修改也快 [ 能很快找到索引值 ]; 增、删 很慢 [ 不管是增还是删,都会创建一个新数组,拷贝等 ] ;每增加一倍,元素的就扩容一倍
-
链表:查询修改是慢的 [ 查询只能从两头开始,全部找];增、删很快
5.
list的三个子类的特点
1>.ArrayList
-
底层数据结构是数组,查询快,增删慢
-
线程不安全,效率高
2>.Vector
-
底层数据结构是数组,查询快,增删慢
-
线程安全,效率低
3>.LinkedList
-
底层数据结构是链表,查询慢,增删快
-
查修慢的原因是:链表的底层会从两端往中间查询;增删快的原因是:只在一个链表中发生,增加和删除会断开两个地址值,然后指向要添加的元素
-
线程不安全,效率高
4>.
面试题目[ 重点掌握 ]
1.Vector 和 ArrayList 的区别
Vector 是线程安全的,效率低
ArrayList 是现成不安全的,效率高
共同点:都是数组实现的,增删慢,改查快
2.ArrayList 和 LinkedList的区别
ArrayList 底层是数组结果,查询和修改快
linkedList 底层是链表结构的,增和删比较快,查询和修改比较慢
共同点:都是线程不安全的
5>.List有三个儿子,我们到底使用谁呢?
-
查询多用 ArrayList
-
增删多用 LinkedList
-
如果都多 ArrayList
6.
泛型 [ generic ]
1>.generic概述和基本使用
-
< >
中放的必须是引用数据类型 -
泛型使用的注意事项:前后的泛型必须一致, 或者后面的泛型可以省略不写 [ 1.7的新特性 ]
-
泛型最好不要定义成Object,没有意义
ArrayList<Object>list=new ArrayList<Person>();//报错
ArrayList<Person>list=new ArrayList<Person>();//1.7版本的新特性
ArrayList<Person>list=new ArrayList<>();//1.7版本的新特性
2>.
泛型类 的概述和使用
-
定义格式:
public class 类名 < 泛型类型1....>{ }
-
注意事项:泛型类型必须是引用类型
public class Tools<Q> {
private Q q;
public void setQ(Q q){
this.q=q;
}
public Q getQ(){
return q;
}
}
3>.
泛型方法的概述和使用
- 格式:修饰符<类型> 返回值类型 方法名(类型 变量名){}
public class Tools<Q> {
private Q q;
public void setQ(Q q){
this.q=q;
}
public Q getQ(){
return q;
}
//方法泛型最好要与类的泛型一致
public void show(Q q){
System.out.println(q);
}
//如果不一致,需要在方法上声明该泛型
public <T>void show2(T t){
System.out.println(t);
}
//Q 是创建对象的时候才给赋值,静态方法必须声明自己的泛型
/* public static void print(Q q){ //报错
}*/
public static<Q> void print(Q q){ //这里的Q与类上面的泛型是不同的泛型
}
}
@Test
public void fun1(){
Tools<String>tools=new Tools<>();
tools.show("abc");//abc
tools.show2("123");//123
}
4>.泛型接口的概述和使用
interface Inter<T>{
public void show(T t);
}
//第一种方式:推荐使用
class Demo implements Inter<String>{
public void show(String t) {
System.out.println(t);
}
}
//第二种方式
/*
class Demo<T> implements Inter<T>{ //没有必要在实现接口的时候给自己类添加泛型
@Override
public void show(T t) {
System.out.println(t);
}
}*/
5>.
泛型高级之通配符
-
①. 泛型通配符<?>:任意类型,如果没有明确,那么就是Object以及任意的Java类
-
②. ?extends E :向下限定,E及其子类 [ 泛型固定上边界 ]
-
③. ? super E: 向上限定, E及其父类 [ 泛型固定下边界 ]
//当右边的泛型是不确定时,左边可以指定?
List<?> list=new ArrayList<Integer>();
//?extends E :向下限定,E及其子类 (?是子类,E是父类)
// addAll(Collection < ? extends E >) 括号里面可以放E及其子类
// 因为我们不确定有多少子类,所以我们使用?代替
public class Student extends Person {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
super(name,age);
}
}
ArrayList<Person>list2=new ArrayList<>();
list2.add(new Person("张三",23));
list2.add(new Person("李四",24));
list2.add(new Person("王五",25));
ArrayList<Student>list3=new ArrayList<>();
list2.add(new Person("赵六",26));
list2.add(new Person("周七",27));
list2.addAll(list3);
System.out.println(list2);//把子类放入到父类中是可以的
//list3.addAll(list2);//在父类放入到子类中是不可以的
// public boolean addAll(Collection<? extends E> c) {
//TreeSet(Comparator<? super E>comparator)
TreeMap(Comparator<? super E>comparator)
//类型通配符上限:<? extends 类型>
//List<? extends Number> list1=new ArrayList<Object>(); 报错
List<? extends Number> list2=new ArrayList<Number>();
List<? extends Number> list3=new ArrayList<Integer>();
//类型通配符下限<? super 类型>
//List<? super Number> list4=new ArrayList<Integer>();报错
List<? super Number> list5=new ArrayList<Number>();
List<? super Number> list4=new ArrayList<Object>();
7.
JDK 5的新特性
1>.
增强for循环[ 底层是iterator ]
2>.
可变参数
-
可变参数概述:定义方法的时候不知道定义多少个参数
-
格式:
修饰符 返回值类型 方法名(数据类型 ... 变量名){ }
-
注意事项:①. 这里的变量其实是一个数组 ②. 如果一个方法有可变参数,那么,可变参数肯定是最后一个
3>.
JDK5 的新特性自动装箱和拆箱
-
自动装箱:把基本数据类型转换成包装类型
-
自动拆箱:把包装类型转换为基本数据类型
-
注意事项: 在使用时,Integer x=null; 代码会出现NullPointerException,建议先判断是否为null,然后使用
Integer i3=null;
int a=i3+100;
System.out.println(a);//NullPointerException
//底层用i3调用的是intValue() ,但是i3是null,null调用方法就会出现空指针异常
8.
Set
1>.
HashSet [ Set的主要实现类 ]
1.1.
HashSet 的概述
-
①. 无序性!=随机性 [ 正在的无序性是指元素在底层存储的位置是无序的 ]
-
②. 不可重复性:当向set中添加进相同的元素的时候,后面的这个不能添加进去
-
③. 没有带索引的方法,所以不能用普通for循环遍历
-
④. 底层数据结构是哈希表
HashSet<String>hs=new HashSet<>();
boolean b1=hs.add("a");
boolean b2=hs.add("a");
hs.add(null);
System.out.println(hs); //HashSet的继承体系中,有重写toString()方法
System.out.println(b1);
System.out.println(b2);
//只要能用迭代器迭代的,就可以使用增强for循环遍历
for(String str:hs){
System.out.println(str);//[a]
}
1.2.
哈希值
1.哈希值的介绍
-
是JDK根据对象的地址或者字符串或者数字算出来的
int类型的数值
-
Object类中有一个方法可以获取对象的哈希值:public int hashCode():返回对象的哈希码值
-
特点:
①.同一个对象多次调用hashCode() 方法返回的哈希值是相同的 -
②.默认情况下,不同对象的哈希值是不相同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
1.3.
哈希值原理 [掌握]
1.4.
自定义对象的唯一性
-
要求添加进Set中的元素所在的类,一定要重写equal()和 hashCode()方法,进而保存不可重复性
[ 掌握 ]
-
hashCode():属性相同的对象返回值必须相同,属性不同的返回值尽量不同(提高效率)
-
equals():属性相同返回true,属性不同返回false,返回false的时候就存储
[ 掌握关于equals方法和hashCode方法底层实现原理 ]
/*
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
为什么是31?
1.31是质数,质数是能被1和它本身整除的数
2.31这个数既不大也不小
3.31这个数好算,2的5次方-1,向左移动5位数-1
* */
@Override
public boolean equals(Object o) {
if (this == o) return true;//调用的对象和传入的对象是同一个对象,直接返回true
if (o == null || getClass() != o.getClass()) return false;//传入的对象为空或者字节码文件不相同,返回false
Person person = (Person) o;//向下转型
return age == person.age &&
Objects.equals(name, person.name);
//如果调用对象的年龄和传入对象的年龄 相同并且 调用对象的姓名和传入对象的姓名相同返回true
}
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;//调用的对象和传入的对象是同一个对象,直接返回true
if (o == null || getClass() != o.getClass()) return false;//传入的对象为空或者字节码文件不相同,返回false
Person person = (Person) o;//向下转型
return age == person.age &&
Objects.equals(name, person.name);
//如果调用对象的年龄和传入对象的年龄 相同并且 调用对象的姓名和传入对象的姓名相同返回true
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
HashSet<Person>hs=new HashSet<>();
hs.add(new Person("zhangsan",23));
hs.add(new Person("zhangsan",23));
hs.add(new Person("zhangsan",23));
hs.add(new Person("lisi",24));
hs.add(new Person("lisi",24));
hs.add(new Person("lisi",24));
System.out.println(hs.size());//2
System.out.println(hs);//[Person{name='lisi', age=24}, Person{name='zhangsan', age=23}]
}
1.5.
HashSet的原理 [ 掌握 ]
- 当HashSet调用add()方法储存对象的时候,先调用对象的hashCode方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象。如果没有哈希值相同的对象就直接存入集合;如果有哈希值相同的对象,就和哈希值相同对象逐个进行equals()比较,比较结果为false就存入,true则不存
2>.
LinkedHashSet
-
①. 哈希表和链表实现的Set接口,具有可预测的迭代次序
-
②. 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
-
③. 由哈希表保证元素唯一,也就是说没有重复的元素
@Test
public void testLinkHashSet(){
Set set=new LinkedHashSet();
set.add(123);
set.add(456);
set.add("AAA");
set.add("BBB");
set.add(null);
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());//123 456 AAA BBB null
}
3>.
Set的练习
- ①. 编写一个程序,获取10个1-20的随机数,要求随机数不能重复,并把最终的结果打印在控制台上
//编写一个程序,获取10个1-20的随机数,要求随机数不能重复,并把最终的结果打印在控制台上
//1.有Random类创建随机数对象
Random random=new Random();
//2.需要储存10个随机数,而且不能重复,所以我们用hashSet集合
HashSet<Integer>hs=new HashSet<>();
//3.如果HashSet的size是<10 就可以不断的储存,如果大于等于10就停止储存
while(hs.size()<=10){
//4.4.通过Randeom类中的nextInt(n)方法获取1-20之间的随机数,并将这些数字储存在hashSet中
int i=random.nextInt(20)+1;
hs.add(i);
}
for(Integer hs2:hs){
System.out.println(hs2);
}
- ②.使用Scanner从键盘读取一行输入,去除重复字符,打印不同的那些字符
Scanner sc=new Scanner(System.in);
System.out.println("请键盘输入字符串");
String str=sc.nextLine();
HashSet<Character> hs=new HashSet<>();
char[] chs = str.toCharArray();
for (int i = 0; i < chs.length; i++) {
hs.add(chs[i]);
}
Iterator<Character> iterator = hs.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
/*for(Character hs2:hs){
System.out.print(hs2);
}*/
- ③.去除list中重复的元素
//将集合中重复的元素去掉
@Test
public void fun7(){
ArrayList<String>list=new ArrayList<>();
list.add("AAA");
list.add("AAA");
list.add("AAA");
list.add("AAA");
list.add("AAA");
list.add("AAA");
list.add("BBB");
list.add("BBB");
list.add("CCC");
list.add("CCC");
getSingle(list);
// 打印
System.out.println(list);
}
public static void getSingle(ArrayList<String> list) {
/*LinkedHashSet<String>lhs=new LinkedHashSet<>();
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
lhs.add(iterator.next());
}
for(String lhs2:lhs){
System.out.print(lhs2+"\t");
}*/
//1.创建一个LinkedHashSet集合
LinkedHashSet<String>lhs=new LinkedHashSet<>();
//2.将List集合中所有的元素添加到LinkedHashSet集合
lhs.addAll(list);
//3.将List 集合中元素清除
list.clear();
//4.将LinkedHashSet集合中的元素添加到List集合中
list.addAll(lhs);
}
4>.
TreeSet
4.1.TreeSet的特点
-
①.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
-
TreeSet():根据其元素的自然排序进行排序
-
TreeSet(Comparator comparator) :根据指定的比较器进行排序
-
②. 没有带索引的方法,所以不能使用普通for循环遍历
-
③. 由于是Set集合,所以不包含重复元素的集合
4.2.
自然排序的原理和图解
1.
自然顺序(Comparable)
-
①. TreeSet 类的add()方法中会把存入的对象提升为Comparable类型
-
②. 调用对象的CompableTo()方法和集合中的对象比较
-
③. 根据CompableTo()方法返回的结果进行储存
-
TreeSet底层是一个二叉树,两个叉。小的存储在左边[ 负数 ],大的存储在右边[ 正数 ],相等就不存 [ 0 ]。CompareTo方法,在TreeSet集合如何存储元素取决于compareTo方法的返回值
[ 掌握 ]
-
①. 返回的0,集合中只有一个元素
-
②. 返回-1,集合会将存储的元素倒序
-
③. 返回1,集合会怎么存就怎么取
/ /按照年龄排序
public int compareTo(Object o) {
Person p=(Person)o;
int num=this.age-p.age;//年龄是比较的主要条件
return num==0?this.name.compareTo(p.name):num;//姓名是比较的次要条件
}
TreeSet<Person>ts=new TreeSet<>();
ts.add(new Person("张三",23));
ts.add(new Person("李四",13));
ts.add(new Person("王五",43));
ts.add(new Person("赵六",33));
System.out.println(ts);
//[Person{name='李四', age=13}, Person{name='张三', age=23},
Person{name='赵六', age=33}, Person{name='王五', age=43}]
4.3.
TreeSet存储自定义对象并遍历练习 [ 自然排序 ]
public class Person implements Comparable{
private String name;
private int age;
}
- ①. 按照姓名排序
//按照姓名排序
public int compareTo(Object o) {
Person p=(Person)o;
int num=this.name.compareTo(p.name);
//姓名是主要条件,年龄是次要条件
return num==0?this.age-p.age:num;
}
TreeSet<Person>ts=new TreeSet<>();
ts.add(new Person("张三",23));
ts.add(new Person("张三",25));
ts.add(new Person("王五",43));
System.out.println('张'+0);//24352
System.out.println('李'+0);//26446
System.out.println('王'+0);//29579
System.out.println("ts = " + ts);
//ts = [Person{name='张三', age=23}, Person{name='张三', age=25},
Person{name='王五', age=43}]
- ②. 按照姓名长度进行排序
public int compareTo(Object o) {
Person p=(Person)o;
int length=this.name.length()-p.name.length();//比较长度为主要条件
int num=length==0?this.name.compareTo(p.name):length;//比较内容为次要条件
return num==0?this.age-p.age:num;//比较年龄也为次要条件
}
TreeSet<Person>ts=new TreeSet<>();
ts.add(new Person("zhangsan",23));
ts.add(new Person("lisi",25));
ts.add(new Person("wangwu",43));
System.out.println("ts = " + ts);
//ts = [Person{name='lisi', age=25}, Person{name='wangwu', age=43}, Person{name='zhangsan', age=23}]
4.4.
比较器顺序(Comparator)
-
①. 创建TreeSet的时候可以制定一个Comparator接口
-
②. 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
-
③. 调用的对象是Comparator接口中compare()方法顺序
-
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
@Test
public void fun4(){
/*
需求:将字符串按照长度排序
* */
//Comparator c=new CompareByLen();
//new TreeSet(Comparator );
TreeSet<String>ts=new TreeSet<>(new CompareByLen());
ts.add("aaaaaaaa");
ts.add("z");
ts.add("wc");
ts.add("nba");
ts.add("cba");
System.out.println(ts);//[n, bb, aaa]
}
class CompareByLen /* extends Object*/implements Comparator<String>{//
@Override
public int compare(String s1, String s2) {
//按照字符串长度进行比较
int num=s1.length()-s2.length();//长度为主要条件
return num==0?s1.compareTo(s2):num;//内容为次要条件
}
}
4.5.
TreeSet原理 [ 掌握 ]
1.
自然顺序(Comparable)
-
①. TreeSet 类的add()方法中会把存入的对象提升为Comparable类型
-
②. 调用对象的CompableTo()方法和集合中的对象比较
-
③. 根据CompableTo()方法返回的结果进行储存
2.
比较器顺序(Comparator)
-
①. 创建TreeSet的时候可以制定一个Comparator接口
-
②. 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
-
③. 调用的对象是Comparator接口中compare()方法顺序
-
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
3.
两种方式的区别
-
TreeSet 如果传入Comparator,就优先按照Comparator .
-
TreeSet构造函数什么都不传,默认按照类中Comparable的顺序(没有就报错ClassCastE xception)
//java.lang.ClassCastException: com.itheima.TreeSetDemo4.Person cannot be cast to java.base/java.lang.Comparable
public class Person {
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Test
public void fun1(){
//1.按照年龄来排序
TreeSet<Person>ts=new TreeSet<>();
ts.add(new Person("张三",23));
ts.add(new Person("张三",23));
ts.add(new Person("张三",23));
ts.add(new Person("李四",24));
ts.add(new Person("王五",24));
System.out.println("ts = " + ts);
}
4.6.
练习 [ 掌握 ]
- ①. 在一个集合中储存了无序并且重复的字符串,定义一个方法,让其有序(字典顺序)而且重复的输出
/*
分析:
1.定义一个List集合,并储存重复的无序的字符串
2.定义方法对其排序保留重复
3.打印list集合
* */
//1.定义一个List集合,并储存重复的无序的字符串
ArrayList<String>list=new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("ccc");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("ddd");
list.add("aaa");
list.add("aaa");
//2.定义方法对其排序保留重复
sort(list);
//3.打印list
System.out.println(list);
}
/**
定义方法,排序并保留重复
分析:
1.创建TreeSet集合对象,因为String本身就具备比较功能,但是重复的不会保留,所以我们用比较器
2.将list集合中所有的元素添加到TreeSet集合中,对其排序保留重复
3.清空list集合
4.将TreeSet集合中排好序的元素添加到list集合中
* */
public static void sort(List<String> list){
// 1.创建TreeSet集合对象,因为String本身就具备比较功能,但是重复的不会保留,所以我们用
TreeSet<String>ts=new TreeSet<>(new Comparator<String>() {
public int compare(String s1, String s2) {
int num=s1.compareTo(s2);//比较内容为主要条件
return num==0?1:num;//返回1就不让你返回1 //保留重复
}
});
//2.将list集合中所有的元素添加到TreeSet集合中,对其排序保留重复
ts.addAll(list);
//3.清空list集合
list.clear();
//4.将TreeSet集合中排好序的元素添加到list集合中
list.addAll(ts);
}
- ②. 从键盘接收一个字符串,程序对其中所有字符进行排序 [ 例如键盘输入: helloitcase程序打印: aceehillost ]
[ 重点掌握 ]
//注意这里的自动装箱和拆箱的过程
// 1.键盘录入字符串,Scanner
Scanner sc=new Scanner(System.in);
System.out.println("请键盘输入一个字符串");
String str=sc.nextLine();
//2.将字符串转换为字符数组,可以拿到每一个字符
char[] chars = str.toCharArray();
//3.定义TreeSet集合,传入比较器对字符排序并保留重复
TreeSet<Character>ts=new TreeSet<>(new Comparator<Character>() {
@Override
public int compare(Character s1, Character s2) {
// int num=s1-s2; //自动拆箱
int num=s1.compareTo(s2);
return num==0?1:num;
}
});
//4.遍历字符数组,将每一个字符储存到TreeSet集合中
for (int i = 0; i < chars.length; i++) {
ts.add(chars[i]); //自动装箱: 会使得char基本数据类型转成包装类型
}
//5.遍历TreeSet集合,打印每一个字符
for(Character c:ts){
System.out.print(c);
}
- ③. 程序启动后,可以从键盘接收多个整数,直到输入quit时结束输入,把所有输入的整数倒序排列打印
//1.创建Scanner对象,键盘录入
Scanner sc=new Scanner(System.in);
//2.创建TreeSet集合对象,TreeSet集合中传入比较器
TreeSet<Integer>ts=new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
int num=i2-i1;//这叫自动拆箱,使用i2-i1就是倒序了
//int num=i2.compareTo(i1)
return num==0?1:num;
}
});
//3.无限循环不断接收整数,遇到quit退出,因为退出是quit,所以键盘录入的时候应该都以字符串的形式录入
while(true){
String str=sc.nextLine();//将键盘录入的字符储存在str中
if("quit".equals(str)){
break;
}
//4.判断是quit就退出,不是就将其转换成Integer,并添加到集合中
Integer i=Integer.parseInt(str);//本来这里变成int的,发生了自动装箱 int---Integer
ts.add(i);
}
//5.遍历TreeSet集合并打印每一个元素
for(Integer integer:ts){
System.out.print(integer);
}
9.
Map接口
1>.
Map接口的概述
-
Map [ 双列集合 ]与 Collection [ 单列集合 ] 并列存在
-
Map
<K, V>
这里的K 、V 都应该是引用数据类型 -
HashSet底层依赖HashMap,单列底层依赖双列集合
[ 理解 ]
2>.
Map 集合的功能概述
2.1
添加功能.
-
V put(K key ,V value):
添加元素,返回的是以前的值 -
如果键是第一次储存,就直接储存元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
Map<String,Integer> map=new HashMap<>();
Integer i1=map.put("张三",23);
Integer i2=map.put("李四",23);
Integer i3=map.put("王五",24);
Integer i4=map.put("赵六",25);
Integer i5=map.put("张三",26);//相同的键不储存,把原来的值覆盖 把被覆盖的值返回
System.out.println(map);//{李四=23, 张三=26, 王五=24, 赵六=25}
//这里的i1--i4 都是null是因为开始的时候[张三,23][李四,23]....
// 在集合中没有,这样把原来的值覆盖了,返回的是原来的值
System.out.println(i1);//null
System.out.println(i2);//null
System.out.println(i3);//null
System.out.println(i4);//null
System.out.println(i5);//23
2.2.
删除功能
-
void clear():移除所有的键值对元素
-
V remove(Object key):根据键删除键值对元素,并把值返回
Map<String,Integer> map=new HashMap<>();
map.put("张三",23);
map.put("李四",23);
map.put("王五",24);
map.put("赵六",25);
//根据键删除元素,返回键对应的值
Integer value=map.remove("张三");
System.out.println(value);//23
2.3.
判断功能
-
boolean containsKey(Objecct key):判断是否包含指定的键
-
boolean containsValue(Objecct value):判断是否包含指定的值
//判断是否包含传入的键
System.out.println(map.containsKey("张三"));//true
//判断是否包含传入的值
System.out.println(map.containsValue(23));//true
2.4.
长度功能 [ 掌握 ]
- int size():返回集合中的键值对的个数
Map<String,Integer> map=new HashMap<>();
map.put("张三",26);
map.put("李四",23);
map.put("王五",24);
map.put("赵六",25);
Collection<Integer>coll=map.values();
System.out.println(coll);//[23, 23, 24, 25]
System.out.println(map.size());//4
2.5.
获取功能 [ 掌握 ]
-
V get(Object key)
:根据键获取值 -
Set<K> keySet()
:获取集合中所有的键集合 -
Collection<V>values
:获取集合中所有值集合 -
Set<Map.Entry<K,V>> entrySet()
:拿到所有的键值对对象 -
①. K getKey():得到entrySet中的键
-
②. V getValue():得到entrySet中的值
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 23);
//V get(Object key):根据键获取值
Integer i = map.get("张三");//26
Integer i2=map.get("小智");//没有的话返回null
//1.获取所有的键
Set<String> keySet = map.keySet();
//iterator遍历
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();//获取每一个键
Integer value = map.get(key); //根据键获取值
System.out.println(key + "=" + value);
}
System.out.println("--------------");
//使用增强for循环
for (String key : map.keySet()) {
Intger value=map.get(key);
System.out.println(key + "=" +value );
}
interface Inter{
interface Inter2{
public void show();
}
}
//这里的Inter.Inter2 和Map.Entry一样的
class Demo implements Inter.Inter2{
@Override
public void show() {
}
}
//2.根据键值对对象,获取键和值
//Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象,并储存在Set集合中
Set<Map.Entry<String,Integer>>entrySet=map.entrySet();
//获取每一个对象
Iterator<Map.Entry<String,Integer>>it=entrySet.iterator();
while(it.hasNext()){
//获取每一个Entry对象
//static class Entry<K,V> implements Map.Entry<K,V>{}
Map.Entry<String,Integer>en=it.next();//父类引用指向子类对象
//Entry<String,Integer>en=it.next(); 子类对象
//根据键值对对象获取键
String key=en.getKey();
//根据键值对对象获取值
Integer value=en.getValue();
System.out.println(key+"="+value);
}
//增强for循环
for(Map.Entry<String,Integer> en:map.entrySet()){
System.out.println(en.getKey()+"="+en.getValue());
}
for(Entry<String,Integer> en:map.entrySet()){
System.out.println(en.getKey()+"="+en.getValue());
}
3>.
HashMap [ Map实现类之一 ]
-
①. Map 中的 key 用 Set 来存放,
不允许重复
,即同一个 Map 对象所对应的类,须重写hashCode( )和equals( )方法
-
②. HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCod e 值也相等
-
③. HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
-
注意
:两个HashMap的hashcode相同,则他们的equal()方法不一定相同,若两个HashMap的equal()相同,则他们的hashcode一定相同
-
允许使用null键和null值,与HashSet一样,不保证映射的顺序
4>.
练习
- ①. HashMap集合键是Student值是String的案例
[ 掌握 ]
// HashMap集合键是Student值是String的案例
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
HashMap<Student,String>hm=new HashMap<>();
hm.put(new Student("张三",23),"北京");
String i=hm.put(new Student("张三",23),"上海");
hm.put(new Student("李四",24),"广州");
hm.put(new Student("王五",25),"深圳");
System.out.println(i);
//没有重写equals方法之前,调用的是Object类的,会比较地址值
//没有重写equals方法和 hashCode方法的时候map中的元素有4个,重写后只有3个
//注意这里的张三对应的值是上海,因为把北京覆盖了
System.out.println(hm);//北京
//{Student{name='张三', age=23}=上海, Student{name='李四', age=24}=广州, Student{name='王五',
- ②. 统计字符串中每个字符出现的次数
[ 掌握 ]
思路:
1.键盘随机输入一个字符串
2.将字符串转换成字符数组
3.定义双列集合,存储字符串中字符以及字符出现的次数
4.遍历字符数组,获取每一个字符并将字符存储在双列集合中
5.存储过程做判断,如果集合中不包含这个键,就将该字符当做键,值为1存储
如果集合中包含这个键,就将值+1存储
6.打印双列集合获取字符出现的次数
//1.键盘随机输入一个字符串
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串");
String line = sc.nextLine();
//2.将字符串转换成字符数组
char[] chars = line.toCharArray();
//3.定义双列集合,存储字符串中字符以及字符出现的次数
HashMap<Character,Integer>hm=new HashMap<>();
//4.遍历字符数组,获取每一个字符并将字符存储在双列集合中
for(char c:chars){
//5.如果不包含这个键,就将该字符当做键,值为1存储
/* if(!hm.containsKey(c)){
hm.put(c,1);
}else{
hm.put(c,hm.get(c)+1); //这里发生了自动装箱
}*/
//这里先发生自动拆箱,后发生自动装箱
hm.put(c,!hm.containsKey(c)?1:hm.get(c)+1);
}
//6.打印双列集合获取字符出现的次数
Set<Map.Entry<Character,Integer>> en=hm.entrySet();
//①.迭代器答应
/* Iterator<Map.Entry<Character,Integer>> iterator=en.iterator();
while(iterator.hasNext()){
Map.Entry<Character,Integer> entry=iterator.next();
System.out.println("字母-->"+entry.getKey()+"在字符串中出现的次数是-->"+entry.getValue());
}*/
//②. 增强for
/* for(Map.Entry<Character,Integer> en2:en){
System.out.println("字母-->"+en2.getKey()+"在字符串中出现的次数是-->"+en2.getValue());
} */
//③. Set<K> keySet(); 调用 V get(K) 调用键得到值
for(Character c2:hm.keySet()){
System.out.println("字母-->"+c2+"在字符串中出现的次数是-->"+hm.get(c2));
}
- ③. 单列集合嵌套双列集合
//需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历
public class ArrayListDemo {
public static void main(String[] args) {
//创建ArrayList集合
ArrayList<HashMap<String,String>> list=new ArrayList<>();
//创建HashMap集合并添加键值对元素
HashMap<String,String>hs=new HashMap<>();
hs.put("唐智","岳阳");
hs.put("洋洋","广州");
hs.put("小幸","长沙");
HashMap<String,String>hs2=new HashMap<>();
hs2.put("诸葛亮","三国");
hs2.put("黄盖","三国");
hs2.put("卢布","三国");
HashMap<String,String>hs3=new HashMap<>();
hs3.put("孙悟空","西游");
hs3.put("猪八戒","西游");
hs3.put("沙僧","西游");
//将HashMap添加到ArrayList
list.add(hs);
list.add(hs2);
list.add(hs3);
//遍历ArrayList
Iterator<HashMap<String, String>> iterator = list.iterator();
while(iterator.hasNext()){
//得到每一个HashMap中的对象
HashMap<String,String> hss=iterator.next();
//对得到的每一个HashMap对象进行遍历
for(String key:hss.keySet()){
String value=hss.get(key);
System.out.println(key+"="+value);
}
}
}
}
- ④. 双列集合嵌套单列集合
public class HashMapDemo2 {
public static void main(String[] args) {
//创建HashMap集合
HashMap<ArrayList<String>,String>hm=new HashMap<>();
//创建ArrayList集合,添加元素
ArrayList<String>list=new ArrayList<>();
list.add("小智");
list.add("小幸");
list.add("洋洋");
ArrayList<String>list2=new ArrayList<>();
list2.add("诸葛亮");
list2.add("黄盖");
list2.add("卢布");
ArrayList<String>list3=new ArrayList<>();
list3.add("鹏翔");
list3.add("碰翔");
list3.add("朋翔");
//将ArrayList添加进HashMap集合中
hm.put(list,"岳阳");
hm.put(list2,"三国");
hm.put(list3,"湖南");
//遍历HashMap集合
for(ArrayList<String> key:hm.keySet()){
System.out.println(hm.get(key));
//遍历ArrayList 得到每一个HashMap中的
Iterator<String> iterator = key.iterator();
while(iterator.hasNext()){
String str=iterator.next();
System.out.println("\t"+str);
}
}
}
}
5>.
LinkedHashMap
LinkedHashMap
:使用链表维护添加进Map中的顺序, 故遍历Map时,是按添加的顺序遍历的
LinkedHashMap<String,Integer>lhm=new LinkedHashMap<>();
lhm.put("张三",23);
lhm.put("李四",24);
lhm.put("王五",25);
lhm.put("赵六",26);
System.out.println(lhm);//{张三=23, 李四=24, 王五=25, 赵六=26}
6>.
TreeMap [ 和TreeSet一样 ]
-
自然排序
:TreeMap 的所有的 Key 必须实现 Comparable 接口,调用对象的comparaTo()方法和集合中的对象进行比较,根据CompableTo()方法返回的结果进行储存 [ 所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException ] -
选择器排序
:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口 -
TreeMap集合键是Student值是String的案例
//自然排序
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student s) {
//以年龄为主要条件,姓名为次要条件
int num=this.age-s.age;
return num==0?this.name.compareTo(s.name):num;
}
}
TreeMap<Student,String> tm=new TreeMap<>();
tm.put(new Student("张三",23),"北京");
tm.put(new Student("李四",13),"上海");
tm.put(new Student("王五",33),"广州");
tm.put(new Student("赵六",43),"北京");
System.out.println(tm);
//{Student{name='李四', age=13}=上海, Student{name='张三', age=23}=北京,
// Student{name='王五', age=33}=广州, Student{name='赵六', age=43}=北京}
//选择器排序
TreeMap<Student,String> tm=new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//按照姓名比较
int num=s1.getName().compareTo(s2.getName());
return num==0?s1.getAge()-s2.getAge():num;
}
});
tm.put(new Student("张三",23),"北京");
tm.put(new Student("李四",13),"上海");
tm.put(new Student("王五",33),"广州");
tm.put(new Student("赵六",43),"北京");
System.out.println(tm);
//{Student{name='张三', age=23}=北京, Student{name='李四', age=13}=上海,
//Student{name='王五', age=33}=广州, Student{name='赵六', age=43}=北京}
7>.
Hashtable
- 面试题目
[ 掌握 ]
HashMap和Hashtable的区别
共同点:
底层都是哈希算法,都是双列集合
区别
1.HashMap是线程不安全的,效率高,jdk1.2版本
Hashtable是现场安全的,效率低,jdk1.0版本
2.HashMap可以存储null键和null值
Hashtable不可以存储null键和null值
8>.
操作集合的工具类:Collections [是一个类]
1.
Collections中的概述
-
Collections 是一个操作 Set、List 和 Map 等集合的工具类
-
Collections中都是静态方法
-
①.
public static <T> void sort(List<T>list)
:对集合进行升序 -
②.
public static <T> reverse(List<?> list)
:将集合顺序反转 -
③.
public static <T> shuffle(List<?> list)
:随机置换,可以用来洗牌
ArrayList<String> list=new ArrayList<>();
list.add("b");
list.add("a");
list.add("d");
list.add("c");
System.out.println(list);//[b, a, d, c]
Collections.sort(list);
System.out.println(list);//[a, b, c, d]
//根据默认排序结果获取集合中最大值
System.out.println("max:"+Collections.max(list));//d
System.out.println("开始的时候的排序是"+list);//[b, a, d, c]
Collections.reverse(list);
System.out.println("反转后时候的排序是"+list);//[c, d, a, b]
System.out.println(list);//[b, a, d, c]
Collections.shuffle(list);//反转集合
System.out.println(list); //[d, c, b, a] [a, d, b, c] [a, b, d, c]....
ArrayList<String>list=new ArrayList<>();
list.add("b");
list.add("c");
list.add("a");
list.add("d");
System.out.println(list);
//如果索引包含在列表中,则返回搜索的索引,否则返回(-(插入点)-1)
System.out.println(Collections.binarySearch(list,"c"));//1
System.out.println(Collections.binarySearch(list,"e"));//-5
2.
Collection的练习
ArrayList存储学生对象,使用Collections对ArrayList进行排序
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
思路:
1:定义学生类
2:创建ArrayList集合对象
3:创建学生对象
4:把学生添加到集合
5:使用Collections对ArrayList集合排序
6:遍历集合
public class CollectionsDemo02 {
public static void main(String[] args) {
//创建ArrayList集合对象
ArrayList<Student> array = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("linqingxia", 30);
Student s2 = new Student("zhangmanyu", 35);
Student s3 = new Student("wangzuxian", 33);
Student s4 = new Student("liuyan", 33);
//把学生添加到集合
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
//使用Collections对ArrayList集合排序
//sort(List<T> list, Comparator<? super T> c)
Collections.sort(array, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
//遍历集合
for (Student s : array) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
String、StringBuffer、StringBuilder
1.
String类
1>.String类的概述
-
String 类在java.lang包下,所以使用的时候不需要导包
-
String类代表字符串,JAVA程序中所有的双引号字符串,都是String类的对象,包括 " " [ 就算没有new,也照样是 ]
-
如果在编程的过程中每次感觉字符串的内容发生了改变,那么一定是产生了一个新的字符串对象
2>.
字符串的特点
-
字符串不可变,它们在值在创建后不能被更改
-
虽然String的值是不可变的,但是它们可以被共享
-
字符串效果上相当于字符数组(char[ ]) ,但是底层原理是字节数组(byte[ ])
-
注意:
在jdk1.8前是用char[ ] 数组存储的,JDK1.9中是用byte[ ] 数组存储的
3>.
Stirng类的构造方法
-
public String()
:创建一个空白字符对象,不含有任何内容 -
public String (char[ ]chs)
:根据字符数组的内容,来创建字符串对象 -
public String(byte [ ]bys)
:根据字节数组的内容,来创建字符串对象 -
String s=" abc "
:直接赋值的方式创建字符串对象,内容就是abc[ 推荐 ]
-
注意:直接写上双引号,就是字符串对象
String s1=new String();
System.out.println("s1"+s1);
//public String(char[]chs):根据字符数组的内容,来创建字符串对象
char[] chs={'a','b','c'};
String s2=new String(chs);
System.out.println("s2\t"+s2);
//public String(byte[]bys):根据字节数组的内容,来创建字符串对象
byte[] bys={97,98,99};
String s3=new String(chs);
System.out.println("s3\t"+s3);
//String s="abc":直接赋值的方式创建字符串对象,内容就是abc
String s4="abc";
System.out.println("s4\t"+s4);
4>.
String对象的特点
-
①. 字符串常量池 [ 在方法区中 ]:程序当中直接写上的双引号字符串,就在字符串常量池中
-
②. 通过new创建的字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
重点掌握
String str1="abc";
String str2="abc";
char[]charArray={'a','b','c'};
String str3=new String(charArray);
System.out.println(str1==str2);//true
System.out.println(str1==str3);//fasle
System.out.println(str2==str3);//fasle
5>.
字符串的比较
1.
使用 == 做比较
-
基本类型:比较的是数据值是否相同
-
引用类型:比较的是地址值是否相同
2.
equals方法
-
public boolean equals (Object anObject)
: 参数可以是任何对象,只有参数是一个字符串并且内容相同才会给true;否则返回false -
public boolean equalsIgnoreCase(String str):忽略大小写,进行内容比较
3.
注意事项
-
①.任何对象都能用Object进行接收
-
②.equals 方法具有对称性,也就是a.equals(b)和 b.equals(a)效果一样
-
③.如果比较双方是一个常量一个变量,推荐把常量字符串写在前面
-
推荐:“abc”.eqauls(str) 不推荐:str.equals(“hello”)
char[] chs={'a','b','c'};
String s1=new String(chs);
String s2=new String(chs);
String s3="abc";
String s4="abc";
//比较字符串对象地址值是否相同
System.out.println(s1==s2);//false
System.out.println(s1==s3);//false
System.out.println(s3==s4);//true
System.out.println("=======");
//比较字符串内容是否相同
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//true
System.out.println(s1.equals(s4));//true
6>.
String类的常见面试题
//1.判断定义为Stirng类型的str1和str1是否相等
//常量池中没有这个字符串对象,就创建一个,如果有,就直接引用
String str1="abc";
String str2="abc";
System.out.println(str1 == str2);//true
System.out.println(str1.equals(str2));//true
//2.下面这句话在内存中创建了几个对象
//创建了两个对象,一个在堆内存中,一个在常量池中
String str3=new String("abc");
//3.判断定义为String类型的str4和str5是否相等
//str4 中的地址是堆内存中的,而str5的地址是常量池中的
String str4=new String("abc");
String str5="abc";
System.out.println(str4==str5);/false
System.out.println(str4.equals(str5));//true
Java语言有常量优化机制
//4.判断定义为Stirng类型的str6和str7是否相等
//byte b=3+4 在编译的时候就变成了7,把7赋值给b,常量优化机制
String str6="a"+"b"+"c"; //这是三个常量字符相加
String str7="abc";
System.out.println(str6==str7);//true,java中有常量优化机制
System.out.println(str6.equals(str7));//true
//5. 判断定义为Stirng类型的str8、str9、str10是否相等
//str10 的地址值是堆内存的,而str9的地址值是常量池的
//变量和常量去串连:在StringBuffer[StringBuilder] 通过append()方法把元素添加进去,在通过toString转成String。
所以s3记录的地址值是ox222
String str8="ab";
String str9="abc";
String str10=str8+"c";
System.out.println(str10==str9);//fasle
System.out.println(str10.equals(str9)); //true
7>.
字符串的常用方法 [ 掌握 ]
-
①.
str.length():
获取字符串长度(包括空格) -
②.
int indexOf(String s) :
返回s字符串在当前字符串中首次出现的位置。若没有,返回-1,下标从0开始 -
③.
int lastIndexOf(String s):
返回s字符串最后一次在当前字符串中出现的位置。若无,返回-1,下标从0开始 -
注意:如果lastIndexOf()方法参数中是空字符""(没有空格),那么结果和length()结果一样
-
④.
char charAt(int index):
获取指定索引位置的字符,下标从0开始 -
⑤.
String subString()
:截取字符串,空格占用一个索 引位置 -
str.subString(int begin):
从指定的索引位置截取到最后 -
str.subString(int begin,int end):
从索引位置开始,包括前面,不包括后面 -
⑥.
public String replace(charSequence oldString,charSequence newString)
:将所有出现的老字符串替换成新的字符串,返回替换之后的结果新字符串 -
charSequence的意思就是说可以接受字符串类型
-
⑦. 字母大小写转换
-
String toLowerCase()
:转小写 -
String toUpperCase()
:转大写 -
⑧.
str.trim():
去除两端的空格 -
⑨.
String [ ] split(String regex):
按照regex将当前字符串拆分,拆分为多个字符串,整体返回值为String [ ] -
注意事项:split 方法的参数其实是一个 “正则表达式”; 今天要注意:如果要按照英文句点进行切分,必须写
"\\."
-
⑩.
public char[] toCharArray():
将当前字符串拆分为字符数组作为返回值 -
11.
public byte[] getBytes():
获取当前字符串底层的字节数组 -
public byte[ ] getBytes(Charset charset):默认是utf-8
-
12.
static String valueOf(char [ ] chs)
:将字符数组转换成字符串 -
13.
static String valueOf(int i)
:将int类型的数据转成字符串; -
注意:String类的valueOf()方法可以把任意类型的数据转成字符串
char[]chars="Hello".toCharArray();
//转换称为字符数组
for(int i=0;i<chars.length;i++){
System.out.print(chars[i]+"\t");//H e l l o
}
System.out.println("======");
//转换成字节数组
byte[]bytes="abc".getBytes();
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i]+"\t");
}
System.out.println("======");
//把o换成*号
String str1="How do you do?";
String str2=str1.replace("o","*");
System.out.println("把o换成*号str2: "+str2);
System.out.println("======");
//String[]split()
String str3="aaa,bbb,ccc";
String[]str4=str3.split(",");
for(String str5:str4){
System.out.println(str5);
}
System.out.println("======");
String str6="aaa.bbb.ccc";
//String[]arrStr=str6.split(".");
String[]arrStr=str6.split("\\.");
for (int i = 0; i < arrStr.length; i++) {
System.out.println(arrStr[i]);
}
//重点掌握
//1.byte[ ]getBytes():将字符串转换成字节数组
String s1="abc";
byte[]ch=s1.getBytes();
for (int i = 0; i < ch.length; i++) {
//System.out.print(ch[i]+"\t");
}
System.out.println("\n"+"--------");
String s2="你好你好";
//通过gbk码表将字符串转换成字节数组-- 编码的过程
//编码:把我们看的懂的转换成计算机看得懂的
//gbk中一个中文代表两个字节
//gbk码表的特点:中文的第一个字节,肯定是负数
byte[]arr2=s2.getBytes();
for (int i = 0; i < arr2.length; i++) {
//System.out.print(arr2[i]+"\t"); //97 98 99
}
//2.char[ ]toCharArray():把字符串转换成字符数组
char[]chs=s1.toCharArray();
for (int i = 0; i < chs.length; i++) {
//System.out.print(chs[i]+"\t");//a b c
}
//3.static String valueOf(char[]ch)
//底层是由String类的构造方法完成的
char[]ch1={'a','b','c'};
String str5=String.valueOf(ch1);
System.out.println("valueOf():"+str5);
String str6=String.valueOf(100);
System.out.println("将int 100 变成 字符串100:"+str6);
8>.
String类的判断功能
1.
String类的判断功能
-
boolean equals(Object obj):
比较字符串的内容是否相同,区分大小写 -
boolean equalsIgnoreCase(String str):
比较字符串的内容是否相同,忽略大小写写 -
boolean contains(String str):
判断大字符串中是否包含小字符串 -
boolean startsWith(String str):
判断字符串是否以某个指定的字符串开头 -
boolean endsWith(String str):
判断字符串是否以某个指定的字符结束 -
boolean isEmpty()
: 判断字符串是否为空
2.
" " 和 null 的区别 [ 重点 ]
-
" "是字符串常量,也是一个String类的对象,既然是对象可以调用String类中的方法
-
null 是空常量,不能调用任何的方法,否则会出现空指针异常,null可以给任意的引用数据类型赋值
String s1="heima";
String s2="heima";
String s3="heiMa";
System.out.println(s2.equals(s3));//false
System.out.println(s2.equalsIgnoreCase(s3));//true
String s4="heimaxiaozhi";
boolean contains=s4.contains(s2);
System.out.println("s4中是否包含了s2的内容"+contains);
//重点知识点
System.out.println("-----");
String str1="heima";
String str2="";
String str3=null;
System.out.println(str1.isEmpty());//false
System.out.println(str2.isEmpty());//true
System.out.println(str3.isEmpty());//java.lang.NullPointerExceptioon
9>.
String类的其他方法
-
String replace(char old , char new)
-
String replace(String old , String new)
-
String trim()
:String 类的去除字符串两端的空格 案例 -
int compareTo(String str)
:String的按字典顺序比较两个字符串及案例演示 -
int comareToIgnoreCase(String str)[了解 ]
String s = "heima";
String s2 = s.replace('e', 'x');
System.out.println(s2);//hxima
String s3 = s.replace('o', 'z');//z 不存在,保留原字符不改变
System.out.println(s3);//heima
String s4 = s.replace("ei", "aa");
System.out.println(s4);//haama
String s5 = " abc defg ";
String s6 = s5.trim();
System.out.println(s6);//abc defg
String s7 = "abc";
String s8 = "bcd";
int num = s7.compareTo(s8);
System.out.println(num); //-3 按照码表值比较
String s9 = "黑";
String s10 = "马";
int num2 = s9.compareTo(s10);
System.out.println(num2);
String s11 = "heima";
String s12 = "HEIMA";
int num3 = s11.compareToIgnoreCase(s12);
System.out.println(num3);//0
10>.
遍历字符串 [ 掌握 ]
-
需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串
-
思路:①.键盘录入一个字符串,用Scanner实现
-
②.遍历字符串,首先要能够获取到字符串中的每一个字符
-
public char charAt(int index)
:返回指定索引出的char值,字符串的索引也是从0开始的 -
③.遍历字符串,其次要能够获取到字符串长度
-
public int length():返回字符串的长度
Scanner sc=new Scanner(System.in);
System.out.println("请随机输入一个字符串:");
String str=sc.nextLine();
for (int i = 0; i <str.length() ; i++) {
System.out.println(str.charAt(i));
}
String str="asdasfsa";
char[]chs=str.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.print(chs[i]);
}
11>.
字符串练习
- ①. 统计字符次数
public static void main(String[] args) {
//键盘输入一个字符串,用Scanner实现
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
//要统计三中类型的字符个数,需要定义三个统计变量,初始值都是0
int bigCount=0;
int smallCount=0;
int numberCount=0;
int otherCount=0;
//遍历字符串,得到每一个字符
for (int i = 0; i < line.length(); i++) {
char ch=line.charAt(i);
//判断该字符属于哪种类型,然后对应类型的统计变量+1
if(ch>='A' && ch<='Z'){
bigCount++;
}else if(ch>='a' && ch<='z'){
smallCount++;
}else if(ch>='0'&&ch<='9' ){
numberCount++;
}else{
otherCount++;
}
}
System.out.println("大些字母:"+bigCount+"个");
System.out.println("小些字母:"+smallCount+"个");
System.out.println("数字有:"+numberCount+"个");
}
- ②. 字符串反转
public class Demo4 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.nextLine();
String strT=reverse(str);
System.out.println("反转后的顺序是:"+strT);
}
public static String reverse(String s){
//在方法中把字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并返回
String ss="";
for(int i=s.length()-1;i>=0;i--){
ss+=s.charAt(i);
}
return ss;
}
}
- ③. 按照制定的格式拼接字符串
/*
定义一个方法,把数组{1,2,3} 按照制定格式拼接成一个字符串。参考按照如下:[word1#word2#word3]
格式:[word1#word2#word3]
用到: for 循环、字符串拼接、每个数组元素之前都有一个word字样、分割使用的是#、区分一下是不是最后一个
* */
public class DemoString {
public static void main(String[] args) {
int [] arr={1,2,3};
String strNew=fromArrayToString(arr);
System.out.println(strNew);
}
public static String fromArrayToString(int[]arr){
String str="[";
for(int i=0;i<arr.length;i++){
if(i==arr.length-1){
str+="word"+arr[i]+"]";
}else{
str+="word"+arr[i]+"#";
}
}
return str;
}
}
@Test
public void fun3(){
/*
需求: 把数组中的数据按照指定的格式拼接成一个字符串
如: int[ ] arr={1,2,3};
输出结果: [1,2,3]
* */
int[ ] arr={1,2,3};
String str="[";
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
str+=arr[i]+"]";
}else {
str+=arr[i]+",";
}
}
System.out.println(str);
}
- ④. 将一个字符串的首字母转成大写,其余为小写
// 链式编程 :只要保证每次调用完方法返回的是对象,就可以继续调用
// 将一个字符串的首字母转成大写,其余为小写
// 链式编程 :只要保证每次调用完方法返回的是对象,就可以继续调用
String str="aFDSFfdsrg";
String str2=str.substring(0,1).toUpperCase().concat(str.substring(1,str.length()).toLowerCase());
System.out.println("str2 = " + str2);
- ⑤. 常见对象(在大串中查找小串出现的次数代码实现)
//定义大串
String strMax = "woaiheima,heimabutongyubaodu,heimajiayou";
//定义小串
String strMin = "heima";
//定义计数器变量
int count = 0;
//定义索引
int index = 0;
//定义循环,判断小串是否在大串中出现
while ((index = strMax.indexOf(strMin)) != -1) {
count++; //计算器自增
strMax = strMax.substring(index + strMin.length());
}
System.out.println("小串在大串中出现的次数是" + count);
- ⑥. 随机获取4个英文大写字母和一个数字
定义String getStr()方法
功能描述:
获取长度为5的随机字符串
字符串由随机的4个大写英文字母和1个0-9之间(包含0和9)的整数组成
英文字母和数字的顺序是随机的
//定义一个随机的数字看是否和循环时候是不是相同
public class StringTe {
public static void main(String[] args) {
String str=getStr5();
System.out.println("str = " + str);
}
public static String getStr5(){
//先随机的生成一个0-5之间的数据,如果种子是5,那么实际产生的数据就是0-4
Random r=new Random();
int j = r.nextInt(5);//就的数就是0-4中间的某一个
StringBuilder sb=new StringBuilder();
for(int i=0;i<5;i++){
if(i==j){
sb.append(r.nextInt(10));
}else {
//随机的产生大写字母字符
int bigNum = r.nextInt(26) + 65;
char ch=(char)bigNum;
sb.append(ch);
}
}
String s=sb.toString();
return s;
}
}
public class StringDemoax {
public static void main(String[] args) {
String str=getStr5();
System.out.println("str = " + str);
}
public static String getStr5() {
StringBuilder sb=new StringBuilder();
Random r=new Random();
int randomNum=r.nextInt(10);
for(int i=0;i<5;i++){
if(i==randomNum/2){
sb.append(randomNum);
}else{
char ch=(char)(r.nextInt(26)+65);
sb.append(ch);
}
}
return sb.toString();
}
- ⑦.将第一个大写,其他的小写
已知字符串如下:
"good good study day day up"
请定义方法将字符串中每一个单词的首字母大写,其余的小写
最后打印的结果如下: (各位,千万别 sout("Good Good Study Day Day Up"); 哟)
"Good Good Study Day Day Up"
public class StringDemo {
public static void main(String[] args) {
String str="good good study day day up";
String string=getChange(str);
System.out.println("string = " + string);
}
private static String getChange(String str) {
StringBuilder sb=new StringBuilder();
//1.将字符串转成数组
String[] split = str.split(" ");
//2.遍历数组,将每一个字符
for (int i = 0; i < split.length; i++) {
//3.将字符串第一个字母大写,其他的转小写
String string=split[i].substring(0,1).toUpperCase().concat(split[i].substring(1).toLowerCase());
//4.创建StringBulider对象,把新的字符串添加进去
sb.append(string).append(" ");
}
//5.最后会有一个空格,最后把空格去掉
return sb.toString().trim();
}
}
public static String change2(String str) {
StringBuilder sb=new StringBuilder();
String str2=" ";
int index=0;
while((index=str.indexOf(str2))!=-1){
String strN=str.substring(0,index);
sb.append(strN.substring(0,1).toUpperCase().concat(strN.substring(1).toLowerCase())).append(" ");
str=str.substring(index+1);
if(!str.contains(str2)){
sb.append(str.substring(0,1).toUpperCase().concat(str.substring(1).toLowerCase()));
}
}
return sb.toString();
}
2.
StirngBuffer
1>. StringBuffer的概述
-
java.lang.StringBuffer代表
可变的字符序列
,线程安全可变的字符序列 [ 线程安全,所以效率低 ] -
很多方法与String相同,但
StingBuffer是可变长度的
。 -
StringBuffer是一个容器。
2>.
StringBuffer的构造方法
-
public StringBuffer()
:无惨构造方法 -
public StringBuffer(String str)
:指定字符串内容的字符串缓冲区对象 -
public StringBuffer(int capacity):指定容量的字符缓冲区对象
-
public int capacity():返回当前容量 [ 理论值 ]
-
public int length( )
:返回长度(字符数) [ 实际值 ]
StringBuffer sb = new StringBuffer();
System.out.println(sb.length());//0
System.out.println(sb.capacity());//容器初始容量,理论值 16
StringBuffer sb2 = new StringBuffer("xiaozhi");
System.out.println(sb2.length());//7
System.out.println(sb2.capacity());//16+7=23
3>.
StringBuffer 的添加功能
-
public StringBuffer append(String str):
可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身 -
public StringBuffer insert(int offset,String str):
在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身 -
StirngBuffer 是将字符串缓冲区,当new 的时候是在堆内存创建了一个对象, 底层是一个长度为16的字符数组,当调用添加方法时,不会再创建对象,在不断向原缓冲区添加字符
StringBuffer str=new StringBuffer("xiaozhi");
StringBuffer str2=str.append("6");
System.out.println(str2);//xiaozhi6
StringBuffer str3=str.insert(0,false);
System.out.println(str3);//falsexiaozhi6
4>. StringBuffer的删除功能 [ 了解 ]
-
public StringBuffer deleteCharAt(int index) : 删除指定位置的字符,并返回本身
-
public StringBuffer delete(int start,int end):
删除从指定位置开始指定位置结束的内容,并返回本身[ )
StringBuffer sb=new StringBuffer("xiaozhi");
//删除a这个元素
sb.deleteCharAt(2);
System.out.println(sb);//xiozhi
//删除o这个字符
sb.delete(2,3);
System.out.println(sb);//xizhi
//清空缓冲区
sb.delete(0,sb.length());
System.out.println("清空缓冲区"+sb);
5>.
StringBuffer的替换和反转功能
-
public StringBuffer replace(int start,int end,String str):从start开始到end用str替换
-
public StringBuffer reverse():
反转
StringBuffer sb=new StringBuffer("heima");
sb.replace(0,3,"ttt");
System.out.println(sb);//tttma
sb.reverse();
System.out.println("反转后的顺序是:"+sb);//amttt
6>.
StringBuffer 的截取功能
-
public String subString(int start):
从指定位置截取到末尾 -
public String subString(int start,int end):
截取从指定位置开始到结束位置,包括开始位置,不包括结束位置 -
注意事项:返回值类型不再是StringBuffer,要拿String去接收
StringBuffer sb=new StringBuffer("xiaozhi");
//截取zhi
String str1=sb.substring(4);
System.out.println("str1 = " + str1);
//截取xiao
String str2=sb.substring(0,4);
System.out.println("str2 = " + str2);
7>.
StringBuffer 和 String相互转换 [ 掌握 ]
1.
String — StringBuffer
-
①. 通过带参构造方法
-
②. 通过无参构造方法 和 通过append()方法
//通过构造方法将字符串转换成StringBuffer对象
StringBuffer sb=new StringBuffer("heima");
System.out.println(sb);//这是一个StringBuffer对象了
StringBuffer sb2=new StringBuffer();
sb2.append("heima");
System.out.println(sb2);
2.
StringBuffer – String
-
①. 通过构造方法
-
②. 通过toString()方法
-
③. 通过subString(0,length)
String s1=new String(sb2);
String s3=sb2.toString();
String s4=sb2.substring(0,sb2.length());
3.
StringBuilder
1>.StringBuilder的概述
-
StringBuilder 是一个可变的字符串类,我们可以把它看成一个容器
-
String和 StringBuilder的区别
-
String:内容是不可变的
-
StringBuilder:内容是可变的
2>.
StringBuilder的构造方法
-
public StringBuilder()
:创建一个空白可变字符串对象,不含有任何内容 -
public StringBuilder(String str)
:根据字符串的内容,来创建可变字符串对象
3>.
StringBuilder的添加和反转方法
-
public StringBuilder append(任意类型)
:添加数据,并返回对象本身 -
public StringBuilder reverse()
:返回相反的字符序列
4>. StringBuilder 和String 相互转换
-
①. StringBuilder 转换为String
-
public String toString()
:通过toString()就可以实现把StringBuilder转换为String -
②. String 转换为 StringBuilder
-
public StringBuilder(String s)
:通过构造方法就可以现在把String 转换为StringBuilder
//StringBuffer-->string
StringBuilder sbf=new StringBuilder();
sbf.append("hello");
String str = sbf.toString();
System.out.println(str);
//String-->StringBuffer
String str1="helloStr";
StringBuilder sb=new StringBuilder(str1);
System.out.println(sb);
public class Demo4 {
public static void main(String[] args) {
//定义一个int类型的数组,用静态初始化完成数组元素的初始化
int[]arr=new int[]{1,2,3};
String strT=arrayToString(arr);
System.out.println("输出的结果是:"+strT);
}
public static String arrayToString(int[]arr){
//在方法中用StringBuilder 按照要求进行拼接,并把结果转成String返回
StringBuilder sb=new StringBuilder();
sb.append("[");
for (int i = 0; i <arr.length ; i++) {
if(i==arr.length-1){
sb.append(arr[i]);
}else{
sb.append(arr[i]).append(",");
}
}
sb.append("]");
String s=sb.toString();
return s;
}
}
4.
StirngBuffer 、StringBuilder 和 String
- StirngBuffer 和 StringBuilder 是一样的
1.
StringBuffer 和 StringBuilder 的区别
-
StirngBuffer 是jdk1.0版本的,是线程安全的,效率低
-
StringBuilder 是jdk1.5 版本的,是线程不安全的,效率高
2.
StirngBuffer 、StringBuilder 和 String 的区别
-
String是一个可变的字符序列
-
StirngBuffer 、StringBuilder 是可变的字符序列
5.
StringBuilder的扩容 [ 掌握 ]
- ①.第一种方式:空惨构造
public class Capacity1 {
public static void main(String[] args) {
System.out.println("-----------第一种-----------");
//可变的字符序列
//StringBuilder() 构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
StringBuilder sb = new StringBuilder();
// int length() 返回长度(字符数)
System.out.println("实际存储字符的个数:"+sb.length()); //0
// int capacity() 返回当前容量。
System.out.println("容量"+sb.capacity()); //16
System.out.println("----------添加数据-----------");
sb.append("aaaaaaaaaaaaaaa"); //字符串的长度是15
System.out.println("实际存储字符的个数:"+sb.length()); //15
System.out.println("容量"+sb.capacity()); //16
sb.append("a");
System.out.println("----------当SB的长度为15的时候,又添加了一个字符----------");
System.out.println("实际存储字符的个数:"+sb.length()); //16
System.out.println("容量"+sb.capacity()); //16
sb.append("a");
System.out.println("----------当SB的长度为16的时候,又添加了一个字符----------");
System.out.println("实际存储字符的个数:"+sb.length()); //17
System.out.println("容量"+sb.capacity()); //34 ? 几倍
//int newCapacity = (value.length << 1) + 2; // 原来容器数据的长度 * 2 + 2
//value.length << 1 相当于 16 * 2
}
}
- ②. StringBuilder(int capacity):在创建sb的时候直接指定容量,一般很少用
System.out.println("-----------第二种--------------");
//StringBuilder(int capacity)
//在创建SB的时候直接指定容量,一般很少用
StringBuilder sb1 = new StringBuilder(30);
System.out.println("容量"+sb1.capacity()); //30
System.out.println("长度"+sb1.length()); //0
- ③. 在构造方法中添加字符串
System.out.println("-----------第三种--------------");
StringBuilder sb2 = new StringBuilder("str");
/*
扩容的源码:super(str.length() + 16);
根据构造方法中添加元素的长度+ 默认的容量 == 19
*/
System.out.println("容量"+sb2.capacity()); //19
System.out.println("长度"+sb2.length()); //3
异常
1>.
异常的概述和分类
2>.
Jvm默认是如何处理异常的
-
①. Jvm有一个默认的异常处理机制,就将该异常进行处理。并将该异常的名称,异常的信息,异常出现的位置打印在了控制台上
-
②. 同时将程序停止运行
//看异常从后往前看,先看这里的 java:6 再看 java:14
public class JvmException {
public static void main(String[] args) {
DemoJvm d=new DemoJvm();
int x=d.div(1,0);
System.out.println(x);//ArithmeticException: / by zero
}
}
class DemoJvm{
public int div(int a,int b){//a=10 b=0
// 10/0 被除数是10,除数是0当除数是0的时候违背了算数运算法则,抛出异常
// new ArithmeticExcpetion("/ by zero")
return a/b;
}
}
3>.
try–catch的方式处理异常
try:用来检测异常的;catch:用来捕获异常的;finally:用来释放资源的
异常处理的两种方式
try.....catch.....finally
1.try.....catche
2.try.....catche.....finally
3.try.....finally
throws
-
世界上最真情的相依就是你在try 我在catch,无论你发神马脾气,我都静静接受,默默处理
-
①.当通过trycatch将问题处理了,程序会继续执行
- ②. try后面如果跟多个catch,那么小的异常放前面,大的异常放后面,根据多肽的原理,如果大的放前面,就会将所有的子类对象接受,后面的catch就没有意义了
4>.
编译期异常和运行期异常的区别
-
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
-
编译时异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译
-
运行时异常:无需显示处理,也可以和编译时异常一样处理
5.
Throwable的集中常见方法
-
String getMessage():
获取异常信息,返回字符串 -
String toString():
获取异常类和异常信息,返回字符串 -
void printStackTrace():
获取异常类名和异常信息,以及异常出现在程序中的位置,返回值为void [ Jvm 默认这种方式 ]
try{
System.out.println(1/0);
}catch (Exception e){
//Exception e =new ArithmeticException("/ by zero");
System.out.println(e.getMessage());//获取异常信息 / by zero
//toString():打印异常类名和异常信息
System.out.println(e.toString());//java.lang.ArithmeticException: / by zero
System.out.println(e);//java.lang.ArithmeticException: / by zero
//printStackTrance():JVM 默认这样方式处理
e.printStackTrace();
//at day08.ExcepetionDemo.main(ExcepetionDemo.java:8)
//java.lang.ArithmeticException: / by zero
}
6>.
throws的方式处理异常
-
①. 编译时异常的抛出,必须对其进行处理
-
②. 运行时异常的抛出,可以处理也可以不处理
- throw:在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出
//掌握
throws:
1. 用在方法声明后面,跟的是异常类名
2. 可以跟多个异常类名,用逗号隔开
3. 表示抛出异常,由该方法的调用者来处理
throw:
1. 用在方法体内,跟的是异常对象名
2. 只能抛出一个异常类对象名
3. 表示抛出异常,由方法体内的语句处理
Exception e=new Exception("年龄非法");
throw e;
7>.
finally
1. finally的特点
-
被finally控制的语句体一定会执行
-
特殊情况:在执行到finally之前jvm退出了(比如:System.exit(0))
//return语句相当于是方法的最后一口气,那么在它将死之前会看一
//看有没有finally帮其完成遗愿。如果有就将finally执行后切底返回
try{
System.out.println(10/0);
}catch (Exception e){
System.out.println("除数为0了");
return;
}finally {
System.out.println("一定会执行的");
}
2.
面试题 [ 掌握 ]
1.final,finally和finalize的区别
final:
final可以修饰类,不能为继承
final修饰方法,不能被重写
final修饰变量,只能赋值一次
finally:
finally是try语句中的一个语句体,不能单独使用,用来释放资源
finalize:
是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,
由对象的垃圾回收器调用次方法
2.如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问在return前还是retrun后
会执行,在return前执行
8>.
自定义异常概述和基本使用
1.
异常的注意事项
-
①. 子类重写父类方法时,要求子类抛出的异常要小于父类抛出的异常
-
②. 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内部有异常发生,那么子类只能try,不能throws
2.
如何使用异常处理
- 原则:如果该功能类部可以将问题处理,用try,如果处理不了,交由调用者处理,这里用throws
//Exception是编译时异常
//RuntimeException时运行时异常
public class ScoreException extends Exception/*RuntimeException*/ {
public ScoreException(){
}
public ScoreException(String message){
super(message);
}
}
public class Teacher {
public void checkScore(int score)throws ScoreException{
if(score<0||score>100){
//throw new ScoreException();
throw new ScoreException("你输入的数字有误");
}else{
System.out.println("正确");
}
}
}
public class Demo {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入你的成绩:");
int score=sc.nextInt();
Teacher t=new Teacher();
try {
t.checkScore(score);
} catch (ScoreException e) {
e.printStackTrace();
}
}
}