java笔记2020/4/17

基础

注释:

1.单行注释

2.多行注释(不可以嵌套使用,这应该都知道吧)

3.文档注释(java特有):注释内容可以 被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档

 

用javadoc所解析的类,通常前面加public

暂时不知道IDEA怎么用文档注释

API(Application Programming Interface应用程序编程接口)

API是Java提供的基本编程接口

 

java提供的类库称作API

对第一个java程序的总结

  1. java程序编写--编译--运行的过程

编写:我们将编写的java代码保存在以“.java”为后缀的源文件中

编译:使用javac.exe命令编译我们的java源文件。格式:javac 源文件名.java

运行:使用java.exe命令解释运行我们的字节码文件。格式:java 类名

  1. 在一个java源文件中可以声明多个class。但是,最多有一个类声明为public的。

     

    public只能加到与文件名同名的类上

  2. 程序的入口是main方法,格式是固定的

    public static void main(String[] args){
    }
    //虽说是固定的,但其实args可以变,args是arguments(参数)的缩写,可以改为任意变量
    //中括号的位置也可以变,可以放在参数的后面
    //但是习惯上中括号放在前面,后面参数用args
  3. 输出语句:

    System.out.println("Hello,World!");输出之后换行   
    System.out.print("Hello,World!");//输出之后不换行
    System.out.print();//换行
  4. 每一行执行语句都以";"结束

  5. 编译的过程:编译以后,会生成一个或多个字节码文件。字节码文件的文件名与java源文件中声明的类名相同,有几个类,就会有几个字节码文件

java集成开发环境(Intergrated Development Environment)

基本语法

(上)变量和运算符

关键字和保留字

关键字(keyword)的定义与特点

  • 定义:在java中被赋予特殊含义,用作专门用途的字符串

  • 特点:关键字中的所有字母均为小写

保留字(reserved word)

  • 在现有java版本中尚未使用,但以后版本中可能作为关键字使用,自己命名标识符的时候要避免使用这些保留字

  • goto、const

标识符(Identifier)

  • 定义:Java对各种变量、方法和类等要素命名时使用的字符序列称为标识符

  • 技巧:凡是可以自己起名的地方都叫标识符

  • 定义合法的标识符规则:

    1. 由26个英文字母大小写,0-9,_或$组成

    2. 数字不可以开头

    3. 不可以使用关键字和保留字,但能包含关键字和保留字

    4. Java中严格区分大小写,长度无限制

    5. 标识符不能包含空格

Java中的命名规范

包名:多单词组成时所有字母都小写

类名、接口名:多单词组成时,所有单词首字母大写

变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个首字母大写

常量名:所有字母都大写。多单词时,每个单词用下划线连接

 

java使用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用

变量

先声明,后使用

除了字符串类型,其他类型println时不需要加双引号

如果一个变量未赋值就打印,会报错

变量都定义在其作用域内

同一个作用域内,不可以声明同名的变量

作用域:其定义所在的一对{}内

数据类型

image-20250310093734976

整形:

java各整数类型有固定的表数范围和字段长度,不受具体OS的影响,以保证java程序的可移植性

java的整形常量默认为int型,声明long型常量需后加“l”或“L”

java程序中变量通常声明为int型,除非不足以表示较大的数,采用long

image-20250310094351854

浮点型:

与整数类型相似,Java浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响

浮点数常量有两种表示形式:

  • 十进制数形式:如5.12、512.0f、.512(必须有小数点)

  • 科学计数法形式:如5.12e2、512E2、100E-2

float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求

double:双精度,精度时float的两倍。通常采用此类型

Java的浮点型常量默认为double型,声明float型常量,需后加“f”或“F”

image-20250310095552268

虽然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不区分大小写

image-20250310151417590

计算机底层都以补码的方式来存储数据!

image-20250310151919213

十进制转二进制:了解

image-20250310152216246

运算符

  • 算术运算符

  • 赋值运算符

  • 比较运算符(关系运算符)

  • 逻辑运算符

  • 位运算符

  • 三元运算符

算术运算符

image-20250311202836000

赋值运算符

=、+=、-=、*=、/=、%=

三元运算符

(条件表达式)?表达式1:表达式2

条件表达式为ture,运算后结果为表达式1。为false,结果为表达式2

  • 表达式1和表达式2为同种类型

