Java笔记--2025/4/28
注释:
1.单行注释
2.多行注释(不可以嵌套使用,这应该都知道吧)
3.文档注释(java特有):注释内容可以 被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档
用javadoc所解析的类,通常前面加public
暂时不知道IDEA怎么用文档注释
API(Application Programming Interface应用程序编程接口)
API是Java提供的基本编程接口
java提供的类库称作API
对第一个java程序的总结
-
java程序编写--编译--运行的过程
编写:我们将编写的java代码保存在以“.java”为后缀的源文件中
编译:使用javac.exe命令编译我们的java源文件。格式:javac 源文件名.java
运行:使用java.exe命令解释运行我们的字节码文件。格式:java 类名
-
在一个java源文件中可以声明多个class。但是,最多有一个类声明为public的。
public只能加到与文件名同名的类上
-
程序的入口是main方法,格式是固定的
public static void main(String[] args){
}
//虽说是固定的,但其实args可以变,args是arguments(参数)的缩写,可以改为任意变量
//中括号的位置也可以变,可以放在参数的后面
//但是习惯上中括号放在前面,后面参数用args -
输出语句:
System.out.println("Hello,World!");输出之后换行
System.out.print("Hello,World!");//输出之后不换行
System.out.print();//换行 -
每一行执行语句都以";"结束
-
编译的过程:编译以后,会生成一个或多个字节码文件。字节码文件的文件名与java源文件中声明的类名相同,有几个类,就会有几个字节码文件
java集成开发环境(Intergrated Development Environment)
基本语法
(上)变量和运算符
关键字和保留字
关键字(keyword)的定义与特点
-
定义:在java中被赋予特殊含义,用作专门用途的字符串
-
特点:关键字中的所有字母均为小写
保留字(reserved word)
-
在现有java版本中尚未使用,但以后版本中可能作为关键字使用,自己命名标识符的时候要避免使用这些保留字
-
goto、const
标识符(Identifier)
-
定义:Java对各种变量、方法和类等要素命名时使用的字符序列称为标识符
-
技巧:凡是可以自己起名的地方都叫标识符
-
定义合法的标识符规则:
-
由26个英文字母大小写,0-9,_或$组成
-
数字不可以开头
-
不可以使用关键字和保留字,但能包含关键字和保留字
-
Java中严格区分大小写,长度无限制
-
标识符不能包含空格
-
Java中的命名规范
包名:多单词组成时所有字母都小写
类名、接口名:多单词组成时,所有单词首字母大写
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个首字母大写
常量名:所有字母都大写。多单词时,每个单词用下划线连接
java使用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用
变量
先声明,后使用
除了字符串类型,其他类型println时不需要加双引号
如果一个变量未赋值就打印,会报错
变量都定义在其作用域内
同一个作用域内,不可以声明同名的变量
作用域:其定义所在的一对{}内
数据类型
整形:
java各整数类型有固定的表数范围和字段长度,不受具体OS的影响,以保证java程序的可移植性
java的整形常量默认为int型,声明long型常量需后加“l”或“L”
java程序中变量通常声明为int型,除非不足以表示较大的数,采用long
浮点型:
与整数类型相似,Java浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响
浮点数常量有两种表示形式:
-
十进制数形式:如5.12、512.0f、.512(必须有小数点)
-
科学计数法形式:如5.12e2、512E2、100E-2
float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求
double:双精度,精度时float的两倍。通常采用此类型
Java的浮点型常量默认为double型,声明float型常量,需后加“f”或“F”
虽然float只占4字节,但是它的表数范围比long还大
字符类型:char
-
char型数据用来表示通常意义上的”字符“(2字节)
-
Java中的所有字符都用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符
-
字符型变量的三种表现形式:
-
字符型常量是用单引号(‘’)括起来的单个字符。如char a1 = ‘a’;char a2 = ‘中’;char a3 = ‘9’;//只能写一个
-
Java中还允许使用转义字符’'来将其后的字符变成特殊字符常量。如char a3 = ‘\n';//\n表示换行符
-
直接使用Unicode值来表示字符型常量:’\uXXXX'。其中,XXXX代表一个十六进制整数。如\u000a表示\n
-
-
char类型是可以运算的。因为它都对应有Unicode码
//char类型可运算:
System.out.print("hello");
System.out.println("world");
//helloworld
char c1 = '\n';
System.out.print("hello" + c1);
System.out.println("world");
//hello
//world
char c2 = '/t';
System.out.print("hello" + c2);
System.out.println("world");
//hello world
布尔型:boolean
-
只能取两个值之一:true、false
-
常在条件判断、循环结构中使用
boolean isMerried = true;
if(isMerried)
System.out.println("你就不能参加单身party了,很遗憾");
/*字符串中的每个字都是一个字符
比如System.out.println("你就不能参加单身party了\n很遗憾")
输出:你就不能参加单身party了
很遗憾
如果就想输出\n,可以再加一个反斜杠\\n
*/
else
System.out.println("你可以多谈谈女朋友");
基本数据类型之间的运算规则
前提:不包含boolean类型的运算
为什么包含char?
char的字符在ASCLL码和Unicode编码中有对应的数字,运算时用该数字进行
自动类型提升
当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型
此时的容量大小指的是,表示数的范围的大和小。比如float的容量要大于long
从大到小:byte、char、short-->int-->long-->float-->double//byte、char、short之间运算得到最少是int
byte b1 = 2;
int i1 = 127;
//如果byte b2 = b1 + i1;编译不通过,超出byte上限
int i2 = b1 + i1;//i2 = 129,自动提升数据类型
short s1 = 12;
double d1 = s1;
//也是可以的
System.out.println(d1);//12.0
char的讨论:
char c1 = 'a';
int i1 = 15;
//char c2 = c1 + i1;//报错
int i2 = c1 + i1;//char与int运算之后是int
char c1 = '15';
short s1 = '12';
//char c2 = c1 + s1;//报错
//short s2 = c1 + s1;//报错
int i1 = c1 + s1;//char与short运算之后是int
//后续测试:byte与short和char运算之后也是int
byte b1 = 123,b2 = 23;
//short s2 = b1 + b2;// 报错
byte、short、char之间直接运算也是得到int
强制类型转换:自动类型转换的逆运算
需要使用强转符()
可能导致精度损失
//精度损失举例1:
double d1 = 12.9;
int i1 = (int)d1;//截断操作,不会四舍五入
System.out.println(i1);//12
//精度损失举例2:
int i1 = 128;
byte b1 = (byte)i1;
System.out.println(b1);//-128
几种编码情况
//1.
long l = 123123;
System.out. println(l);//123123——为什么long类型不加l没有报错?不加l,会把123123当成int类型,自动类型提升
long l = 124123432434212212;
System.out.println(l);//报错
float f = 12.3 ;//报错,这个不加f是不行的,int中没有小数点
//2.
//整形常量,默认为int类型
//浮点型常量,默认为double类型
byte b = 12;
//byte b1 = b + 1;//编译失败,1默认是int型,与byte运算后得到int
//float f1 = b + 12.3;//编译失败,12.3默认是double型,用float降级了
字符串类型:String
-
String不是基本数据类型,属于引用数据类型
-
使用方式与基本数据类型一致。例如:String str = ”abcd“;
-
一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据。例如:
str = str + "xyz";
int n = 100;
str = str + n;
/*
String类型变量的使用:
1.String属于引用数据类型,翻译为:字符串
2.声明String类型变量时,使用一对""
3.String可以与八种基本数据类型变量做运算,且运算只能是连接运算:+
String s1 = "Hello,world!";
System.out.println(s1);
String s2 = "c";
String s3 = "";
//char c1 = '';//报错,编译不通过
连接运算:结果一定是String类型
int i = 1001;
String s = "学号:";
String S1 = i + s;//+:连接运算
boolean b1 = true;
String s2 = b1 + s1;
System.out.println(s1);//学号:1001
System.out.println(s2);//学号:1001true
判断+是加号还是连接运算,看它两边是否有String类型,如果有,就是连接运算,否则就是加号
char c = 'a';//a97 A65
int num = 10;
String str = "hello";
System.out.println(c + num + str);107hello
System.out.println(c + str + num);ahello10
System.out.println(c + (num + str));a10hello
System.out.println((c + num) + str);107hello
System.out.println(str + num + c);//hello10a
练习:输出* *
System.out.println("* *");//T
System.out.println('*' + '\t' + '*');//F
System.out.println('*' + "\t" + '*');//T
System.out.println('*' + '\t' + "*");//F
System.out.println('*' + ('\t' + "*"));//T
String a = 123;//编译不通过
String a = 123 + "";//T
进制
二进制:0-1,满二进一,ob或oB开头
八进制:0-7,满八进一,数字0开头
十进制:0-9,满十进一
十六进制:0-9及A-F,满十六进一,0x或0X开头。此处A-F不区分大小写
计算机底层都以补码的方式来存储数据!
十进制转二进制:了解
运算符
-
算术运算符
-
赋值运算符
-
比较运算符(关系运算符)
-
逻辑运算符
-
位运算符
-
三元运算符
算术运算符
赋值运算符
=、+=、-=、*=、/=、%=
三元运算符
(条件表达式)?表达式1:表达式2
条件表达式为ture,运算后结果为表达式1。为false,结果为表达式2
-
表达式1和表达式2为同种类型
三元运算符与if-else的联系和区别
-
三元运算符可以简化if-else语句
-
三元运算符要求必须返回一个结果
-
if后的代码块可有多个语句
程序流程控制:
分支结构
if-else语句
-
if(条件表达式){
执行代码块;
} -
if(条件表达式){
执行代码块1;
}
else{
执行代码块2;
} -
if(条件表达式1){
执行代码块1;
}
else if(条件表达式2){
执行代码块2;
}
......
else{
执行代码块n;
}
用键盘获取不同类型的变量:需要使用Scanner类
具体实现步骤:
1.导包:
import java.util.Scanner;
//类似#include,写在class声明之上
2.Scanner的实例化
Scanner scan = new Scanner(System.in);//scan是一个变量名
3.调用Scanner类的相关方法,来获取指定类型的变量
double weight = scan.nextDouble();
int age = scan.nextInt();
boolean gender = scan.nextBoolean();
//char类型的获取,Scanner没有提供相关的方法。只能获取一个字符串
System.out.println("请输入你的性别(男/女");
String gender = scan.next();//"男"
//如果非要用char:
char genderChar = gender.charAt(0);//第一个char索引位置在0,它是把String的每个字符分开,作为类似数组的结构
swtich-case结构
switch(表达式){
case 常量1:
语句1;
//break;----break不是必须要有的,根据实际情况
case 常量2:
语句2;
//break;
...
case 常量n:
语句n;
//break;
default:
语句;
//break;
}
//default类似else,如果前面都没有匹配到,则直接用default.default结构也是可选的
/*
说明:
根据swtich表达式的值,依次匹配各个case中的常量,一旦匹配成功,则调用相应case中的执行语句.
调用完之后程序不会立刻终止,而是知道碰到break;或者程序结束
switch结构中的表达式一定是如下6种类型之一:byte\short\char\int\枚举类型(JDK5.0新增)\String类型(JDK7.0新增)
注意:布尔型严格不能放在表达式中
case只能声明常量,不能声明范围
*/
特殊关键字的使用:break,continue
| 使用范围 | 循环中的作用 | 相同点 | |
|---|---|---|---|
| break | switch-case,循环结构 | 结束当前循环 | 关键字后面不能声明执行语句 |
| continue | 循环结构 | 结束当次循环 | 关键字后面不能执行声明语句 |
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
break;//123
//System.out.prinln("nihaoshijie");//报错
}
System.out.println(i);
}
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
continue;//123567910
//System.out.println("nihaoshijie");//报错
}
System.out.print(i);
}
在嵌套循环中:
break:默认跳出包裹此关键字最近的一层循环
continue:默认跳出包裹此关键字最近的一层循环的一次
带标签的break,continue关键字:
label:for(int i = 1;i <= 10;i++){
for(int j = 1;j <= 10;j++)
if(j % 4 == 0){
break label;//结束指定标签的一层循环结构
//continue label;//结束指定标签的一层循环结构的当次循环
//按理来说标签可以是任意单词
}
System.out.print(i);
}
附加:特殊流程控制语句3
-
return:并非专门用于结束循环的,它的功能是结束一个方法.当一个方法执行到return语句时,这个方法将被结束
-
与break和cotinue不同的是,return会直接结束整个算法,不管这个return处于多少层循环之中
数组:
数组存储数据时,可以使用自动转换类型.但还是建议容器和存储的数据类型保持一致
格式:
数据类型 []数组名;
数据类型 数组名[];
//两者一样,习惯上用第一种
初始化:在内存中,为数组容器开辟空间,将数据存入容器的过程.
数组的静态初始化
//完整格式:
数据类型 [] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
//如:
int [] array = new int[][1,2,3...];//注意前后数据类型一定保持一致
//简化格式:
数据类型 [] 数组名 = {元素1,元素2,元素3...}
数组定义之后长度确定,不能发生改变
//定义数组储存5名学生的年龄
int [] arr1 = {15,12,23,23,12};
int [] arr2 = new int[]{15,12,23,23,12};
//定义数组储存3名学生年龄
String [] arr3 = {"zhangsan","lisi","wangwu"};
String [] arr4 = new String[]{"zhangsan","lisi","wangwu"};
数组的地址值
double [] arr = {1.93,1.75,1.73,1.81};
System.out.println(arr);//[D@776ec8df
//解释下地址值的格式含义:
//[:表示当前是一个数组
//D:表示当前数组里面的元素都是double类型的
//@:表示一个间隔符号.(固定格式,没有什么特别含义)
//776ec8df:才是真正的地址值(16进制)
//平时习惯性的会把这个整体叫做数组的地址值.
数组元素的访问
格式:数组名[索引];
//索引也叫下标,或者角标
//索引特点:从零开始,逐个加一增长,连续不间断
//利用索引对数组中的元素进行访问
//1.获取数组中的元素
//格式:数组名[索引]
int [] arr = {1,2,3,4,5};
//获取数组中的第一个元素值
int number = arr[0];
System.out.println(number);//1
System.out.println(arr[0]);//1
2.把数据存储到数组中:
//格式: 数组名[索引] = 具体数据/变量;//原来数据被覆盖
数组遍历
数组遍历:把数组中所有的内容取出来,之后可以(打印,求和,判断...)
注意:遍历指的是取出数据的过程,不要局限的理解为,遍历就是打印
int []arr = {1,2,3,4,5};
for(int i = 0;i < 5;i++){
System.out.println(arr[i]);
}
//在java中.关于数组的一个长度属性,length
//调用方式:数组名.length
System.out.println(arr.length);//5
//现在用for循环遍历数组就不用数数组的长度了
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//扩展:
//自动的快速生成数组的遍历方式:
//idea提供
数组名.fori//输入之后idea自动补全循环语句
//练习:遍历数组并求和
//定义一个数组,存储1,2,3,4,5
//遍历数组得到每一个元素,求数组里面所有的数据和
int [] arr = {1,2,3,4,5};
int sum = 0;
for(int i = 0;i < arr.length;i++){
sum += arr[i];
}
System.out.println(sum);
//统计个数:
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到的每一个元素,统计数组里面一共有多少能被3整除的数字
int [] arr = {1,2,3,4,5,6,7,8,9,10};
int count = 0;
for(int i = 0;i < arr.length;i++){
if(arr[i] % 3 == 0){
count++;
}
}
System.out.println(count);
//变化数据:
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到每一个元素.
//要求:1.如果是奇数,则当前数字扩大两倍
//2.如果是偶数,则将当前数字减小二分之一
int [] arr = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;i < arr.length;i++)[
if(arr[i] % 2 == 0){
arr[i] = arr[i] /2;
}
else{
arr[i] = arr[i] *2;
}
//System.out.println(arr[i]);//我一开始是这样写的,这样写结果一样,但是不建议这样
//一个循环尽量只做一件事,防止思路混淆+
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i])
}
数组的动态初始化
数组的动态初始化:初始化只指定数组长度,由系统为数组分配初始值
//格式:数据类型[] 数组名 = new 数据类型[数组长度];
//创建
int[] arr = new int[3];
//添加
arr[0] = 12;
//获取
System.out.println(arr[0]);//12
System.out.println(arr[1]);//输出默认初始化值----0
数组默认初始化值的规律:
整数类型----0
浮点数类型----0.0
字符类型----'/u0000',也就是空格
布尔类型----false
引用数据类型----null.(现在默认除了八种基础数据类型以外的都是引用数据类型)
数组的动态初始化和静态初始化的区别
动态初始化:手动指定数组长度,由系统给出默认初始化值
-
只明确元素个数,不明确具体数值,推荐使用动态初始化
-
举例:使用数组容器来存储键盘录入的5个整数
静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度.
-
需求中已经明确了要操作的具体数据,直接静态初始化即可
-
举例:将全班学生的成绩存入数组中,11,22,33
数组常见问题
当访问了数组中不存在的索引,就会引发索引越界异常
数组例题
//求最大值
//已知数组元素为{33,5,22,44,55},请找出数组中最大值并打印在控制台
int [] arr = {33,5,22,44,55};
int max = arr[0];
for(int i = 0;i < arr.length;i++){
//循环的开始条件如果为0,那么第一次循环的时候是自己跟自己比了一下,对结果没有任何影响,但是效率偏低
if(arr[i] > max){
max = arr[i];
}
}
System.out.println(max);
//犯了一个重大错误:一个for中做了多件事
import java.util.Random;
public class _07 {
public static void main(String[] args) {
//生成10个1~100之间的随机数存入数组
//1.求出所有数据的和
//2.求出所有数据的平均数
//3.统计有多少个数据比平均数小
int[] arr = new int[10];
int sum = 0;
Random r = new Random();
for (int i = 0;i < arr.length; i++) {
int number = r.nextInt(100) + 1;//r.nextInt(100)指的是在1~99随机,所以要加一
arr[i] = number;
sum += arr[i];
}
int average = sum / 10;
int count = 0;
for (
int i = 0;
i < arr.length; i++) {
if (arr[i] < average) {
count++;
}
}
System.out.println("所有数据的和为:" + sum);
System.out.println("所有数据的平均数为:" + average);
System.out.println(count + "个数比平均数小");
}
}
//需求:定义一个数组,存入1,2,3,4,5.按照要求交换索引对应的元素
//交换前:1,2,3,4,5
//交换后:5,2,3,4,1
public class _08 {
public static void main(String[] args){
int [] arr = {1,2,3,4,5};
int temp;
temp = arr[0];
arr[0] = arr[4];
arr[4] = temp;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
//打乱数组中的数据
//需求:定义一个数组,存入1~5.要求打乱数组中所有数据的顺序
import java.util.Random;
public class _09 {
public static void main(String[] args) {
int [] arr ={1,2,3,4,5};
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
int j = r.nextInt(arr.length);
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
数组的内存图
回头再看吧,看不下去了
Java内存分配:
| Java内存分配 | |
|---|---|
| 栈 | 方法运行时使用的内存,比如main方法的运行,进入方法栈中执行 |
| 堆 | 存储对象或者数组,new来创建的,都存储在堆内存 |
| 方法区 | 存储可以运行的class文件 |
| 本地方法栈 | JVM在使用操作系统的时候使用,和我们开发无关 |
| 寄存器 | 给CPU使用,和我们开发无关 |
方法
什么是方法?
main方法是程序的主入口,程序从main方法开始
什么是方法?
方法是程序中最小的执行单元,在同一方法中的代码,要么全部执行,要么全部不执行
实际开发中,什么时候用到方法?
重复的代码,具有独立功能的代码可以抽取到方法中
实际开发中,方法有什么好处?
提高代码的复用性和可维护性
方法的格式
方法定义:把一些代码打包在一起,该过程称为方法定义
方法调用:方法定义并不是直接运行的,需要手动调用才行,该过程称为方法调用
最简单的方法定义和调用:
//定义:
public static void 方法名(){
方法体(就是打包起来的代码);
}
//调用
方法名();
方法必须先定义后调用,否则程序报错
//例子:
public static void main(String[] args){
playGames();
}
public static void playGames(){
System.out.println("选英雄");
System.out.println("失败");
}
带参数的方法定义和调用
//定义:
//单个参数:
public static void method(参数){
...
}
//多个参数:
public static void method(参数1,参数2){//各参数以逗号隔开
...
}
方法调用时,参数的数量与类型必须与方法定义中小括号里面的变量一一对应,否则程序将报错.
//调用:
public static void getSum(int num1,int num2){// num1 和 num2 称为形参
int result = num1 + num2;
System.out.println(result);
}
public static void main(String[] args){
getSum(12,23);//35
//12和23称为实参
}
带返回值的方法定义和调用
public static 返回值类型 方法名(参数){
方法体;
return 返回值;
}
//调用:
public static void main(String[] args){
getSum();//直接调用,适用于没有返回值的方法
int sum = getSum(12,23);//赋值调用
System.out.println(getSum(12,23));//输出调用
}
public static int getSum(int num1,int num2){
int result = num1 + num2;
return result;//返回给方法的调用出
}
方法的注意事项:
-
方法不调用就不执行
-
方法和方法之间是平级关系,不能互相嵌套定义
-
方法的编写顺序和执行顺序无关
-
方法的返回值为void,表示该方法没有返回值
-
没有返回值的方法可以省略return不写
-
如果要编写return,后面不能跟具体的数据
-
-
return语句的下面,不能编写代码,因为永远执行不到,属于无效的代码
return关键字
方法没有返回值:可以省略不写.如果书写,表示结束方法
方法有返回值:必须要写,表示结束方法和返回结果
方法的重载
-
在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能
-
每一个方法具有不同的参数类型或参数个数,这些同名的方法,就构成了重载关系
-
简单记:同一个类中,方法名相同,参数不同的方法.与返回值无关
-
参数不同:个数不同,类型不同,顺序不同.(顺序不同可以构成重载,但是不建议)
public static void fn(int a){
//方法体
}
public statc int fn(int a){
//方法体
}
//如上两个方法不构成重载
通常把功能相同的方法定义为同名,定义和调用更方便
//需求:使用方法重载的思想,设计比较两个整数是否相同的方法
//要求:兼容全整数类型(byte,short,int,long)
public class _10 {
public static void main(String[] args) {
compare((byte)10,(byte)20);
byte b1 =10;
byte b2 =20;
compare(b1,b2);
}
public static void compare(byte b1 ,byte b2){
System.out.println(b1 == b2);
}
public static void compare(short s1 ,short s2){
System.out.println(s1 == s2);
}
public static void compare(int i1, int i2){
System.out.println(i1 == i2);
}
public static void compare(long n1 ,long n2){
System.out.println(n1 == n2);
}
}
练习
//复制数组:
//需求:定义一个方法copyOfRange(int[] arr,int from,int to)
//功能:将数组arr中从索引from(包含from)开始,到索引to结束(不包含to)的元素复制到新数组中,将新数组返回
public class _11 {
public static void main(String[] args) {
int[] arr ={1,2,3,4,5,6,7,8,9};
int[] copyArr = copyOfRange(arr,3,7);
for (int i = 0; i < copyArr.length; i++) {
System.out.println(copyArr[i]);
}
}
public static int[] copyOfRange(int[] arr,int from,int to){
int[] newArr = new int[to - from];
//伪造索引的思想,没有能表示索引的变量时考虑
int index = 0;
for(int i = from;i <to;i++){
newArr[index] = arr[i];
index++;
}
return newArr;
}
}
方法调用的基本内存原理
没听,黑马---方法--9--10--11
面向对象
面向:拿.找
对象:能干活的东西
面向对象编程:拿东西过来做对应的事情
01设计对象并使用
类和对象
-
类(设计图):是对对象共同特征的描述
-
对象:是真实存在的具体东西.
在Java中,必须先设计类,才能获取对象
如何定义类?
public class 类名 {
1.成员变量(代表属性,一般是名词)
2.成员方法(代表行为,一般是动词)
3.构造器(后面学习)
4.代码块(后面学习)
5.内部类(后面学习)
}
如何得到对象?
类名 对象名 = new 类名();
定义类的补充注意事项
-
用来描述一类事物的类,专业叫做:Javabean类
在Javabean类中,是不写main方法的
-
编写main方法的类,叫做测试类
我们可以在测试类中创建Javabean的对象并进行赋值调用
-
类名的首字母建议大写,需要见名知意.驼峰模式.
-
一个Java文件中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须成为代码文件名
实际开发中还是建议一个文件夹定义一个class类
-
成员变量的完整定义格式是: 修饰符 数据类型 变量名称 = 初始化值;,一般无需指定初始化值,存在默认值.
| 数据类型 | 明细 | 默认值 |
|---|---|---|
| byte short int long | 0 | |
| 基本类型 | float double | 0.0 |
| boolean | false | |
| 引用类型 | 类 接口 数组 String | null |
//定义女朋友类
public class GirlFriend{//不带main,就是Javabean类
//属性
String name;
int age;
String gender;
//行为
public void sleep(){
System.out.println("女朋友在睡觉");
}
public void eat(){
System.out.priintln("女朋友在吃饭");
}
}
//新建一个测试类文件
public class GirlFriendTest{
public static void main(String[] args){
GirlFriend gf1 = new GirlFriend();
gf1.name = "小诗诗";
gf1.age = 18;
gf1.gender = "萌妹子";
gf1.eat();
gf1.sleep();
}
}
02封装
什么是封装?
封装告诉我们,如何正确设计对象的属性和方法
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为
封装的好处:
-
让编程变得很简单,有什么事,找对象,调方法就行.
-
降低我们的学习成本,可以少学,少记,或者说不用学,不用记对象有哪些方法,有需要时去找就行
private关键字
-
是一个权限修饰符
-
可以修饰成员(成员变量和成员方法)
-
被private修饰的成员只能在本类中才能访问
-
针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作(如:set和get方法)
对于每一个私有化的变量,都要提供get和set方法
-
set方法:给成员变量赋值
-
get方法:对外提供成员变量的值
public class GirlFriend{
private String name;
private int age;
private String gender;
public void setname(String n){
name = n;
}
public String getname(){
return name;
}
public void setAge(int age){
if(a >= 18 && a <= 50){
this.age = age;
}
else{
System.out.println("非法参数");
}
public String setGender(String gender){
this.gender = gender;//前面的gender为成员变量.后面的gender为测试类中赋值的"女",是局部变量
}
public String getGender(){
return gender;
}
}
}
public class GirlFriendTest{
public static void main(String[] agrs){
GirlFriend gf1 = new GirlFriend();
gf1.setName("小诗诗");
gf1.setAge(18);
gf1.setGender("萌妹子");
System.out.println(gf1.getName());
System.out.println(gf1.getAge());
System.out.println(gf1.getGender());
}
}
this关键字
成员变量和局部变量
public class GirlFriend {
private int age;//此处age是成员变量,未赋值默认0
public void method(){
int age = 10;//此处age是局部变量
System.out.println(age);//遵循就近原则,取局部变量age = 10
System.out.println(this.age);//如果就想在此处用成员变量,可以调用this关键字
//使用this关键字,不会从局部变量中调用,而是直接从成员变量中寻找
}
}
this关键字的作用:
可以区别成员变量和局部变量
构造方法
构造方法概述
构造方法也叫作构造器,构造函数
作用:在创建对象的时候,由虚拟机自动调用,给成员变量进行初始化
//构造方法格式:
public class Student{
修饰符 类名(参数) {
方法体;
}
}
构造方法有几种,各自的作用是什么?
-
无参数构造方法:初始化对象时,成员变量的数据均采用默认值
-
有参数构造方法:初始化对象时,同时可以为对象进行赋值
特点:
-
方法名和类名相同,大小写也要一致
-
没有返回值类型,连void都meiy
-
没有具体的数值(不能由return带回结果数据)
执行时机:
-
创建对象时由虚拟机调用,不能手动调用构造方法
-
每创建一次,就会调用一次构造方法
public class Student {
private String name;
private int age;
public Student() {
System.out.println("空参构造");
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String setName() {
this.name = name;
}
public int getAge() {
return age;
}
public int setAge() {
this.age = age;
}
}
public class StudentTest {
Student s = new Student();
//调用了一个空参构造,但是上面没有写任何构造方法,为什么没有报错呢?
//如果我们没有写任何的构造方法,那么虚拟机会自动给我们加一个空参构造方法
//测试该行代码时还未写"带参构造"代码块
Student s = new Student("zhangsan",23);
//系统会自动根据参数选择有参构造,而略过空参构造
//所以 name 和 age 赋值成功,可以调用 getName 和 getAge 获取相应值
//这样调用有参构造,我们可以不必再自己调用set方法
System.out.println(s.getName);//zhangsan
System.out.println(s.getAge);//23
}
构造方法注意事项:
-
构造方法定义:
-
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
-
如果定义了构造方法,系统将不再提供默认的构造方法
-
-
构造方法的重载
-
带参构造方法,和无参构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载
-
-
推荐的使用方式:
-
无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法
-
标准 JavaBean
-
类名需要见名知意,驼峰命名
-
成员变量使用private修饰
-
提供至少两个构造方法
-
无参构造方法
-
带全部参数的构造方法
-
-
成员方法:
-
提供每一个成员变量对应的setXxx()/getXxx()
-
如果还有其他行为,也需要写上
-
//写一个标准JavaBean
public class User {
private String username;
public User(){}
public User(String username){
this.username = username;
}
public String getName(){
return username;
}
}
public class UserTest {
public static void main(String[] args) {
User u = new User("shijiehuizhuan");
System.out.println(u.getName());
}
}
快捷键
-
alt + insert(alt + Fn + insert)-->Constructor
-
-->Select None :创建空参构造
-
-->ctrl + A全选 --> OK :创建有全部参数的构造
-
-
alt + insert -->Getter and Setter-->Ctrl + A全选-->OK :创建每个私有化成员变量的set和get方法
插件PTG:一秒生成标准JavaBean
ctrl + shift + 逗号:一键生成标准javabean
最后生成toString方法还没学,删掉即可
对象内存图:听不进去
补充知识:成员变量,局部变量区别
成员变量:类中方法外的变量,没有上下顺序
局部变量:方法中的变量
| 区别 | 成员变量 | 局部变量 |
|---|---|---|
| 类中位置不同 | 类中,方法外 | 方法内,方法声明上 |
| 初始化值不同 | 有默认初始化值 | 没有,使用之前需要完成赋值 |
| 内存位置不同 | 堆内存 | 栈内存 |
| 生命周期不同 | 随着对象的创建而存在,随着对象的消失而消失 | 随着方法调用而存在,随着方法的运行结束而消失 |
| 作用域 | 整个类中有效 | 当前方法中有效 |
面向对象综合案例
文字版格斗游戏
格斗游戏,每个游戏角色的姓名,血量,都不相同,在选定人物的时候(new对象的时候),这些信息就应该被确定下来
角色1:乔峰,血量:100
角色2:鸠摩智,血量:100
乔峰举起拳头打了鸠摩智一下,造成了XX伤害,鸠摩智还剩下XXX点血
鸠摩智举起拳头打了乔峰一下,造成了XX伤害,乔峰还剩下XXX点血
乔峰举起拳头打了鸠摩智一下,造成了XX伤害,鸠摩智还剩下XXX点血
鸠摩智举起拳头打了乔峰一下,造成了XX伤害,乔峰还剩下XXX点血
鸠摩智K.O.了乔峰
import java.util.Random;
public class Role {
private String name;
private int blood;
//空参
public Role() {
}
//全参
public Role(String name, int blood) {
this.name = name;
this.blood = blood;
}
//name的get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//Blood的get和set方法
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
//定义一个方法用于攻击别人
//思考:谁攻击谁?
//Role r1 = new Role();
//Role r2 = new Role();
//r1.攻击(r2);
//方法的调用者去攻击参数
public void attack(Role role) {
//计算造成的伤害 1~20
Random r = new Random();
int hurt = r.nextInt(20) + 1;
//剩余血量
int remainBlood = role.getBlood() - hurt;
//对剩余血量进行验证,如果为负数了,就修改为0
remainBlood =remainBlood < 0 ? 0 : remainBlood;
//修改一下挨揍的人的血量
role.setBlood(remainBlood);
System.out.println(this.getName() + "举起拳头,打了" + role.getName() + "一下,造成了" + hurt + "点伤害,"
+ role.getName() +"还剩下" + remainBlood + "点血");
}
public class GameTest {
public static void main(String[] args) {
//1.创建第一个角色
Role r1 = new Role("乔峰",100);
//2.创建第二个角色
Role r2 = new Role("鸠摩智",100);
//3.开始格斗,回合制游戏
while(true){
//r1开始攻击r2
r1.attack(r2);
//判断r2剩余血量
if(r2.getBlood() == 0){
System.out.println(r1.getName() + "K.O了" + r2.getName());
break;
}
//r2开始攻击r1
r2.attack(r1);
//判断r1剩余血量
if(r1.getBlood() == 0){
System.out.println(r2.getName() + "K.O了" + r1.getName());
break;
}
}
}
}
System.out,printf("你好啊%s","张三");//在IDEA中输入souf.
//打印出:你好啊张三
//有两部分参数:
//第一部分参数:要输出的内容%s(占位)
//第二部分参数:填充的数据
//前面有几个占位,后面就要有多少个数据
//没有huan'han
对象数组练习
购物车
字符串
String
API和API帮助文档
API (Application Programming Interface):应用程序编辑接口
简单理解:API就是别人已经写好的东西,我们不需要自己写,直接使用即可
Java API : 指的就是JDK中提供的各种功能的Java类
这些类将底层的实现封装起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可
已经学习过的API:
Scanner 键盘录入
Random 随机数
API帮助文档 : 帮助开发人员更好的使用API和查询API的一个工具
字符串学习内容
String StringBuilder StringJonier StringBuffer Pattern Matcher
String概述
java.lang.String类代表字符串 , java程序中的所有字符串文字都为此类对象
创建String对象的两种方式
//01---直接赋值
String name = "尼古拉斯.阿玮";
| 构造方法 | |
|---|---|
| public String() | 创建空白字符串,不含任何内容 |
| public String(String original) | 根据传入的字符串,创建字符串对象(脱裤子放屁) |
| public String(char[] chs) | 根据字符数组,创建字符串对象 |
| public String(byte[] chs) | 根据字节数组,创建字符串对象 |
//根据传入的字符串,创建字符串对象
String s1 = new String("abc");
System.out.println(s1);//abc
//根据字符数组,创建字符串对象
//应用场景:需要改变字符串的内容,可以通过改变数组中的内容来改变
char[] chs = {'a','b','c'};//char[] chs = {'A','b','c'};
String s2 = new String(chs);
System.out.println(s2);//abc-->Abc
//根据字节数组,创建字符串对象
//应用场景:以后在网络中传输的数据其实都是字节信息
//我们一般要把字节信息进行转换,转成字符串,此时就要用到这个构造了
byte[] bytes = {97,98,99,100};
String s3 = new String(bytes);
System.out.println(bytes);//abcd
字符串的比较
String s1 = new String("abc");//记录堆里面的地址值
String s2 = "abc";//记录串池中的地址值
System.out.println(s1 == s2);//false
| == 号比的到底是什么 | |
|---|---|
| 基本数据类型 : 比较的是数据值 | 引用数据类型 : 比较的是地址值 |
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
//直接赋值的字符串会存储在串池中
//当使用双引号直接赋值时,系统会检查该字符在串池中是否存在
//不存在:创建新的
//存在:复用
字符串内容比较:
-
boolean equals方法(要比较的字符串)----完全一样结果才是true,否则为false
-
boolean equalsIgnoreCase(要比较的字符串)----忽略大小写的比较
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);//false
boolean result1 = s1.equals(s2);//比较内容是否相等
System.out.println(result1);//true
boolean result2 = s1.equalsIgnoreCase(s2);//比较内容是否相等,忽略大小写
System.out.println(result2);//true
//1.假设我在键盘中录入一个abc
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");//看源码得知:输入的字符串最终是new出来的
String str1 = sc.next();//如果是字符串类型,next后面不用写数据类型吗
//2.代码中再定义一个字符串abc
String str2 = "abc";
//3,用"=="比较,能一样吗
System.out.println(str1 == str2);//false
//已知正确的用户名和密码,请用程序实现模拟用户登录,总共有三次机会,登录之后,给出相应的提示
public class _13 {
public static void main(String[] args) {
//正确的用户名和密码
String rightUsername = "zhangsan";
String rightPassword = "123456";
Scanner sc = new Scanner(System.in);
//三次机会:
for (int i = 0; i < 3; i++) {
//用户输入用户名和密码
System.out.println("请输入用户名");
String username = sc.next();
System.out.println("请输入密码");
String password = sc.next();
//比较
if (username.equals(rightUsername) && password.equals(rightPassword)) {
System.out.println("成功登录");
break;
} else {
if (i < 2)
System.out.printf("输入错误,请重新输入,您还剩下%d次机会", 2 - i);
System.out.println();
else {
System.out.println("错误次数过多,请稍后再试");
}
}
}
}
}
//为什么用for不用while?
//for:已知循环次数
//while:不知道循环次数,但知道结束循环条件
//遍历字符串
//需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串
//public char charAt(int index):根据索引返回字符
//public int length():返回此字符串的长度
//数组的长度:数组名.length
//字符串长度:字符串对象.length()
import java.util.Scanner;
public class _14 {
public static void main(String[] args) {
//录入一个字符串
System.out.println("请输入一个字符串");
Scanner sc = new Scanner(System.in);
String str = sc.next();
//进行遍历
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
System.out.println(c);
}
}
}
//金额转换
//需求:2135-->零佰零仟零万贰仟壹佰叁拾伍元
import java.util.Scanner;
public class _15 {
public static void main(String[] args) {
//1.键盘录入一个金额
Scanner sc = new Scanner(System.in);
int money;
while (true) {
System.out.println("请输入金额");
money = sc.nextInt();
if(money < 0 || money >9999999){
System.out.println("输入错误,请重新输入");
}
else{
break;
}
}
//用一个变量来表示钱的大写
String moneyStr ="";
//2.得到money里面的每一位数字,再转成中文
while(true){
//从右往左获取数据,因为右侧是数据的个位
int ge = money % 10;
String capitalNumber = getCapitalNumber(ge);
//把转换之后的大写拼接到moneyStr中
moneyStr = capitalNumber + moneyStr;
money =money / 10;
//如果数字上的每一位全部获取到了,那么money记录的就是0,此时循环结束
if(money == 0){
break;
}
}
//在前面补零,补齐七位
int count = 7 - moneyStr.length();
for (int i = 0; i < count; i++) {
moneyStr = "零" + moneyStr;
}
//插入单位
//定义一个数组表示单位
String result = "";
String[] arr = {"佰","拾","万","仟","佰","拾","元"};
//遍历moneyStr,然后把arr的单位插进去
for (int i = 0; i < moneyStr.length(); i++) {
char c = moneyStr.charAt(i);
//把大写数字和单位拼接
result =result + c + arr[i];
}
System.out.println(result);
//定义一个方法把数字变成大写的中文
}
public static String getCapitalNumber(int number) {
//定义数组,让数字跟大写的中文产生一个对应关系
String[] arr = {"零", "壹", "二", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
//返回结果
return arr[number];
}
}
//案例:手机号屏蔽
//String substring(int beginIndex,int endIndex) 截取
//注意点:包头不包尾,包左不包右
//只有返回值才是截取的小串
//对原本字符串是没有任何影响的,因为字符串是不能被更改的
//String substring(int binginIndex) 截取到末尾
//beginIndex和endIndex都是索引
public static void main(String[] args) {
//获取一个手机号码
String phoneNumber = "13112349468";
//截取手机号码前三位
String start = phoneNumber.substring(0,3);
//截取手机号码后面4位
String end = phoneNumber.substring(7);
//拼接
String result = start + "****" + end;
//输出打印
System.out.println(result);
}
}
//敏感词替换
//String replace(旧值,新值)替换
//注意点:只有返回值才是替换之后的结果
public class _18 {
public static void main(String[] args) {
//获取说到的话
String talk = "你玩的真好,以后不要再玩了,TMD,CNM";
//定义一个敏感词库
String[] arr = {"TMD","CNM","SB","MLGB"};
//将里面的敏感词替换为***
for (int i = 0; i < arr.length; i++) {
talk = talk.replace(arr[i],"***");
}
//打印结果
System.out.println(talk);
}
}
StringBuilder
为什么学习StringBuilder?
String s = "";
for(int i = 0,i < 1000000;i++) {
s = s + "abc";
}
System.out.println(s);
//当数据量特别大的时候,拼接速度非常慢
StringBuilder sb = new StringBuilder("");
for(int i = 0;i < 1000000;i++){
sb.append("abc");
}
System.out.println(sb);
//用StringBuilder拼接就非常的快
StringBuilder概述
StringBuilder可以看成一个容器,创建之后里面的内容是可变的
-
作用:提高字符串的操作效率
String s1 = "aaa";
String s2 = "bbb";
String s3 = "ccc";
String s4 = "ddd";
//如果要将这些字符串拼接,因为字符串是不可改变的,所以在拼接的过程中会产生"aaabbb""aaabbbccc"等无用的字符串,非常影响内存和代码的运行效率
StringBuilder构造方法
| 方法名 | 说明 |
|---|---|
| public StringBuilder() | 创建一个空白可变字符串对象,不含有任何内容 |
| public StringBuilder(String str) | 根据字符串的内容,来创建可变字符串对象 |
StringBuilder常用方法
| 方法名 | 说明 |
|---|---|
| public StringBuilder append(任意类型) | 添加数据,并返回对象本身 |
| public StringBuilder reverse() | 反转 |
| public int length() | 返回长度(字符出现的个数) |
| public String toString() | 通过toString()就可以把StringBuilder转换为String |
//常用方法演示
//1.创建对象
StringBuilder sb = new StringBuilder(abc);
//2.添加元素
/*sb.append(1);
sb.append(2.3);
sb.append(true);*/
sb.reverse();
System.out.println(sb);//abc12.3true
//反转
StringBuilder sb = new StringBuilder(abc);
sb.reverse();
System.out.println(sb);/cba
//获取长度
StringBuilder sb = new StringBuilder(abc);
int length = sb.length();
System.out.println(length);//3
//toString方法演示
StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
Sb.append("ccc");
System.out.println(sb)//aaabbbccc,打印结果没问题
//把StringBuilder变回字符串
String str = sb.toString();//可以使用String的一些方法了
System.out.println(str);
//打印:
System.out.println(sb);//什么也没有
//普及:
//因为StringBuilder是java已经写好的类
//java在底层对它做了一些特殊处理
//打印对象不是地址值而是属性值
StringBuilder sb = new StringBuilder("投币");
System.out.println(sb);//投币
链式编程
当我们在调用一个方法的时候,不需要接收它的结果,可以继续调用其他方法
public static void main(String[] args){
int len = getString.substring(1).replace("A","Q").length();
Sytem.out.println(len);//2
}
public static String getString(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");//abc
String str = sc.next();
return str;
}
//利用链式编程,就可以简化StringBuilder方法的使用
StringBuilder sb = new StringBuilder();
sb.append("aaa").append("bbb").append("ccc");
System.out.println(sb);//aaabbbccc
//需求:键盘接收一个字符串,程序判断出该字符是否是对称字符串,并在控制台打印是或不是
//对称字符串111.121
//非对称字符串123123
import java.util.Scanner;
public class _19 {
public static void main(String[] args) {
String s = getString();
boolean result = s.equals(new StringBuilder().append(s).reverse().toString());
System.out.println(result);
}
public static String getString(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String sb = sc.next();
return sb;
}
}
StringJoiner
StringJoiner概述
-
StringJoiner和StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的
-
作用:提高字符串的操作效率,而且代码编写特别简介,但是目前市场上很少有人用
-
JDK8出现的
StringJoiner的构造方法
| 方法名 | 说明 |
|---|---|
| public StringJoiner(间隔符号) | 创建一个StringJoiner的对象,指定拼接时的间隔符号 |
| public StringJoiner(间隔符号,开始符号,结束符号) | 创建一个StringJoiner的对象,指定拼接时的间隔符号,开始符号,结束符号 |
StringJoiner sj = new StringJoiner("---");1---2---3
StringJOiner sj = new StringJoiner(",","[","]");//[1,2,3]
StringJoiner的成员方法
| 方法名 | 说明 |
|---|---|
| public StringJoiner add(添加内容) | 添加数据,并返回对象本身 |
| public int length() | 返回长度(字符出现的个数) |
| public String toString() | 返回一个字符串(该字符串就是拼接之后的结果) |
//1.创建一个对象
StringJoiner sj = new StringJoiner("---");
//2.添加元素(目前只能添加字符串)
sj.add("aaa").add("bbb").add("ccc");
//3.获取长度
int len = sj.length();
System.out.println(len);//15,算上了间隔符号长度,注意空格也会算长度
//.打印结果
System.out.println(sj);//aaa---bbb---ccc
总结
1.String
表示字符串的类,定义了很多操作字符串的方法
2.StringBuilder
一个可变的操作字符串的容器
可以高效地拼接字符串,还可以将容器里面的内容反转
3.StringJoiner
JDK8出现的一个可变的操作字符串的容器,可以高效,方便地操作字符串
在拼接的时候,可以指定间隔符号,开始符号,结束符号
扩展:字符串原理
扩展底层原理1:字符串存储的内存原理
-
直接赋值会复用字符串常量池中的
-
new出来不会复用,而是开辟一个新空间
扩展底层原理2:==号比较的到底是什么?
-
基本数据类型比较数据值
-
引用数据类型比较地址值
扩展底层原理3:字符串拼接的底层原理
-
拼接时如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,会复用串池中的字符串
String s = "a"+"b"+"c";
String s = "abc";
//两种是完全一样的 -
拼接的时候含有变量
-
JDK8以前:会new一个StringBuilder,使用append进行拼接,拼接完成后调用toString方法再转换为String(一个加号,堆内存中至少两个对象,浪费性能,且速度慢)
-
JDK8及以后:根据变量字符串长度预估一个数组的长度,将这些字符串变量都写到数组里,但是预估字符串也需要时间
-
结论:如果很多字符串变量拼接,不要直接+.在底层会创建多个对象,浪费时间,浪费性能
推荐使用StringBuilder或者StringJoiner
扩展底层原理4:StringBuilder提高效率原理图
-
所有要拼接的内容都会往StringBuilder中放,不会创建很多无用的空间,节约内存
扩展底层原理5:StringBuilder源码分析
-
默认创建一个长度为16的字节数组
-
添加的内容长度小于16,直接存
-
添加的内容长度大于16会扩容(原来的容量*2+2)
-
如果扩容之后长度还不够,以实际长度为准,比如初始16,现在需要存36个,则扩容到36
StringBuilder sb = new StringBuilder();
System.out.println(sb.capacity);//16
System.out.println(sb.length);//0
字符串原理小结
集合
长度可变,自动扩容
数组可以存基本数据类型,也可以存引用数据类型
集合可以存引用数据类型,但不能直接存基本数据类型,但可以把基本数据类型转换成它们对应的包装类之后,再进行储存
定义:
ArrayList list = new ArrayList();
泛型:限定集合中的数据类型
ArrayList<String> list = new ArrayList<>();
sout(list);//[]
此时,我们创建的是ArrayList的对象,而ArrayList是java已经写好的一个类
这个类在底层做了一些处理:打印对象不是地址值,而是集合中存储的数据内容
在展示时,会用[]包裹所有的数据
| 方法名 | 说明 |
|---|---|
| boolean add(E e) | 添加元素,返回值表示是否添加成功(不管添加什么都能添加成功,返回值永远是true) |
| boolean remove(E e) | 删除指定元素,返回值表示是否删除成功 |
| E remove(int index) | 删除指定索引的元素,返回被删除的元素 |
| E set(int index,E e) | 修改指定索引下的元素,返回原来的元素 |
| E get(int index) | 获取指定索引的元素 |
| int size() | 集合的长度,也就是集合中元素的个数 |
ArrayList<String> list = new ArrayList<>();
list.add("aaa");//aaa
list.add("bbb");//aaabbb
list.add("ccc");//aaabbbccc
list.remove("aaa");//bbbccc
list.remove(1);//bbb
list.set(0,"aaa");//aaa
list.get(0);//aaa
sout(list.size());1
遍历:
list.size.fori{
String str = list.get(i);
sout(str);
}
//定义一个集合,添加字符串并进行遍历
//遍历格式参照:[元素1.元素2.元素3]
import java.util.ArrayList;
public class bbb {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.print("[");
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
System.out.print(list.get(i));
} else {
System.out.print(list.get(i) +",");
}
}
System.out.print("]");
}
}
| 基本数据类型 | 对应的包装类 |
|---|---|
| byte | Byte |
| short | Short |
| char | Character |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
import java.util.ArrayList;
public class StudentTest {
public static void main(String[] args) {
ArrayList<bbb> list = new ArrayList<>();
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",21);
Student s3 = new Student("wangwu",22);
list.add(s1);
list.add(s2);
list.add(s3);
for (int i = 0; i < list.size(); i++) {
Student stu = list.get(i);
System.out.println(stu.getName() + "," + stu.getAge());
}
}
}
public class Student {
private String name;
private int age;
}//标准JavaBean省略
面向对象进阶
static
static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量
| 被static修饰的成员变量,叫静态变量 | 被static修饰的成员方法,叫静态方法 |
|---|---|
| 特点:被该类所有对象共享,不属于对象而是属于类,随着类的加载,优先于对象存在 | 特点:多用于测试类和工具类中,在JavaBean中很少使用 |
| 调用方式:类名调用(推荐),对象名调用 | 调用方式:类名调用(推荐),对象名调用 |
public class Student(){
private String name;
private static String teacherName;
public void show(){
sout("name" + "teacherName");
}
}//javabean在此省略
public class StudentTest(){
public static void main(){
//方法1:Student.setTeacherName = "wangermazi";
Student stu1 = new Student();
stu1.setName = "zhangsan"
//方法2:stu1.setTeacherName = "wangermazi"
Student stu2 = new Student();
stu2.setName = "lisi";
//方法1和2任意使用一个:
show(stu2);//lisi,wangermazi
}
}
工具类:能帮助我们做一些事情的,但是不描述任何事物的类
JavaBean类:用来描述一类事物的类.比如:Student,Dog,Cat等
测试类:用来检查其他类是否书写正确.带有main方法的类,是程序的入口
工具类需要:
类名见名知意
私有化构造方法
方法定义为静态(方便调用)
工具类示例:
public class ArrayUtil(){
private ArrayUtil(){}//私有化构造方法
public static String printArr(int[] arr){
略//用来打印数组
}
}
public class Test(){
public static void main(){
String str = ArrayUtil.printArr(arr1);//调用工具类的方法
sout(str);
}
}
static注意事项
-
静态方法只能访问静态变量和静态方法
-
非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
-
静态方法中没有this关键字
重新认识main方法
public: 被JVM(虚拟机)调用,访问权限要足够大
static: 被JVM调用,不用创建对象,直接类名访问
因为main方法是静态的,所以测试类中其他方法也是静态的
void: 被JVM调用,不需要给JVM返回值
main: 一个通用的名字,虽然不是关键字,但是被JVM识别
String[] args: 以前用于接收键盘数据录入数据的,现在没用
继承
-
java中提供一个关键字extends,用这个关键字.我们可以让一个类与另一个类建立起继承关系
public class Student extends Person {}
-
Student称为子类(派生类),person称为父类(基类或超类)
使用继承的好处:
-
可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
-
子类可以在父类的基础上,增加其他的功能,使子类更强大
什么时候用继承?
当类和类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑继承,来优化代码
继承的特点:
Java只支持单继承,不支持多继承,但支持多层继承
-
单继承:一个子类只能继承一个父类
-
不支持多继承:子类不能同时继承多个父类
每一个类都直接或者间接的继承于Object
子类只能访问父类中的非私有成员
子类到底能继承父类中的哪些内容?
构造方法: 非私有--不能 私有--不能 如果构造方法能被继承的话,子类的构造方法名与类名就不一样了,不符合规则
成员变量:非私有--能 私有--能
成员方法:非私有--能 私有--不能
此处数据结构没看
继承中:成员变量的访问特点
就近原则:先在局部位置找,本类成员位置找,父类位置找,逐级往上
出现了同名的成员变量怎么办?
-
name:从局部位置开始往上找
-
this.name:从本类成员位置开始往上找
-
从父类成员位置开始往上找
public class Fu {
String name = "Fu";
}
public class Zi extends Fu {
String name = "Zi";
public void Zishow(){
String name = "Zishow";
sout(name);Zishow
sout(this.name);Zi
sout(super.name);//Fu----super调用,直接访问父类最多只能调用一次super
}
}
继承中:成员方法的访问特点
与成员变量一样,就近原则
方法的重写
当父类的方法不能满足子类现在的需求时,就需要进行方法重写
书写格式:在继承体系中,子类出现了和父类一模一样的方法声明时,我们就称子类的这个方法是重写的方法
@Override重写注解
-
@Override是放在重写后的方法上.校验子类重写时语法是否正确
-
加上注解后如果有红色波浪线,表示语法错误
-
建议重写方法都加@Override注解,代码安全,优雅
@Override//如果重写的出错,会在@Override下面出现红色波浪线
public void eat(){
sout("吃意面");
}
方法重写的注意事项和要求:
-
重写方法的名称,形参列表必须与父类中一致
-
子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解:空着不写<protected<public)
-
子类重写父类方法时,返回值类型子类必须小于等于父类
-
建议:重写的方法尽量和父类保持一致
-
只有被添加到虚方法表中的方法才能被重写
继承中:构造方法的访问特点
-
父类中的构造方法不会被子类继承
-
子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
为什么?
-
子类在初始化的时候,有可能会使用到父类中的数据
-
子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
怎么调用父类构造方法的?
-
子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行
-
如果想调用父类有参构造,必须手动写super进行调用
总结:
-
子类不能继承父类的构造方法,但是可以使用super调用
-
子类构造方法的第一行,有一个默认的super()
-
默认先访问父类中无参的构造方法,在执行自己
-
如果想要访问父类有参构造,必须手动书写
this,super使用总结
-
this:理解为一个变量,表示当前方法调用者的地址值
-
super:代表父类存储空间
一些注意:
-
子类在写带参构造方法时,要父类和子类的参数全写
多态
定义:同类型的对象,表现出的不同形态
表现形式:父类类型 对象名称 = 子类对象;
前提:
-
有继承/实现关系
-
有父类引用指向子类对象
Fu f = new Zi();
-
有方法重写
多态调用成员的特点
-
变量调用:编译看左边,运行也看左边
-
方法调用:编译看左边,运行看右边
class Animal{
String name = "动物";
public void show(){
sout("动物----show");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show(){
sout("狗----show");
}
}
class Cat extends Animal{
String name = "猫";
@Override
public void show(){
sout("猫----show");
}
}
public static void main(String[] args){
//创建对象:多态方式
Animal a = new Dog();
sout(a.name);//动物
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码时,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有,编译失败
//运行也看左边,java运行代码的时候,实际获取的就是左边父类中成员变量的值
//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败
//java运行代码的时候,实际上运行的是子类的方法
a.show();//狗----show
//理解:
//Animal a = new Dog();
//现在是用a区调用变量和方法的
//而a是Animal类型的,所以默认都会从Animal这个类去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下来
//成员方法:如果子类中对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的
}
一些问题:
为什么注释@override之后,重写的方法即使不加注释符号也会被注释
多态的优势:
-
在多态形式下,右边的对象可以实现解耦合,便于扩展和维护
Person p = new Student();//如果要改成老师工作,把Student换成Teacher就行了
p.work();//业务逻辑发生改变时,后续代码无需修改
-
定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
多态的弊端:
-
不能调用子类的特有功能----调用方法先看左边,后看右边,编译的时候先检查父类中是否有这种方法,发现没有,报错
解决方案:把调用者a变回子类类型就行了
Dog d = (Dog)a;//强转类型
//转换的时候不能瞎转,如果转换成其他类的类型,就会报错
//比如用多态以动物类型创建了一条狗,不能转成猫,只能强转成狗
关键字istanceof:判断变量是哪种类型
if(a instance Dog){
Dog d = (Dog)a;
}else if(a instance Cat){
Cat d = (cat a);
}else{
sout("无此类型,无法转换")
}
//JDK14新特性:判断强转写在一起
if(a instance Dog d){}//先判断a是否为Dog类型,如果是,强转成Dog类型,转换之后变量名为d,如果不是,不强转
包
包就是文件夹.用来管理各种不同功能的Java类,方便后期代码维护
-
包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意.如:com.itheima.domain
package com.itheima.domain;
public class Student{
}//Student的全类名/全限定名:com.itheima.domain.Student使用其他类时,需要使用全类名.
import com.itheima.domain.Student;//导包
public class Test{
public static void main(String[] args){
Student s = new Student();
}
} -
使用其他类的规则:
-
使用同一个包中的类时,不需要导包
-
使用java.lang包中的类时,不需要导包------String就是定义在java.lang包中的,java.lang包是Java的核心包
-
其他情况都要导包
-
如果同时使用两个包中的同名类,需要用全类名(此时就可以不用导包了)
-
final关键字
final:最终的-->不能被重写
修饰方法:表明该方法是最终方法,不能被重写
修饰类:表面该类是最终类,不能被继承
修饰变量:叫做常量,只能被赋值一次
final int a = 10;赋值之后a就是个常数,不能被再次赋值,且此处必须赋值.这种用法类似C语言中的define
sout(a);//10
用final关键字修饰方法和修饰类很少用,了解即可
常量:实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性
常量的命名规范:
-
单个单词:全部大写
-
多个单词:全部大写,单词与单词之间用下划线隔开
细节:
-
如果final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变
-
如果final修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部可以改变
核心:常量记录的数据是不能发生改变的
权限修饰符
-
权限修饰符:是用来控制一个成员能够被访问的范围的
-
可以修饰成员变量,方法,构造方法,内部类
有四种作用范围由小到大(private<空着不写<protected<public)
| 修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
|---|---|---|---|---|
| private | yes | |||
| 空着不写 | yes | yes | ||
| protected | yes | yes | yes | |
| public | yes | yes | yes | yse |
权限修饰符使用规则:
实际开发中,一般只用private和public
-
成员变量私有
-
方法公开
特例:如果方法中的代码是抽取其他方法中的共性代码,这个方法一般也私有
protected:本包中可以使用,其他包的子类可以使用
接口中的成员特点:
-
成员变量:
-
只能是常量
-
默认修饰符:public static final
-
-
构造方法:没有
-
成员方法:
-
只能是抽象方法
-
默认修饰符:public abstract
-
-
JDK以前:接口中只能定义抽象方法
-
JDK8:
-
JDK9:
接口和类之间的关系:
-
类和类的关系:继承关系,不能多继承,但是可以多层继承
-
类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(如果这样的话,需要把接口中的所有方法全部重写
-
接口和接口的关系:
接口的应用
-
接口代表规则,是行为的抽象.想要哪让哪个类拥有一个行为,就让这个实现对应的接口就行了
-
当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态
适配器设计模式
-
设计模式(Design pattern)是一套被反复使用,多数人知晓的,经过分类 编目的,代码设计经验的总结,使用设计模式是为了可重复用代码,让代码更容易被他人理解,保证代码可靠性,程序的重用性
简单理解:设计模式就是各种套路
-
适配器设计模式:解决接口与接口实现类之间的矛盾问题
写法:用一个实现类作为适配器,对所实现接口的所有默认方法进行空实现(方法体不写),再用InterImpl继承适配器,将需要用的默认方法重写即可
注意:适配器一般会加abstract修饰,因为里面的空实现方法没有意义,所以不会让外界建立适配器的对象
总结:
-
当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式
-
书写步骤:-
-
编写中间类XXXAdapter(适配器),实现对应的接口
-
对接口中的抽象方法进行空实现
-
让真正的实现类继承中间类,并重写需要用的方法
-
为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
-
代码块
代码块就是{}内的东西
| 局部代码块 | 方法里 |
|---|---|
| 构造代码块 | 方法外,类里 |
| 静态代码块 | 类里 |
局部代码块的好处是节约内存,但是现在硬件够硬,不再使用局部代码块了
构造代码块:
-
写在成员位置的代码块
-
作用:可以把多个构造方法中重复代码提取出来
-
执行时机:我们在创建本类对象时会先执行构造代码块在执行构造方法
构造代码块也被逐渐淘汰了,不够灵活
不用构造代码块的话,如果有相同代码怎么办?用this关键字
public Student(){
this(null,0);//给name和age赋初始值null和0
}
public Student(String name,int age){
sout("创建对象");
this.age = age;
this.name = name;
}
或者:把重复代码抽取成方法,如果要用,调用方法即可
静态代码块(常用)
//格式:
static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载时,做一些数据初始化时使用
接口
接口就是一种规则,是对行为的抽象
接口的定义和使用
-
接口用关键字interface定义
public interface 接口名{} -
接口不能实例化
-
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{} -
接口的子类:实现类(要么重写接口中的所有抽象方法,要么是抽象类)
1.接口和类的实现关系,可以是单实现,也可以多实现
public class 类名 implements 接口名1,接口名2{}
2.实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2{}
抽象方法:没有方法体的方法是抽象方法
public abstract void eat();
抽象方法所在的类是抽象类,用abstract修饰
重写接口中的方法:@Override
@Override
public void swim(){
sout("青蛙在游泳");
}
接口中成员的特点
-
成员变量:只能是常量.默认修饰符:public static final
-
构造方法:没有
-
成员方法:只能是抽象方法,默认修饰符:public abstract
-
JDK7以前:接口中只能定义抽象方法
-
JDK8:接口中可以定义有方法体的方法
-
JDK9:接口中可以定义私有方法
-
-
一个类实现了多个接口,就要重写接口中的所有抽象方法
-
如果在两个接口中有重名的方法,有一个类同时接入这两个接口时,就只要重写这两个接口中不重复的方法
接口和接口的关系
继承关系:可以单继承,也可以多继承
注意:如果实现类接入了最下面的接口,那么它就要重写接口中的所有抽象方法
一些注意
-
复习继承条件:
-
子类是父类中的一种
-
一个父类中所有的子类是同一种事物
-
-
如果两个子类继承的方法功能不一样,可以在父类中把方法写成抽象的
-
抽象方法所在的类必须是抽象类
-
子类中的构造方法只要写有参的即可
接口扩展
JDK8之后新增的方法
-
允许接口中定义默认方法.需要用关键字default修饰
-
作用:解决接口升级的问题(如此可以使接口新增功能后,实现类不需要立马做出重写,等到以后需要用到某个规则再重写就行了)
接口中默认方法的定义格式:
-
格式:public default 返回值类型 方法名(参数列表){}
-
范例:public default void show(){}
接口中默认方法的注意事项:
-
默认方法不是抽象方法,所以不强制被重写,但是如果需要被重写,重写的时候要去掉default关键字
-
public 可以省略,但是default不能被省略
-
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
JDK8以后接口中新增的方法
-
允许接口中定义静态方法,需要用static修饰
-
接口中静态方法的定义格式:public static 返回值类型 方法名 (参数列表){}
-
范例:public static void show(){}
接口中静态方法的注意事项:
-
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
-
public 可以省略,但是static 不能省略
-
static 静态方法不能被重写
JDK9之后新增的方法:私有
接口中私有方法的定义格式:
//格式1:
private 返回值类型 方法名 (参数列表){}
//范例1:
private void show(){}
//格式2:
private static 返回值类型 方法名(参数列表){}
//范例2:private static void method({})
私有接口的定义和使用可以更好的解决接口中有重复代码的问题:
public interface InterA{
public default show1(){
sout("show1执行");
sout("日志");//改为调用show()
}
}
public interface InterA{
public default show2(){
sout("show2执行");
sout("日志");//改为调用show()
}
}
//JDK9之前的解决方法
public default show(){
sout("日志");
}
//JDK9之后的解决方法:不再可以被外界调用
private void show(){
sout("日志");
}//用于默认方法
private static void show(){
sout("日志");
}//用于静态方法
内部类
什么是内部类
类的五大成员: 属性 , 方法 , 构造方法 , 代码块 , 内部类
什么是内部类?
在一个类的里面,再定义另一个类
其中一个类是外部类,另一个类是内部类,其他所有类是外部其他类
public class Car{外部类
String carName;
int carAge;
int carColor;
public void show(){
Engine e = new Engine();
sout(e.engineName);//外部类要访问内部类的成员,必须创建对象
}
class Engine{//内部类
String engineName;
String engineAge;
public void show(){
sout(carName);//内部类可以直接访问外部类的成员
}
}
}
内部类表示的事物是外部类的一部分
内部类单独出现没有任何意义
内部类的访问特点:
-
内部类可以直接访问外部类的成员,包括私有
-
外部类要访问内部类的成员,必须创建对象
什么时候用到内部类?B类事物是A类事物的一部分,且B单独存在没有意义,比如:汽车的发动机,ArrayList的迭代器,人的心脏等
内部类的分类
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类(常用)
成员内部类
成员内部类的书写
public class Car{外部类
String carName;
int carAge;
int carColor;
class Engine{//成员内部类
String engineName;
String engineAge;
}
}
-
写在成员位置的(类中,方法外),属于外部类的成员
-
成员内部类可以被一些修饰符所修饰:private , 默认 , protected , public , static等
-
如果用private修饰成员内部类,其他类就不能直接创建成员内部类的对象,只能在外部类中创建
-
-
在成员内部类的里面,JDK16之前不能定义静态变量,JDK16之后才可以定义静态变量
获取成员内部类的对象
方式一:当成员内部类被private修饰时, 在外部类中编写方法,对外提供内部类的对象
public class Outer{
publi Inner getInstance(){//instance:对象
return new Inner();
}
private class Inner{//内部类是私有类,外部其他类不能直接创建它的对象
}
}
//外部其他类有两种创建Inner的方式:
public class a{
//方式一:
Outer o = new Outer();
object i = o.getInstance();
//方式二:
Outer o = new Outer();
sout(o.getInstance());
}
方式二:当成员内部类被非私有修饰时,直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
范例:Outer.Inner oi = new Outer().new Inner();
//面试题:??填什么能输出对应值
public class Outer{
private int a = 10;
class Inner{
int a = 20;
public void show(){
int a = 30;
sout(??);//10
sout(??);//20
sout(??)://30
}
}
}
//答案:a , this.a , Outer.this.a
静态内部类
静态内部类是成员内部类的一种,再成员内部类前加static修饰
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态内部类中非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
局部内部类
-
定义:将内部类定义在放法里面就叫作局部内部类,类似于方法里面的局部变量
-
外界是无法直接使用局部内部类的,需要现在方法内部创建对象猜可以使用
-
该类可以直接访问外部类的成员,也可以访问方法内部的局部变量
匿名内部类(重点)
匿名内部类本质上就是隐藏了名字的内部类
//格式:
new 类名或接口名(){
重写方法;
};
//举例:new Inter(){
public void show(){
}
};
其中{
public void show(){
}
}才是没有名字的类,它是Inter的实现类,而对于这个整体,我们可以理解为Inter接口的实现类对象,可以把这个对象赋值给一个Inter类对象
//方法重写:重写接口或者类中的所有抽象类
匿名内部类的应用:
//需求:在测试类中调用下面的method方法
public class test{
public static void method(Animal a){
a.eat();
}
Dog d = new Dog();
method(d);
//Dog类只需要用一次,还需要单独定义一个类,太麻烦了
}
//以前的方式:自己写一个子类继承Animal类,再创建子类的对象,传递给method方法
public class Dog extends Animal{
@Override
public void eat(){
sout("狗吃骨头");
}
}
//现在,用匿名内部类
method(
new Animal(){
@Override
public void eat(){
sout("狗吃骨头");
}
}
);
-
什么是匿名内部类?
-
隐藏了名字的内部类(名字不需要用户自己起,Java会自动起名),可以写在成员位置,也可以写在局部位置
-
-
匿名内部类的格式?
new 类名或者接口名(){
重写方法;
}
-
格式的细节
包含了继承或实现,方法重写,创建对象
整体就是一个类的子类或者接口的实现类对象
-
使用场景
当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象.如果实现类只需要使用一次,就可以用匿名内部类简化代码
阶段综合项目:拼图小游戏puzzlegame(GUI)
主界面分析
JFrame:最外层的窗体
JMenuBar:最上层的菜单
JLabel:管理文字和图片的容器
上述三个都是组件之一
创建主界面1
//需求:
//到IDEA中创建一个宽603像素,高680像素的游戏主界面
//到IDEA中创建一个宽488像素,高430像素的登陆界面
//到IDEA中创建一个宽488像素,高500像素的注册界面
创建主界面2
//用继承改写上述的主界面,并思考用继承的好处:把代码可以分类来写
//设置一些
菜单制作
//JMenuBar是Java中对应菜单的类
//JMenuBar是一个菜单长条,里面可以存放多个JMenu对象,而每一个JMenu对象里又有许多JMenuItem
/*
1.先创建JMenuBar
2.再创建JMenu
3.再创建JMenuItem
4.把JMenuItem放到JMenu里面
5.把JMenu放到JMenuBar里面
6.把整个菜单JMenuBar添加到整个JFrame界面中
*/
添加图片
//Imageicon是Java中对应图片的类
//JLable管理区域,可以用来管理图片和文字,在管理图片时,可以对图片的宽高,位置,还有边框进行设置,默认是展示图片在界面的正中央
//初始化图片
//1.创建一个ImageIcon的对象
//2.创建一个JLable的对象(管理容器)
//3.把管理容器添加到界面中
//指定图片在界面中的位置
//坐标:以左上角为原点,横为x轴,纵为y轴
//创建界面时会自动生成三个部分,标题,菜单,窗体
//组件都是放置在窗体中的.在放置时,默认居中.如果想要指定位置,需要用this.setLayout(null);取消默认
//窗体要用getContentPane()调用
打乱图片顺序
//定义一个数组.把每一张图片定义为数组中的一个数,打乱数组中数的顺序,图片的顺序也随之打乱
//因为有行和列的问题,定义二维数组方便管理
//一般来说,长度确定的用数组,长度不确定的用集合
//练习:打乱一维数组中的数据
int[] tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//要求:打乱一维数组里面的数据,并按照4个一组的方式添加到二位数组中
//解法:遍历数组.得到每一个元素.拿着每一个元素跟随机索引上的数据进行交换
Random r = new Random();
for(int i = 0;i < tempArr.length;i++){
//获取到随机索引
int index = r.nextInt(tempArr.length);
//拿着遍历到的每一个数据,跟随机索引上的数据进行交换
int temp = tempArr[i];
tempArr[i] = tempArr[index];
tempArr[index] = temp;
}
//创建二维数组
int[][] data = new int[4][4];
//给二维数组添加数据
//解法1:遍历一维数组tempArr,得到每一个元素,把每一个元素依次添加到二维数组当中
for(int i = 0;i < tempArr.length;i++){
data[i / 4][i % 4] = tempArr[i];
}
//解法2:遍历二维数组,给里面的每一个数据赋值
int index = 0;
for(int i = 0;i < data.length;i++){
for(int j = 0;j < data[i].length;j++){
data[i][j] = tempArr[index];
index++;
}
}
事件
事件就是可以被组件识别的操作
-
事件源:按钮,图片,窗体...
-
事件:某些操作,如鼠标单击,鼠标划入...
-
绑定监听:当事件源上发生了某个事件,则执行某段代码
-
KeyListener:键盘监听(键盘相关一系列动作)
-
MouseListener:鼠标监听(鼠标相关一系列动作)
-
ActionListener:动作监听(包含鼠标左键点击和空格)
-
//创建一个按钮对象
JButton jtb = new JButton("点我啊");
//设置位置和宽高
jtb.setBounds(0,0,100,50);
//给按钮添加动作监听
jtb.addActionListener(new MyActionListener());
//把按钮添加到界面中
JFrame.getContentPane().add(jtb);
public classMyActionLIstender implements ActionListener {
@Override
public void actionPerformed(ActionEvent e){
System.out.println("按钮被点击了");
}
}
//如果一个实现类只会被一个按钮使用,可以用匿名内部类,这样就不用再创建实现类了
jtb.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvernt e){
sout("不要点我");
//获取当前被操作的那个按钮对象
e.getSource();
}
});
//第三种添加动作监听的方法:直接在让写界面的类实现ActionListener
//在第一种方法中我们需要new一个MyActionListener实现类,但在这里,我们只需要调用this就行,this调用的是本类中重写的ActionListener的方法
//getSourse()的使用方法
Object sourse = e.getSourse();
if (sourse == jtb1){
jtb1.setSize(200,200);
}
鼠标监听机制-MouseListener
划入,按下,松开,划出
其中按下和松开被归为单击动作
如果我想监听一个按钮的单击事件,有几种方式?
-
动作监听
-
鼠标监听中的单击事件
-
鼠标监听中的松开事件
| 方法 | 描述 |
|---|---|
| mouseClicked(MouseEvent e) | 在组件上单击(按下并释放)鼠标按钮时调用 |
| mouseEntered(MouseEvent e) | 当鼠标进入组件时调用 |
| mouseExited(MouseEvent e) | 当鼠标退出组件时调用 |
| mousePressed(MouseEvent e) | 在组件上按下鼠标时调用 |
| mouseReleased(MouseEvent e) | 在组件上释放鼠标按钮时调用 |
键盘监听机制-KeyLIstener
| 方法 | 描述 |
|---|---|
| ( | 按下键时调用。 |
| ( | 当键已被释放时调用。 |
| ( | 键入键时调用。(一般不用,有局限性,有些比如ctrl,alt键不能输入) |
//一般是给整个窗体添加键盘监听
this.addKeyListener(this);//括号里写KeyListener的实现类对象,可以让本类实现KeyListener接口,在这里直接调用this
//调用者this:本类对象,也就是当前的界面对象,表示要给整个界面添加监听
//addKeyListener:表示要给本界面添加键盘监听
//参数this:当事件触发之后,会执行本类中的对应代码
//细节1:如果按住一个按键没有松开,那么会重复调用keyPeressed()方法
//细节2:键盘中那么多按键,怎么区分呢?每一个按键都有一个编号与之对应
//获取键盘上每一个按键的编号
int code = e.getKeyCode();//虽然ABCD等编号与ASCII码表中大写字母编号一样,但是这个编号与ASCII码表没什么关系
美化界面
//1.将图片移动到中间偏下的地方,通过给图片的位置横纵坐标添加位移,来改变图片位置
//2.给图片添加一个背景图
//添加图片的细节:
//先加载的图片在上方,后加载的图片塞在下面
//3.给每张图片添加边框
setBorder();//Border是一个接口,需要实现
//4.优化路径
//把绝对路径改为相对路径
路径分为两种
-
绝对路径:一定是从盘符开始.C: D:
-
相对路径:不是从盘符开始的
-
相对路径是相对当前项目而言的
-
实际开发中,推荐写相对路径
图片加载不出来,一般是路径写错了,路径写错并不会报错,只是没有图片
上下左右移动
-
本类实现KeyListener接口,并重写所有抽象方法
-
给整个界面添加键盘监听事件
-
统计一下空白方块对应的数字0在二维数组的位置
-
在keyReleased方法中实现移动逻辑
-
Bug修复:
-
当空白块在边界时,无法继续朝边界移动
-
查看完整图片
作弊码
判断胜利
其实就是判断二维数组中的数字是否按照顺序进行排列
如果按照顺序进行排列的,那么显示胜利的图片
实现步骤:
-
定义一个正确的二维数组win
-
在加载图片之前,先判断一下二维数组中的数字跟win数组中是否相同
-
如果相同展示胜利图标
-
如果不相同不展示胜利图标
游戏打包exe要考虑的因素
1.一定要包含图形化界面
2.代码要打包起来
3.游戏用到的图片也要打包起来
4.JDK也要打包
游戏打包exe的核心步骤
-
把所有代码打包成一个压缩包,jar后缀的压缩包
-
把jar包转换成exe的安装包
-
把第二步的exe,图片,JDK整合在一起,变成最终的exe安装包
Objects
Objects是一个工具类,提供了一些方法去完成一些功能
需要重点学习的Objects成员方法
public static String toString(Object o) // 获取对象的字符串表现形式
public static boolean equals(Object a, Object b) // 比较两个对象是否相等
public static boolean isNull(Object obj) // 判断对象是否为null
public static boolean nonNull(Object obj) // 判断对象是否不为null
需要了解的Objects成员方法
public static <T> T requireNonNull(T obj)
// 检查对象是否不为null,如果为null直接抛出异常;如果不是null返回该对象;
public static <T> T requireNonNullElse(T obj, T defaultObj)
// 检查对象是否不为null,如果不为null,返回该对象;如果为null返回defaultObj值
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
// 检查对象是否不为null,如果不为null,返回该对象;
// 如果为null,返回由Supplier所提供的值
BigInteger(大整数)
BigInteger构造方法
public BigInteger(int num, Random rnd) //获取随机大整数,范围:[0 ~ 2的num次方-1]
public BigInteger(String val) //获取指定的大整数
public BigInteger(String val, int radix) //获取指定进制的大整数
下面这个不是构造,而是一个静态方法获取BigInteger对象
public static BigInteger valueOf(long val) //静态方法获取BigInteger的对象,内部有优化
BigInteger对象一旦创建,内部记录的值不能发生改变
public static void main(String[] args) {
//获得随机大整数
Random r = new Random();
BigInteger bd1 = new BigInteger(4,r);
//或者两行合并:BigInteger bd1 = new BigInteger(4,new Random());
BigInteger bd2 = new BigInteger(4,new Random());
System.out.println(bd1);//13
System.out.println(bd2);//14
//获得指定的大整数
BigInteger bd3 = new BigInteger("1000");
System.out.println(bd3);//1000
//细节:
//字符串中的数字必须是整数
//获得指定进制的大整数
BigInteger bd4 = new BigInteger("10000",2);//意思是前面的10000是二进制数
System.out.println(bd4);//输出的是二进制数的十进制格式:16
//细节:
//1.字符串中的数字必须是整数
//2.字符串中的数字必须与进制吻合,比如二进制中只能存在0和1
//静态方法获取BigInteger的对象,内部有优化
//细节:
//1.能表示的范围较小.在long的取值范围之内,如果超出long的范围就不行了
//2.在内部对常用的数字: -16~16 进行了优化
//提前把 -16~16 先创建好BigInteger的对象,如果多次获取不会创建新的
BigInteger bd5 = BigInteger.valueOf(100);
System.out.println(bd5);//100
BigInterger bd6 = BigInteger.valueOf(16);
BigInteger bd7 = BigInterger.valueof(16);
System.out.println(bd6 == bd7);//true
//对象一旦创建,内部的数据不能发生改变
BigInteger bd8 = BigInteger.valueOf(1);
BigInteger bd9 = BigInteger.valueOf(2);
BigInteger result = bd8.add(bd9);
System.out.println(result);//3
System.out.println(bd8);//1
System.out.println(bd9);//2
}
System.out.println("Long.MAX_VALUE"); //可以获取Long能存储的最大长度
BigInteger构造方法小结:
-
如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。
-
如果BigInteger表示的超出long的范围,可以用构造方法获取。
-
对象一旦创建,BigInteger内部记录的值不能发生改变。
-
只要进行计算都会产生一个新的BigInteger对象
常见成员方法总结
public BigInteger add(BigInteger val) //加法
public BigInteger subtract(BigInteger val) //减法
public BigInteger multiply(BigInteger val) //乘法
public BigInteger divide(BigInteger val) //除法
public BigInteger[] divideAndRemainder(BigInteger val) //除法,获取商和余数
public boolean equals(Object x) //比较是否相同
public BigInteger pow(int exponent) //次幂、次方
public BigInteger max/min(BigInteger val) //返回较大值/较小值
public int intValue(BigInteger val) //转为int类型整数,超出范围数据有误
代码演示
BigInteger bd1 = BigInteger.valueOf(10);
BigInteger bd2 = BigInteger.valueOf(9);
//加法
System.out.println(bd1.add(bd2));//19
//减法
System.out.println(bd1.subtract(bd2));//1
//乘法
System.out.println(bd1.multiply(bd2));//90
//除法
System.out.println(bd1.divide(bd2));//1
//除法,获得商和余数
//错误示范
//System.out.println(bd1.divideAndRemainder(bd2));//[Ljava.math.BigInteger;@1b6d3586
//正确示范:
BigInteger[] arr = bd1.divideAndRemainder(bd2);
System.out.println(arr[0]);//1
System.out.println(arr[1]);//1
System.out.println(arr.length);//2
//比较是否相同
System.out.println(bd1.equals(bd2));//false
//次幂、次方
System.out.println(bd1.pow(3));//1000
//返回较大值/较小值
System.out.println(bd1.max(bd2));//10
//转为int类型整数,超出范围数据有误
int i = bd1.intValue();
//同理
double d = bd1.doubleValue();
System.out.println(i);//10
System.out.println(d);//10.0
BigInteger bd3 = BigInteger.valueOf(2147483648L);//int的最大值为2147483647
System.out.println(bd3.intValue());// - 2147483648,超出范围有误
BigInteger底层存储方式
对于计算机而言,其实是没有数据类型的概念的,都是0101010101,数据类型是编程语言自己规定的,所以在实际存储的时候,先把具体的数字变成二进制,每32个bit为一组,存储在数组中。
数组中最多能存储元素个数:21亿多
数组中每一位能表示的数字:42亿多
理论上,BigInteger能表示的最大数字为:42亿的21亿次方。
但是还没到这个数字,电脑的内存就会撑爆,所以一般认为BigInteger是无限的。
总结
-
BigInteger表示一个大整数
-
如何获取BigInteger对象?(常用)
-
BigInteger b1 = BigInteger.valueOf(0.1);
-
BigInteger b1 = new BigInteger("整数");
-
-
常见操作
-
加:add
-
减:subtract
-
乘:multiply
-
除:divide , divideAndRemainder
-
比较:equals , max , min
-
次幂:pow
-
转成整数:intValue , longValue
-
BigDecimal
计算机中的小数(引子)
一个浮点数在二进制中由整数部分和小数部分组成
十进制:69.875 -->整数部分二进制:0100 0101
-->小数部分二进制:111
不同小数的二进制的差距:
69.875 : 111
0.9 : 11100110011001100110011001100110011001101,共45位
0..226 : 0011001110110110010001011010000111001010111000000100001,共55位
| 类型 | 占字节数 | 总bit位数 | 小数部分bit位数 |
|---|---|---|---|
| float | 4个字节 | 32个bit位 | 23个bit位 |
| double | 8个字节 | 64个bit位 | 52个bit位 |
小数部分超出的bit位数只能舍弃了
BigDecimal的作用
-
用于小数的精确运算
-
用来表示很大的小数
如何创建BigDecimal的对象?
-
通过传递double类型的小数来创建对象
-
这种方式可能是不精确的,不建议使用
-
-
通过传递字符串表示的小数来创建对象
-
通过静态方法获取对象
-
如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法
-
如果要表示的数字比较大,超出课double的取值范围,建议使用构造方法
-
如果我们传递的是0 ~ 10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new
/*构造方法获取BigDecimal的对象
public BigDecimal(Double val)
public BIgDecimal(String val)
静态方法获取BigDecimal的对象
public static BigDecimal valueOf(Double val)
*/
//1,通过传递double类型的小数来创建对象
//细节:这种方式可能是不精确的,不建议使用
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1);//0.01000000000000000020816681711721685132943093776702880859375
System.out.println(bd2);//0.0899999999999999966693309261245303787291049957275390625
//2.通过传递字符串表示的小数来创建对象
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
System.out.println(bd3);//0.01
System.out.println(bd4);//0.09
System.out.println(bd3.add(bd4));//0.10
//3.通过静态方法获取对象
BigDecimal bd5 = BigDecimal.valueOf(10);
System.out.println(bd5);
//细节:
//1.如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法
//2.如果要表示的数字比较大,超出课double的取值范围,建议使用构造方法
//3.如果我们传递的是0 ~ 10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new
BigDecimal的使用
public static BigDecimal valueOf(double val) //获取对象
public BigDecimal add(BigDecimal value) // 加法运算
public BigDecimal subtract(BigDecimal value) // 减法运算
public BigDecimal multiply(BigDecimal value) // 乘法运算
public BigDecimal divide(BigDecimal value) // 除法运算,除不尽的话不能用,要用下面的方法
public BigDecimal devide(BigDecimal value,精确几位,舍入模式) //除法
其中舍入模式:RoudingMode.HALF_UP 四舍五入,具体的舍入方法在api中查询
舍入模式:去尾法等
底层存储方式
把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。
总结
-
BigDecimal的作用是什么?
-
表示较大的小数和解决小数运算精度失真问题
-
-
BigDecimal的对象如何获取?
-
BigDecimal bd1 = new BigDecimal("较大的小数");
-
BIgDecimal bd2 = BigDecimal.valueOf(0.1);
-
-
常见操作
-
加:add
-
减:subtract
-
乘:multiply
-
除:divide(四舍五入 : RoundingMode.HALF_UP)
-
正则表达式
正则表达式可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性
集合进阶
Collection(单列集合)
List系列集合:添加的元素是有序,可重复,有索引的
-
有序:存和取的顺序一样
-
可重复:集合中存储的元素是可以重复的
-
有索引:可以通过索引去获取List系列集合中的每一个元素
Set系列集合:添加的元素是无序,不重复,无索引
-
无序:存和取的顺序有可能不一样
-
不重复:集合中不能存储重复的元素(可以根据这一特性完成元素的去重)
-
无索引:不能通过索引取获取set系列集合中的每一个元素
Collection集合
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
常用方法
| 方法名 | 说明 |
|---|---|
| boolean add(E e) | 添加元素 |
| boolean remove(Object o) | 从集合中移除指定的元素 |
| boolean removeIf(Object o) | 根据条件进行移除 |
| void clear() | 清空集合中的元素 |
| boolean contains(Object o) | 判断集合中是否存在指定的元素 |
| boolean isEmpty() | 判断集合是否为空 |
| int size() | 集合的长度,也就是集合中元素的个数 |
代码演示
public class a01collectiondemo {
public static void main(String[] args) {
/*
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeIf(Object o) 根据条件进行移除
void clear() 清空集合中的元素
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数
*/
/*
注意点:
Collection是一个接口,我们不能直接创建它的对象
所以,现在我们学习它的方法时,只能创建它的实现类的对象
实现类:ArrayList
*/
//目的:为了学习Collection接口里面的方法
//自己在做一些练习的时候,还是按照之前的方式去创建对象
Collection<String> coll = new ArrayList<>();//左边写接口,右边new它的实现类的对象,可以使用左边接口的方法
//1.添加元素
//细节:关于返回值的boolean
//细节1:如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列是允许元素重复的
//西街2:如果我们要往Set系列集合中添加数据,如果当前要添加的元素不存在,方法返回true,表示添加的成功
// 如果当前要添加的元素已经存在,方法返回false,表示添加失败
// 因为Set系列集合中不允许重复
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
System.out.println(coll);
//2.清空
//coll.clear();
//3.删除
coll.remove("aaa");
//细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除,只能通过元素的对象进行删除
//细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false
//如果要删除的元素不存在,就会删除失败
System.out.println(coll);//
//4.判断元素是否包含
//细节:底层是依赖equals方法进行判断是否存在的
//所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法
boolean result = coll.contains("bbb");//字符串在底层也是依赖equals()方法进行判断,只是Java已经重写好了,不需要我们自己再写了
System.out.println(result);//true
//5.判断集合是否为空
boolean result2 = coll.isEmpty();
System.out.println(result2);//false
//6.获取集合的长度
int size = coll.size();
System.out.println(size);//2
}
}
Collection的三种通用的遍历方式
迭代器遍历
-
迭代器在java中的类是Iterator,迭代器是集合专用的遍历方式
Collection集合获取迭代器
| 方法名称 | 说明 |
|---|---|
| Iterator<E> iterator() | 返回迭代器对象,默认指向当前集合的0索引 |
Iterator中的常用方法
| 方法名称 | 说明 |
|---|---|
| boolean hasNext() | 判断当前位置是否有元素,有元素返回true,没有元素返回false |
| E next() | 获取当前位置的元素,并将迭代器对象移向下一个位置 |
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class a03collectiondemo {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();//创建集合对象
coll.add("0");
coll.add("1");
coll.add("2");
coll.add("3");
Iterator<String> it = coll.iterator();//创建指针
while(it.hasNext()){//判断是否有元素
String str = it.next();//获取元素,移动指针
if("1".equals(str)){
//coll.remove("1");//不可行,会报错
it.remove();//用迭代器中的删除方法
}
}
System.out.println(coll);
/*
细节注意点:
1.迭代器已经遍历到集合没有元素的地方,如果继续调用next()方法,会报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位.想要复位,只能重新再获取一个迭代器对象
3.循环中只能用一次next方法,如果想要多次使用当前元素,可以先用一个变量接收该元素,再调用对象就可以了
4.迭代器遍历时,不能用集合的方法进行增加或者删除
如果实在是要删除,可以用迭代器提供的remove方法进行删除
如果是要添加,暂时没有办法
*/
//System.out.println(it.next());//报错NoSuchElementException
}
}
总结
-
迭代器在遍历集合的时候是不依赖索引的
-
迭代器需要掌握三个方法
Iterator<String> it = list.iterator();//iterator()创建迭代器对象
while(it.hasNext()){//hasNext()判断当前指向的位置是否有元素
String str = it.next();//next()获取当前指向位置的元素,并移动迭代器中的指针
System.out.println(str);
} -
迭代器的四个细节:
1.迭代器已经遍历到集合没有元素的地方,如果继续调用next()方法,会报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位.想要复位,只能重新再获取一个迭代器对象
3.循环中只能用一次next方法,如果想要多次使用当前元素,可以先用一个变量接收该元素,再调用对象就可以了
4.迭代器遍历时,不能用集合的方法进行增加或者删除
如果实在是要删除,可以用迭代器提供的remove方法进行删除
如果是要添加,暂时没有办法
增强for遍历
-
增强for的底层就是迭代器,为了简化迭代器的代码书写的
-
它是JDK5之后出现的,其内部原理就是一个Iterator迭代器
-
所有的单列集合和数组才能使用增强for进行遍历
格式:
for(元素的数据类型 变量名 : 数组或者集合){
}
//比如
for(String s : list) {
System.out.println(s);
}
代码演示:
import java.util.ArrayList;
import java.util.Collection;
public class a04collectiondemo {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
//2.利用增强for进行遍历
for(String s : coll){
System.out.println(s);
}
/*
注意点:
s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个元素
*/
//IDEA快捷生成增强for方式: coll.for
}
}
增强for的细节
-
修改增强for中的变量 , 不会改变集合中原本的数据
for(String s : list){
s = "q";//没什么卵用
}
Lambda表达式遍历
-
得益于JDK8开始的新技术Lambda表达式,提供了一种更简单 , 更直接的遍历集合的方式
方法名称 说明 default void forEach(Consumer<? super T> action): 结合lambda遍历集合 import java.util.ArrayList;
import java.util.Collection;
public class a04collectiondemo {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
//3.Lambda表达式
//形式: ()->{}
/*完整版
coll.forEach((String s)->{
System.out.println(s);
}
);
*/
coll.forEach((String s)-> System.out.println(s) );
//元素的数据类型可以省略,如果参数只有一个,小括号可以省略.如果方法体只有一行,大括号可以省略,return可以省略,分号可以省略
}
}
为什么没有普通for遍历?
在Collecton集合的set系列集合中,是没有索引的,而普通for遍历是通过索引遍历,所以不能使用
总结:
-
Collection是单列集合的顶层接口,所有方法被List和Set系列集合共享
-
常见成员方法
-
add , clear , remove , contains , isEmply , size
-
-
三种通用的遍历方式:
-
迭代器:在遍历的过程中需要删除元素,请使用迭代器
-
增强for , Lambda : 仅仅只是想遍历 , 那么使用增强for或Lambda表达式就可以了
-
List集合
List集合的特点
-
有序:存和取的顺序一样
-
可重复:集合中存储的元素是可以重复的
-
有索引:可以通过索引去获取List系列集合中的每一个元素
List集合的特有方法
-
Collection的方法List都继承了
-
LIst集合因为有索引,所以多了很多索引操作的方法
方法名 描述 void add(int index,E element) 在此集合中的指定位置插入指定的元素 E remove(int index) 删除指定索引处的元素,返回被删除的元素 E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 E get(int index) 返回指定索引处的元素 方法演示以及注意事项
import java.util.ArrayList;
import java.util.List;
public class a06listdemo {
public static void main(String[] args) {
//1.创建一个集合
List<String> list = new ArrayList<>();
//2.添加元素
list.add("aaa" );
list.add("bbb" );
list.add("3" );
list.add("ccc");
//在集合中指定位置插入指定元素
//void add(int index E,element)
list.add(1,"QQQ");
//细节:原来索引上的元素会依次往后移动
//删除指定索引的元素,返回被删除的元素
//E remove(int index)
String remove1 = list.remove(0);
System.out.println(remove1);//aaa
String remove2 = list.remove(3);
//请问:此时删除的是3索引的元素,还是3这个元素? ----3索引上的
//为什么?
//因为在调用方法的时候,如果出现了重载现象
//优先调用,实参和形参一致的那个方法
//可以使用手动装箱 : Integer i = Integer.valueOf(3);
//list.remove(i);
//这样删除的就是3这个元素了
System.out.println(remove2);
//修改指定索引的元素,返回被修改的元素
//E set(int index,E element)
String result = list.set(0,"QQQ");
System.out.println(result);
//返回指定索引的元素
//E get(int index)
String s = list.get(0);
System.out.println(s);
//3.打印集合
System.out.println(list);
}
}
List集合的遍历方式
迭代器遍历
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
}
}
列表迭代器遍历
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
//获取一个列表迭代器对象,里面的指针也是默认指向零的
//与迭代器不同的是:列表迭代器额外添加了一个方法,在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str = it.next();
}
}
}
增强for遍历
import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (String s : list) {
System.out.println(s);
}
}
}
Lambda表达式遍历
import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.forEach((String s)-> System.out.println(s) );
}
}
普通for循环(因为List集合存在索引)
import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(str);
}
}
}
总结:
-
迭代器遍历 : 在遍历的过程中需要删除元素,请使用迭代器
-
列表迭代器 : 在遍历的过程中需要添加元素,请使用列表迭代器
-
增强for和Lambda : 仅仅想要遍历,那么使用增强for或Lambda表达式
-
普通for : 如果遍历的时候想操作索引,可以使用普通for
数据结构
数据结构:计算机存储 , 组织数据的方式 . 是指数据相互之间是以什么方式排列在一起的 . 数据结构是为了更加方便地管理和使用数据 , 需要结合具体地业务场景来进行选择 . 一般来说 , 精心选择的数据结构可以带来更高的运行或者存储效率
要知道
-
每种数据结构长什么样子
-
如何添加数据
-
如何删除数据
常见的数据结构:
-
栈
-
队列
-
数组
-
链表
-
二叉树
-
二叉查找树
-
平衡二叉树
-
红黑树
数据结构(栈)
栈的特点:后进先出,先进后出
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
类似子弹的弹夹,最先压进去的子弹是最后打出来的
在Java的内存区域有一块叫栈内存,也是这个思想
方法运行的时候进栈,执行完毕出栈
数据结构(队列)
队列特点 : 先进先出,后进后出
数据从后端进入队列模型的过程称为 : 入队列
数据从前端离开队列模型的过程称为 : 出队列
数据结构(数组)
数组是一种查询快,增删慢的模型
-
查询速度快 : 查询数据通过地址值和索引定位 , 查询任意数据耗时相同 . (元素在内存中是连续存储的)
-
删除效率低 : 要将原始数据删除 , 同时后面每个数据前移
-
添加效率极低 : 添加位置的每个数据后移 , 再添加元素
数据结构(链表)
链表中的结点是独立的对象 , 在内存中是不连续的 , 每个结点包含数据值和下一个结点的地址
链表查询速度慢 , 无论查询哪个数据都要从头开始找
链表增删相对快
双向链,查询较快,如果要查找的数据靠近前端,就从前面开始找,靠近后端,就从后端开始找
总结:
各种数据结构的特点和作用是什么样的?
-
栈 : 后进先出,先进后出
-
队列 : 先进先出 , 后进后出
-
数组 : 内存连续区域 , 查询快 , 增删慢
-
链表 : 元素是游离的 , 查询慢 , 首尾操作极快

浙公网安备 33010602011771号