三元运算符与if-else的联系和区别

  1. 三元运算符可以简化if-else语句

  2. 三元运算符要求必须返回一个结果

  3. if后的代码块可有多个语句

程序流程控制:

分支结构

if-else语句

  1. if(条件表达式){
    执行代码块;
    }
  2. if(条件表达式){
    执行代码块1;
    }
    else{
    执行代码块2;
    }
  3. 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{
修饰符 类名(参数) {
方法体;
}
}

构造方法有几种,各自的作用是什么?

  • 无参数构造方法:初始化对象时,成员变量的数据均采用默认值

  • 有参数构造方法:初始化对象时,同时可以为对象进行赋值

特点:

  1. 方法名和类名相同,大小写也要一致

  2. 没有返回值类型,连void都meiy

  3. 没有具体的数值(不能由return带回结果数据)

执行时机:

  1. 创建对象时由虚拟机调用,不能手动调用构造方法

  2. 每创建一次,就会调用一次构造方法

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

}

构造方法注意事项:

  1. 构造方法定义:

    • 如果没有定义构造方法,系统将给出一个默认无参数构造方法

    • 如果定义了构造方法,系统将不再提供默认的构造方法

  2. 构造方法的重载

    • 带参构造方法,和无参构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载

  3. 推荐的使用方式:

    • 无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法

标准 JavaBean

  1. 类名需要见名知意,驼峰命名

  2. 成员变量使用private修饰

  3. 提供至少两个构造方法

    • 无参构造方法

    • 带全部参数的构造方法

  4. 成员方法:

    • 提供每一个成员变量对应的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重写注解

  1. @Override是放在重写后的方法上.校验子类重写时语法是否正确

  2. 加上注解后如果有红色波浪线,表示语法错误

  3. 建议重写方法都加@Override注解,代码安全,优雅

@Override//如果重写的出错,会在@Override下面出现红色波浪线
public void eat(){
sout("吃意面");
}

方法重写的注意事项和要求:

  1. 重写方法的名称,形参列表必须与父类中一致

  2. 子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解:空着不写<protected<public)

  3. 子类重写父类方法时,返回值类型子类必须小于等于父类

  4. 建议:重写的方法尽量和父类保持一致

  5. 只有被添加到虚方法表中的方法才能被重写

继承中:构造方法的访问特点

  • 父类中的构造方法不会被子类继承

  • 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己

为什么?

  • 子类在初始化的时候,有可能会使用到父类中的数据

  • 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化

怎么调用父类构造方法的?

  • 子类构造方法的第一行语句默认都是: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:

接口和类之间的关系:

  • 类和类的关系:继承关系,不能多继承,但是可以多层继承

  • 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(如果这样的话,需要把接口中的所有方法全部重写

  • 接口和接口的关系:

接口的应用

  1. 接口代表规则,是行为的抽象.想要哪让哪个类拥有一个行为,就让这个实现对应的接口就行了

  2. 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态

适配器设计模式

  • 设计模式(Design pattern)是一套被反复使用,多数人知晓的,经过分类 编目的,代码设计经验的总结,使用设计模式是为了可重复用代码,让代码更容易被他人理解,保证代码可靠性,程序的重用性

 

简单理解:设计模式就是各种套路

  • 适配器设计模式:解决接口与接口实现类之间的矛盾问题

写法:用一个实现类作为适配器,对所实现接口的所有默认方法进行空实现(方法体不写),再用InterImpl继承适配器,将需要用的默认方法重写即可

注意:适配器一般会加abstract修饰,因为里面的空实现方法没有意义,所以不会让外界建立适配器的对象

总结:

  1. 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式

  1. 书写步骤:-

    • 编写中间类XXXAdapter(适配器),实现对应的接口

    • 对接口中的抽象方法进行空实现

    • 让真正的实现类继承中间类,并重写需要用的方法

    • 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰

内部类

什么是内部类

类的五大成员: 属性 , 方法 , 构造方法 , 代码块 , 内部类

什么是内部类?

在一个类的里面,再定义另一个类

其中一个类是外部类,另一个类是内部类,其他所有类是外部其他类

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之后才可以定义静态变量

获取成员内部类的对象

方式一:在外部类中编写方法,对外提供内部类的对象

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();

 
posted @ 2025-04-17 19:51  世界回转  阅读(17)  评论(0)    收藏  举报