首页 > 编程语言 >Java学习笔记(上)——动力节点老杜(某站2000万播放)

Java学习笔记(上)——动力节点老杜(某站2000万播放)

时间:2024-09-24 18:52:59浏览次数:15  
标签:java int 某站 class 2000 实例 Java 方法 public

此文章是本人大一学习java时记的笔记,原视频在https://www.bilibili.com/video/BV1Rx411876f,配套服用更佳!

一.JAVA开发环境的搭建

1.常用的Dos命令

1.1 win+r打开Dos命令窗口
1.2什么是Dos命令

在最初的计算机中没有图形界面,也就是说通过Dos命令窗口可以完全完成文件的新建、编辑、保存、删除等一系列操作。

1.3 mkdir abc

创建一个目录,起名abc

1.4 在Dos窗口中怎么复制内容

左键选中要复制的内容,右键点击一次,内容就到了剪贴板了。

1.5切换盘符

直接输入盘符即可。

c:回车 就到了c盘,同理其他也一样。

1.6切换目录

使用cd命令来完成目录的切换。

cd+路径

路径可以是相对路径也可以是绝对路径。

cd…回到上级目录

cd \ 回到根目录

cd+.代表当前路径

1.7 cls清屏
1.8 dir查看当前目录下有啥东西
1.9 exit 退出Dos命令窗口
1.10 del删除一个或者多个文件

del 1.txt 就删除了他

del *.txt 就删除所有txt文件

t1.class

t1.glass

del *ass 这个命令会将上述两个文件都删除。

1.11 怎么查看本机的IP地址

在命令框输入ipconfig(可查询ip地址的配置信息)

ipconfig /all 可查看更详细信息

1.12 怎么查看两台计算机可以正常通信

ping IP地址

ping 域名

例如 ping www.baidu.com

后面加个-t 就一直ping,直到人操作终止。

一直ping的目的是查看网络是否稳定。

在一个DOS窗口中一个命令一直执行,想结束

用ctrl+c结束。

1.13 DOS命令窗口中,可以使用tab键自动补全。
1.14 DOS命令窗口中,使用上下箭头可以翻出历史记录。
1.15 电脑快捷键

win+d 显示桌面

win+l 锁屏

2.文本快捷键

ctrl+s保存
ctrl+z撤销
ctrl+y重做
ctrl+a全选
fn+home光标回到行首
fn+end光标回到行尾
ctrl+fn+home光标回到文章开始
ctrl+fn+end光标回到文章末尾
shift+上方向键或下方向键选中一行
鼠标双击选中一个单词
ctrl+shift+右箭头或左箭头选中一个单词
鼠标连击三次选中一行
ctrl+f查找

3.java语言特性

3.1简单性

java中真正操作内存的是:JVM(java虚拟机)

所有java程序都是运行在java虚拟机当中的。

而java虚拟机执行过程中再去操作内存。

而c语言时可以直接通过指针操作内存的。

因此

java优点:简单,不容易导致内存泄漏

缺点:效率问题。

java语言底层是c++,JVM是用c++写好的的一个虚拟的电脑。

安装了JDK后,JVM就安装好了。

计算机重要部件

cpu:中央处理器,相当于人类的大脑,负责发送并执行指令。

cpu负责计算,但计算过程中有数据需要临时找个空间存储一下:就需要内存

内存

程序执行过程中临时数据存储空间。

断电之后数据就消失了。

硬盘

持久化设备。

主板

相当于人的躯干,是一个载体。

3.2java是堪称完全面向对象的

面向对象更容易让人理解,使复杂问题简单化。

3.3健壮性

java中有一种机制

自动垃圾回收机制(GC机制)

JVM负责调度GC机制,程序员不需要干涉。

3.4 JVM

jvm是java虚拟机,用c++写好的,java程序实际没有直接和操作系统打交道。

java程序写出来后是一样的,windows上的jvm和mac上的不同,然后就可将java程序翻译成Windows和mac分别能看懂的语言。

jvm屏蔽了操作系统之间的差异

jvm是安装jdk的时候自带的。

因此jdk也分Windows和mac

3.5可移植性/跨平台

java语言只要编写一次,可以做到到处运行。

jvm这种机制实现了跨平台。

优点:一次编写到处运行。

缺点:麻烦,运行java程序必须先有一个jvm。

3.6 JDK、JRE、JVM三者之间的关系

jdk:java开发工具箱

jre:java运行环境

jvm:java虚拟机

jvm不能独立安装,jdk包括jre,jre包括jvm。

安装jdk,jre自动安装,jvm也自动安装

安装jre,jvm也自动安装

假如一个电脑只需将程序跑起来,那就值安装jre就行,不需要jdk

3.7 java从编译到运行

程序员写的java代码无法被jvm识别。必须经过编译变成字节码,才能被jvm识别。

java代码变成字节码的过程叫 编译

java代码被称为源代码。

字节码可以到不同系统去使用,因为java是跨平台的。

放源代码的文件扩展名是 xxx.java

编译生成的字节码文件扩展名是:xxx.class

1个java文件可编译生成多个class文件。

字节码不是二进制文件

如果是的话,就不需要jvm什么事情了。

需要使用的两个命令

javac命令,负责编译

java命令,负责运行

小插曲

xxx.java经过编译后生成了A.class B.class C.class,那么我们称A是一个类,B是一个类,C是一个类。

4.java使用

4.1 java下载以及环境变量配置

javac -version命令可查看java版本

path环境变量作用

告诉Windows系统去哪里找这个命令文件。

4.2javac命令怎么用

格式: javac java源文件的路径。

路径可以是绝对或相对。

javac 然后把java文件拖进命令框也可以。

除了win+r进入DOS命令框以外,还可以在文件夹地址栏输入cmd,这样的好处是进入DOS命令框后,路径深入,更好找到想找的java文件

4.3 java命令

java+类名

使用时

先用cd命令找到.class文件所在路径,然后在java+类名

4.4 classpath环境变量

进行

java+类命令后

第一步:会启动JVM(java虚拟机)

第二步:jvm启动之后,jvm会自动启动类加载器classloader

作用:负责去硬盘找类对应的字节码文件。

第三步:找不到就报错。

找到了,类加载器会将该字节码文件装载到jvm中,启动解释器将字节码解释为二进制码,操作系统执行二进制码和硬件交互。

类加载器找的是当前路径下的类文件。

能不能给加载器一个路径,直接让他去别的地方找??

但需要classpath

classpath环境变量不属于Windows操作系统,是java特有的。

配置classpath 后,java命令只会去配置classpath的文件夹找,不会在当前路径找,所以不如不配。

目前不需要classpath,理解即可、

java后面直接加java源文件路径,可以直接运行,但是这样不会生成class文件,现在没必要这么使用

5.java注释

//单行

/* */多行

    
    这种叫javadoc注释:这里的注释信息可以自动被javadoc.exe命令解析提取并生成到帮助文档中。
/**

*

*

*

*/

6.类体

1.在java中任何有效代码必须写到类体中,最外层必须是一个类的定义。
2.public表示公开,class表示定义一个类,test是一个类名

例 public class test{

}

7.方法

1.main方法
public static void main(String[] args){



}

也可以

public static void main(String args[]){



}

整个这一块被称为main方法(程序的入口)

main方法也可以叫做主方法。

注意:方法必须放在类体中,不得放到类体外。

任何一个程序都要有入口。

能再来一次main方法吗?

不能,会有语法错误。

main方法中,arge可以改名字,对于主方法来说,只有这个位置可以改,

java语句必须写在方法里。

100和“100”不一样

±*/也都和c语言一样。

2.class

一个java文件中

class A{

}

class B{

}

可以运行吗?

可以的。并且生成两个class文件。

结论一:一个java源文件中可以定义多个class

结论二:public的类不是必须的。

结论三:public的类必须和源文件名保持一致

结论四:public的类也只能有一个

class t1{

    public static void main(String[] arge)

    system.out.println(“t1”);

}

class t2{

    public static void main(String[] arge)

    system.out.println(“t2”);

}

class t3{

    public static void main(String[] arge)

    system.out.println(“t3”);

}

要想运行这些,java+类名时 类名用t1,t2,t3即可

(不过一般不这样,只是为了理解)

一般软件的入口只有一个。

3.public class

必须和java文件名相同。

二.标识符与关键字

1.标识符可以标识什么

类名,变量名,方法名,变量名等等

数字,字母,下划线。以及¥,标识符也可以是中文。

关键字不能做标识符

2.可以命名123.java吗

可以,在Windows操作系统中文件名叫做:123.java没问题。

只不过在123.java文件中无法定义public的类。

因为类名不能以数字开头。

3.规范

见名起意

驼峰

类名,接口特殊要求

类名接口首字母大写,后面每个单词首字母大写。

变量名,方法名

首字母小写,后面每个单词首字母大写。

所有常量名全部大写,单词单词之间采用下划线衔接

4.关键字

蓝色字体,小写,是关键字。

三.变量

1.字面量

和c一样

2.变量

变量名首字母小写,后面字母大写

与c语言不同,java变量必须初始化(成员变量例外)

3.变量分类

在方法体当中声明的变量,叫局部变量。

在方法体之外的叫成员变量。

类似于c语言全局变量和局部变量。

与c语言相同。

局部变量只在方法体内有效,方法体执行结束变量的内存就释放了。

四.数据类型

4.1整型

数据类型分为两类在java中
1.基本数据类型

整数型:byte,short,int,long

浮点型:float,double

布尔型:true,false

字符型:用单引号括起来

2.引用数据类型

String属于引用数据类型

引用数据类型后期面向对象的时候才会接触。

3.区别

float double 以及 int long这些占据的空间不同

4.占用字节数量
类型占用字节数量
byte1
short2
int4
long8
float4
double8
boolean1
char2
5.取值范围

byte是[-128~127],可以标识256个不同数字。

byte类型最大值是怎么算出来的???

btye是一个字节,8个比特位。

byte最大值为 01111111

因为二进制位最左边的是符号位,为0时为正数,1时为负数。

所以byte最大为01111111

则是256

需要记忆的取值范围

byte -128~127 可以标识256个不同数字

short -32768~32767 可以标识65536个不同数字

int -2147483648~214783647

char 0~65535 可以标识65536个不同数字

6.字符编码

规定了一系列的文字对应的二进制

最开始支持的文字是英文,英文对应的字符编码方式是:ASCII码

ASCII采用1byte进行存储,因为英文字母只有26个,1byte足以表示。

‘a’–>97

‘b’–>98

‘A’–>65

‘B’–>66

‘0’–>48(文字0)

‘1’–>49

中文编码方式:GB2312<GBK<GB18030 (简体中文)

繁体中文:big5

java为了支持全球所有文字,采用unicode编码。

具体的实现包括:UTF-8 UTF-16 UTF-32

7.转义字符

\t \n与c一样

输出时

System.out.println()这种是输出后换行

System.out.print()这种输出后不换行。

想在控制台输出一个\字符咋办

System.out.println('\ ') 不行

因为\会把 '转义

因此就

System.out.println(’ \ \ ')

这样做\会把\转化为普通的\,就输出了。

同理,想在控制台输出一个’也一样

想输出"test"

System.out.println(“\“test\””)

或者 内引号用中文引号。

\u是 表示后面的是一个字符的unicode编码

System.out.println(‘\ u4e2d’)

本身’'里只能放一个字符,但\u会变成unicode编码,4e2d输出后是中字。

8.进制

十进制 10

八进制 010

十六进制 0x10

二进制 0b10

9.整数型被默认当做int处理

想被当做long处理,则在数字后面写L。

10.自动类型转换

long i=200;

200虽然被当做int,但是可以自动转换为long(没超过范围就可以)

long i=2147483645

编译器会报错吗???

会,因为java中会字面量被当做int处理,2147483645就是int,然后超出范围了

byte<short(char)<int<long<float<double

11.强制类型转换

long x=100L

int y=(int)x

才能编译通过,但会损失精度

byte b=300可以编译通过吗???

不行,300的int要转换为byte,必须强制类型转换

int类型的300 是 00000000 00000000 00000001 00101100

byte自动砍掉前三字节,00101100就是44

12.整数能否直接赋值给char

char x=97

输出结果为’a’

将整数赋值给char,会自动转化为char型。

13.二进制补码
1.计算机在任何情况下都只能识别二进制
2.计算机在底层存储数据时,一律存储的是二进制的补码形式。
3.什么是补码

二进制包括:原码,反码,补码

4.对于一个正数来说:原码,补码,反码是同一个,完全相同。

对一个负数来说:

byte i=-1

原码 10000001

反码 11111110(符号位不变,其他位取反)

补码 11111111(反码+1)

byte b=(byte)150

150转换为二进制 00000000 00000000 00000000 10010110

byte后 剩下 10010110

(计算机中存储的永远都是二进制补码形式)

所以 反码 =补码-1 :10010101

原码:11101010

所以b=-106

14. char+byte

char c1=‘a’

byte b=1

输出c1+b为一个int型的98

结论:byte char short做混合运算的时候,各自先转换成int再做运算。

short s=c1+b(报错) 与 short k=98(正常)

为什么???

第二种正常说明98并没有超出范围,第一种是加法运算,编译器编译时并不知道他的结果超出范围了没,只有运行的时候才会出结果,所以需要强制类型转换。

同理

int a=1

short x=a;

(错误)

与上面同理,编译过程中,计算机只判断a是int型,int型赋给short就得需要转换,他不会知道a很小。

15.多种类型运算

long a=10L

char c=‘a’

short s=‘100’

int i=30

求和后是long型

结论:多种数据类型做混合运算时,最终的结果类型是“最大容量”对应的类型。

但char+short+byte混合除外,因为这个会各自转换为int再运算。

4.2浮点型

float 4字节

double 8字节

float是单精度,double双精度

double更精确

long 8字节 float 4字节 哪个容量大??

注意:任意一个浮点型都比整数型空间大。

java中规定,任何一个浮点型数据默认被当做double来处理如果想让被当做float类型出行,后面加F或f,或者用强制类型转换,但可能会损失精度

4.3 boolean型

在java中只有true和false ,和c语言不同,不能用0和1表示。

一般用在逻辑判断中。

int a=10

int b=20

boolean flag=a<b

输出flag就是true

五.运算符

&有一边为false,即为false。

|有一边为true,即为true。

1.短路

&和&&结果相同

但&&会有短路效果,&&左面表达式是false,决定整个式子是false了,右边就不会进行。

同理||也一样。

2. i=i+1和i+=1真的一样吗???

byte x=100;

x=x+1 [会报错,1是int类型,x+1也是int类型,直接赋值给byte,会报错]

x+=1 [不会报错,扩展赋值运算符,不会改变运算结果类型。]

x+=1等同于:x=(byte)(x+1)

3.三目运算符

跟c一样

4.运算符(字符串拼接)

当+运算符两边的任意一边是字符串类型,那么这个+会进行字符串拼接操作。

字符串拼接完之后的结果还是一个字符串

加法遵循自左向右。

例子

在控制台输出 100+200=300

int a=100,b=200

System.out.println(a+“+”+b+“=”+c)

字符串拼接易错题

'5'+2  //等于55
 "5"+2 //等于52
5.如果定义字符串型变量

String name=“zhang”

六.接收用户键盘输入(先不用理解)+退出JVM

java.util.Scanner s=new java.util.Scanner(System.in);

int i=s.nextInt(); [输入整型]

String str=s.next();   [输入字符串]

double score=s.nextDouble() [输入小数]



System.exit(0); 退出jvm

七.if 三目 Switch

1.Switch

Switch(值)

值支持int类型以及String类型。

byte short char也可以使用在Switch里。因为可自动类型转换

但是long 型的不行

case 1:

....... ;

break;

case 2 :

.......;

break;

想合并就

case1: case2:

........;

break;

八.转向语句

break的一种特殊用法

 a:for(){

    b:for(){

           i f(){

             break a;

             }

     }

}

这样可以选择终止哪个循环。

九.方法

跟c语言函数类似。

语法机制

[修饰符列表] 返回值类型 方法名(形式参数列表){

​ 方法体;

}

注意: []里的不是必须的。

修饰符列表目前写成public static ,后续就知道怎么写了。

1.返回值类型

所有基本数据类型和引用数据类型。

2.返回值类型不是void,就必须有return
3.类名省略

当a方法调用b方法时,a和b方法都在同一个类中,“类名”可以省略。如果不在同一个类中不能省略。

意思是a,b在同一个类时,引用直接 方法名()就行。

不在同一类,引用得 类名.方法名()

4.return 用来终止离他最近的一个方法。
5.缺少返回语句

public static int m(){

boolean flag =true;

if(flag){

​ return 1;

​ }

}

上述程序会报错

编译器不会知道flag是true型,他只知道如果flag是false,则没有返回值,而int方法必须有返回值

6.JVM(java虚拟机)内存结构

JVM中主要的三块内存空间:

栈、堆、方法区

栈:在方法被调用时候,该方法需要的内存空间在栈中分配。

堆:以后讲。

方法区:类加载器classloader,将硬盘上的XXX.class字节码文件装载到JVM的时候,会将字节码文件存放到方法区当中。也就是说方法区存储的是代码片段。

因为类需要加载,所以方法区当中最先有数据。

7.方法执行过程中内存变化

1.方法区最先有数据:方法区中放代码片段,存放class字节码。

2.方法只有在调用的时候才会在栈中分配空间,并且调用时就是压栈。

3.方法执行结束后,该方法所需要的空间就会释放,此时发生弹栈动作。

4.栈中存储什么? 方法执行过程中需要的内存,以及栈中会存储方法的局部变量。

在这里插入图片描述

类加载器将字节码文件放到方法区里,然后开始执行,最初执行main方法,main方法的栈帧先在栈区有空间,然后调用m1,m1也在栈区里。最后m3、m2、m1、main依次顺序弹栈

在这里插入图片描述

8.方法重载

[想求int的和,小数的和,long型的和需要创三个方法]

如果不使用方法重载,有什么缺点???

1.代码不美观

2.程序员记忆方法名很累

方法重载

功能相似的方法给一样的方法名。

比如int求和,long求和,double求和。

public static int sum(int a,int b)

public static long sum(long a,long b)

public static double sum(double a,double b)

调用的时候都用sum就行,编译器会根据你给参数的数据类型判断去哪里执行。

重载

{

在一个类中

方法名相同

参数列表不同

1.个数不同

  1. 类型不同
  2. 顺序不同

}

9.函数递归

跟c语言递归一个道理

递归可能会导致栈空间不足。所以必须有结束条件。

但即使有结束条件,也可能会栈空间不足。(有可能递归太深)

可以用java-X命令查询,如何更改栈空间之类的操作。

n的阶乘

public class test{
     public static void main(String[] args){
          int a=sum(4);
          s.p(a);
     }  
     public static int sum(int n){
         if(n==1){
            return 1;
         }
         return n*sum(n-1);
     }
}

十.面向对象

1.面向对象和面向过程区别

面向过程:c语言

注重的是实现这个功能的步骤

面向过程也注重实现功能的因果关系。

面向过程中没有对象的概念,只是实现这个功能的步骤。

缺点

面向过程主要是一步一步的因果关系,其中A步骤因果关系到B步骤,A和B联合起来形成一个子模块,子模块和子模块之间又因为因果关系结合在一起,假设其中一个因果关系出现问题(错误),此时整个系统的运转都会出现问题。(代码和代码粘合度高,扩展力差)

优点

小型项目,直接干活,编写因果关系,从而实现功能。

面向对象

采用面向对象的方式进行开发,更符合人类的思维方式。

面向对象就是将现实世界分割成不同的单元,然后每一个单元都实现成对象,然后驱动一下,让各个对象之间写作起来形成一个系统

例子

对象”张三“

对象“香烟”

对象“打火机”

然后将上面对象组合在一起,就可以模拟一个人的抽烟场景。

其中“张三”还可以换成“李四”。

扩展力强

2. OOA OOD OOP

OOA: 面向对象分析

OOD:面向对象设计

OOP:面向对象编程

上述为实现一个软件的过程。

3.面向对象三大特征(了解,后面讲)

封装

继承

多态

任何一个面向对象的编程语言都包括这三个特征。

4.类和对象的概念

什么是类?

​ 类实际上在现实世界中是不存在的,是一个抽象的概念,是一个模板。是我们人类大脑进行思考,总结,抽象的一个结果。

什么是对象?

​ 对象是实际存在的个体。

例如

赵露思是一个对象

鹿晗是一个对象

刘德华是一个对象

这三个对象都属于“明星”这个类。

在java中,要想得到“对象”,必须先定义“类”,“对象”是通过“类”

这个模板创造出来的、

类就是一个模板:类中描述的是所有对象的共同特征信息,对象是通过类创建出的个体。
在这里插入图片描述

类=属性+方法

属性来源于状态,方法来源于动作。

public class 明星类{

//属性–>状态,多见于名词。

名字属性:

身高属性:

//方法–>动作,多见于动词。

打篮球方法(){}

study方法(){}

}

5.java程序员是一个转换的桥梁

将现实生活中的一些事物转换。

具备 观察+抽象能力,观察现实世界中对象1和对象2的共同特征,然后在java程序中使用“类”来描述这些共同特征,然后通过“类”实例化“对象”,让对象之间互相协作起来形成系统,模拟现实世界。

6.类的定义

6.1 怎么定义一个类?

[修饰符列表] class 类名{

​ //类体=属性+方法

​ //属性在代码上以“变量”的形式存在(描述状态)

​ //方法描述动作/行为

}

属性其实就是成员变量

6.2对象的创建
public class XueSheng{
    int xuehao;
    int nianling;
    char xingbie;
}

之后在其他地方

new XueSheng();就行。

但是你创建了想使用的话,必须接收一下他。

接收东西,比如 int b=10;

变量类型+变量名=

因此,创建对象是: XueSheng s1=new XueSheng ();

类就是引用数据类型

6.3 编译过程

按理来说,需要先编译那个类的文件,再编译使用类去弄出来的对象的文件。

但是

可以直接编译对象那个文件,这样类文件会自动编译。

6.4创建对象对应的jvm内存结构

在这里插入图片描述

之前说,变量必须声明,赋值后才能使用。

但成员变量,没有赋值时,系统默认赋值

类型默认值
byte0
short0
int0
long0L
float0.0F
double0.0
booleanfalse
char\u0000
引用数据类型null

实例变量

对象又被称为实例。

实例变量实际上就是:对象级别的变量。

public class 明星类{

double height;

}

身高这个属性所有明星对象都有,但是每一个对象都有“自己的身高值”。假设创建10个明星对象,height变量应该有10份。

所以这种变量被称为对象级别的变量,属于实例变量。

因此,不能通过类名直接访问实例变量,必须创键对象。

对象和引用

对象是通过new出来的,在堆内存中存储。

引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的。

怎么访问实例变量

语法:引用.实例变量名

实例变量都是默认值,如何修改

直接通过=赋值进行修改。

6.5 内存图

内存图上不要体现出代码,主要体现数据

画内存图是对java运行机制的一种理解,有助于以后复杂程序出错的调试。

画图时,应有先后顺序。

6.6 引用一定是局部变量吗?

不一定

new的某个对象里面也可以有一个引用指向其他对象。

就类似c语言结构体里面还有一个结构体的感觉。

通过引用的目的是访问对象里面的数据。

6.7 属性是引用类型怎么访问。

一个例子

如何从test类一直访问到i的值

public class test{
    A o1;
    
     public static void main(String[] arge) {
      D d =new D();
      C c =new C();
      B b =new B();
      A a =new A();
     
      test t =new test();
  
       
       c.o4=d;
       b.o3=c;
       a.o2=b;
       t.o1=a;
       System.out.println(t.o1.o2.o3.o4.i);
   }
} 
class A{
   B o2;
}
class B{
   C o3;
}
class C{
   D o4;
}
class D{
   int i=100;
}
6.8空指针异常(NullPointerException)
public class NullPointertest{
    public static void(String[] args){
        customer c=new customer();
        System.out.println(c.id);//0
        //若c为null会怎样?
        c=null;
        System.out.println(c.id);//NullPointerException
    }
}

在这里插入图片描述

java中的垃圾回收器 GC

​ 在java语言中主要针对的是堆内存,在一个java对象没有任何引用指向该对象的时候,GC会考虑将该垃圾数据释放回收掉。

6.7中的代码

如果

c.o4=d;
​ b.o3=c;
​ a.o2=b;
​ t.o1=a;

这一段不写,也会出现空指针异常。

因为o1 o2 o3都是空的,无法点点点,必须把new来的对象赋给他们。

空指针异常的前提条件

“空引用”访问实例相关【对象相关】的数据时,都会出现空指针异常

6.9方法调用时参数的传递问题

参数传递只是将值复制一份传递下去。

跟c语言一样,变量有作用域,在某个方法里i++了,但出了这个方法i还是原来的值。

但是! 参数如果是对象,传递的是地址,比如传递一个p,p点什么什么不管在哪个方法里都是一样的,因为传递前后地址一样。(跟c语言指针差不多)

7.构造方法

7.1什么是构造方法,有什么用?

构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值。(注意:实例变量没有手动赋值的时候,系统会赋默认值。)

7.2 当一个类没有提供构造方法时,系统会默认提供一个无参数的构造方法(这个构造方法被称为缺省构造器)。但是当一个类中提供了方法时,系统就不会再提供那一个无参数的构造方法了,这时你想调用无参的这个方法,就会报错
7.3构造方法如何调用?

使用new运算符来调用构造方法

new+方法名+(实参)

7.4 构造方法的语法结构是?

[修饰符列表] 构造方法名 (形式参数列表){

构造方法体;

通常在构造方法体当中给属性赋值,完成属性的初始化。

}

修饰符列表目前统一写 public,千万别写public static。

构造方法名和类名必须一致。

构造方法不需要指定返回值类型,也不能写void。

普通方法语法结构

[修饰符列表] 返回值类型 方法名 (形式参数列表){

方法体;

}

构造方法支持重载,因为构造方法名必须跟类名相同,所以当参数不同,就重载了。

7.5 实例变量初始化啥时候?

创键了对象后。

十一.封装

1.面向对象的三大特征

封装

继承

多态

2.面向对象的首要特征:封装

2.1 什么是封装?

现实生活中例如手机,电视机。外部有一个坚硬的壳,保护内部部件安全。

对于使用者来着,看不见内部复杂结果,我们也不需要看见,只需要操作外部的几个按钮就行。

2.2 封装有什么用?
1.保证内部结构安全
2.屏蔽复杂,暴露简单

在代码级别上,封装有什么用?

一个类体当中的数据,假设封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,只需要通过一个简单的入口就可以访问了。

另外,类体中安全级别较高的数据封装起来,外部人员不能随意访问,来保证数据的安全性。

2.3不封装有什么问题

例如person对象里的属性age,如果不封装,年龄可在外部任意更改,哪怕给年龄负数,也不会报错,不符合业务要求。

2.4尝试封装一下

不再对外暴露复杂的数据,封装起来。

对外只提供简单的操作入口。

优点:第一数据安全了,第二调用者也方便了。

2.5怎么进行封装?

第一步:属性私有化(使用private关键字进行修饰)私有的可以在本类中访问

第二步:对外提供简单的操作入口。(set+get);

private

public class Person{

    //被这个关键词修饰以后,该数据只能在本类中访问。
    //出了这个类,age属性就无法访问了,私有的。
    private int age;

}

之后new对象后,想输入和修改这个对象的age值,编译会报错说age在Person中被private控制。

age彻底在外部不能访问了,但是这有点太安全了。age不能访问,这个程序就意义不大了。

因此还需要对外提供简单的入口,外部程序只能通过调用以下的代码来完成访问。

思考:你应该对外提供几个访问入口。

思考:这些操作入口是否应该是方法呢。

写一个方法专门来完成读(get)

写一个方法专门来完成写(set)

中间引入一个实例方法
带有static的方法
不带static的方法

(后面会讲为什么)

分别怎么调用?

带static通过类名.的方式访问

不带static的被称为实例方法

对象被称为实例。

实例相关的都需要先new对象,通过引用.的方式去访问。比如实例方法。

实例方法跟构造方法一样,也会出现空指针异常

空指针异常导致的根本原因是?

空引用访问“实例相关的数据”,会出现空指针异常。

实例相关的包括:实例变量,实例方法。

接着2.5继续

get和set应该带有static还是不该?

不应该,改年龄和读年龄都是操作一个对象的年龄。

封装第二步(第一步是private):对外提供公开的set方法和get方法作为操作入口

public class Person{

    //被这个关键词修饰以后,该数据只能在本类中访问。
    //出了这个类,age属性就无法访问了,私有的。
    private int age;
     
       
        
    public int getAge(){
        return age;
    }
    public void setAge(int nianling){
        age=nianling;
    }
}

java开发规范中有要求,set方法和get方法的格式

get

​ public 返回值类型 get+属性名首字母大写(无参){

​ return xxx;

}

set

​ public void set+属性名首字母大写(有一个参数){

​ xxx=参数;

}

十二 static

1.static

static翻译为“静态的”

所有static关键字修饰的都是类相关的,类级别的。

所有static修饰的,都是采用类名.的方式访问

static修饰的方法:静态方法。

变量分为局部变量和成员变量

成员变量又分为静态变量和实例变量

class vartest{
    //实例的都是和对象相关,访问时用引用.访问。需要先new,可能出现空指针异常
    int i; //实例变量
     public void m2(){
        //实例方法
    }
    //以下静态的,都是类相关的,访问采用类名.的方式。不需要new,没有空指针异常的发生。
    static int k;  //静态变量
    public static void m1(){
        //静态方法
    }
   
}

2.什么时候用静态的,什么时候用实例的。

一个对象一份的是实例变量,所有对象一份的是静态变量。

中国人的例子

public class staticTest{
    public static void main(String[] arge){
        
    }
    
    class Chinese{
        String idcard;//idcard每个人都不一样,所以不加static,是实例变量。
        String name;//name也不一样
        String country;//对于中国人这个类来说,country都一样,先假设他是实例,画下面内存图,看有什么问题。
    }
}

在这里插入图片描述

由图能看出,country每个人都一样,但因为是实例变量,所以每次都new出country,浪费了不必要的空间。

因此给country加static

public class staticTest{
    public static void main(String[] arge){
        
    }
    
    class Chinese{
        String idcard;//idcard每个人都不一样,所以不加static,是实例变量。
        String name;//name也不一样
        static String country;//对于中国人这个类来说,country都一样.
    }
}

static String country 是成员变量,会给默认值,但他不像实例变量,实例变量是在new对象时给默认值

静态变量在类加载时初始化,不需要new对象,静态变量存储在方法区。

在方法区内加载chinese类时,发现chinese类中有一个静态变量,此时会给该静态变量初始化。

此时,要访问country,静态变量,所以用 chinese.country访问。

如果你想 chinese.idcard 则会报错 无法从静态上下文中引用非静态变量idcard 意思是chinese是class,是静态的,idcard是实例变量,是非静态的,无法这么引用。

栈—局部变量 堆—实例变量(对象级别) 方法区—静态变量(类级别)

3.空引用访问静态变量

实例一定需要引用.访问

静态的建议使用类名.访问(引用.也行)(不建议)。

同时,若用空引用访问静态变量,

c1=null c1.country

不会出现空指针异常,因为静态变量不需要对象的存在。

因此,只有在“空引用”访问“实例”相关的,才会出现空指针异常。

4.关于实例方法的调用

静态方法 使用类名.,也可以引用.,但不建议

实例方法 new对象,用引用.来访问。

什么时候定义为实例方法?什么时候定义为静态方法?

方法一般都是描述了一个行为,如果说该行为必须由对象去触发,就定义为实例方法

比如之前的set和get方法(年龄),他是为了让外界查找年龄或者更改年龄,而年龄是每个对象不一样,所以要定义为实例方法。

如果某个方法体执行过程中访问了实例变量,那这个方法必须定义为实例方法

开发中,大部分情况下,如果是工具类的话,工具类中的方法一般都是静态的。

工具类就是为了方便开发的一些类。直接类名.去访问,比较方便。

//例子
//打印名字
class User{
    private int id;
    private String name;
    public void printName1(){
        System.out.println(name);
    }
    public static void printName2(){
        System.out.println(name);   //name是一个实例变量,在方法体内访问实例变量,那方法必须是实例方法,不能是static
    }
}

5.静态代码块

5.1使用static可以定义静态代码块
5.2什么是静态代码块?
static{
   java语句
    java语句
}
5.3static静态代码块什么时候执行呢?

类加载时执行,并且只执行一次。

static{
    System.out.println(A);
}
public static void main(){
    System.out.println(hello);
}
static{
    System.out.println(B);
}

输出是 A B hello

因为静态代码块是类加载时执行的,main方法时在栈里执行,先类加载完,才会让main方法压栈,才执行main里的输出。

5.4静态代码块有什么用?

为java程序员准备的一个特殊时机,时间点。

5.5 先后顺序

静态代码块和静态变量都在类加载的时候执行,时间相同,只能看代码先后顺序判断谁先谁后。

静态代码块不能访问实例变量,因为实例变量是在new对象时才有的。

6.实例语句块

6.1实力语句块语法

直接一个{}

6.2实例语句块不是在类加载时执行
6.3实例语句块在构造方法执行之前执行。

叫做对象构建时机。

6.4实例语句块有什么用

比如你有很多的构造方法,但他们的前几句代码都一样,你就可以写到实例代码块里,不管调哪个构造方法,都会先进行实例语句块。

十三.this

在这里插入图片描述

1.this是一个关键字,全部小写、

2.this是什么?

一个对象一个this。

this是一个变量,是一个引用。this保存的当前对象的内存地址,指向自身。

所以this代表的就是当前对象,this存储在堆内存当中对象的内部。

1.this的应用

this只能使用在实例方法中。谁调用这个实例方法,this就是谁。所以this代表的是:当前对象。

public static void main(String[] arge){
    user s1=new user("张三");
    user s2=new user("李四");
}
class user{
    String name;
    public user{
        
    }
    public user(String s){
        name=s;
    }
    public voud shopping{
        System.out.println(this.name+"正在购物!")
            //为什么不能写c1.name? 
            //c1是在main方法中定义的,是局部变量,去了别的方法不能用,但想调用name这种实例变量,必须“引用.”,所以产生this。
            
    }
}

同时,更好的理解什么时候定义实例方法,什么时候定义静态方法。

如果方法体里有实例变量,想访问它,就是this.name,必须是实例方法里才能出现this。

只是一般this省略了

在这里插入图片描述

2.this什么时候不能省略?

class Student{
    private int no;
    private String name;
    public Student(){
        
    }
    public Student(int no,String name){
        //int no,String name 为什么不用int i?因为代码无可读性。
        this.no=no;
        this.name=name;  
        //这个时候this不能省略,如果省略就是no=no,就近原则。将没有意义。
    }
    //同理 get和set方法里也不能省略。
}

3.通过this()在一个构造方法里调用另一个。

class Date{
    private int year;
    private int month;
    private int day;
    public Date(){
       /*
        代码重复
        this.year =1970;
        this.month=1;
        this.day=1;
        */
        this(1970,1,1);
    }
    public Date(int year,int month,int day){
        this.year=year;
        this.month=month;
        this.day=day;
    }
    
}

this()使用一个构造方法去调用当前类的另一个构造方法,是代码简洁

特别注意

对this的调用必须是构造器中的第一个语句!!!!!!

22/12.8

十四.继承extends

1.什么是继承

使用继承解决代码臃肿。

一个类中的全部代码(父类)在另一个类(子类)也有。

可继承父类所有代码,除了构造方法以外

父类 class one{}

比如子类

class two{}

只需在子类 class two extends one{}即可

2.作用

基本作用:代码的复用。

主要作用:有了继承,才有了后期的方法覆盖和多态机制、

3.继承的相关特性

B类继承A类,则称A类为超类(superclass),父类,基类。

B类则称为子类(subclass),派生类,扩展类

java中的继承只支持单继承,c++支持多继承

不过可以好几个类同时继承一个类。

如果想继承好几个类

class a{
    
}
class b extends a{
    
}
class c extends b{
    
}

这样c就同时继承了a和b

直接继承b,间接继承a。

3.除构造方法外,其他都能继承。 但是私有的属性无法在子类中直接访问。

4.java中,所有的类默认继承object类。

5.继承缺点:一改父类,子类受牵连。

4.什么时候用继承

从语文上理解,cat是animal,所以cat能继承animal

5.object类中的toString方法

每个类都会自动继承object类。

toString是实例方法。

需要先new对象。

class project{
    project pro=new project();
    System.out.println(pro.toString);
    //project@5305068a
    //@前面是类名,后面的可等同看做对象在堆内存当中的内存地址。
    //实际上的内存地址经过“哈希算法”得出的十六进制结果。
    
    //如果直接输出引用呢
    System.out.println(pro);
    //project@5305068a
    //println方法会自动调用pro的toString()方法。
}

因为println会自动调用引用的toString()方法,所以可以利用方法覆盖把toString改成自己想要的内容,然后调用时println(引用)就行,很方便、

十五.方法的覆盖和多态

1.方法覆盖override

方法覆盖,方法重写,override overwrite

1.1什么时候方法重载overload(回顾)

在一个类中,功能相似的话,建议将名字定义的一样,代码美观。

什么条件满足后能构成方法重载?????

1.在同一个类中

2.方法名相同

3.参数列表不同(个数.顺序.类型)

1.2方法覆盖例子
public static void main(String[] arge){
   cat s1=new cat();
   dog s2=new dog();
   s1.move();
   s2.move();
}
class Animals{
    public void move(){
       System.out.println("动物在行动");
    }
    
}
class cat extends Animals{
     public void move(){
       System.out.println("猫在走猫步");
    }
}
class dog extends Animals{
     public void move(){
       System.out.println("狗在跑步");
    }
}

继承后的某些方法不能满足业务需求,直接把方法重写一次,然后改掉里面想更改的就行。

1.3方法覆盖条件

1.两个类必须有继承关系

2.重写后的方法和原方法一切都相同(除了想更改的)。

3.访问权限不能更低,可以更高。 【先记住】 修饰符列表 protected(受保护的)比public低。

4.重写之后的方法不能比之前的方法抛出更多的异常,可以更少(这个先记住)。

1.4注意事项

1.方法覆盖只是针对方法,和属性无关

2.私有方法无法覆盖。

3.构造方法不能被继承,所以构造方法也不能被覆盖

4.只是针对实例方法覆盖,静态方法覆盖没有意义

2.多态的基础语法

了解多态之前,先了解

1.向上转型

子------>父(自动类型转换)

2.向下转型

父------>子(强制类型转换,需要加强制类型转换符)

无论向上还是向下,两者之间必须有继承关系

1.1向上转型

例如之前写过的一些例子,有父类animal,子类cat和bird。

animal s1=new cat();

这样也是允许的。

cat is a animal。

父类型的引用允许指向子类型的对象。

那么

s1.move();//输出什么呢? 猫走猫步 

为什么???

什么是多态?

多种形态,多种状态。

分析 s1.move();

java程序分为编译阶段和运行阶段。

先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是animal,所以编译器在检查语法的时候,会去animal.class字节码文件中找move()方法。找到之后绑定上,静态绑定成功(编译阶段静态绑定)

运行阶段:

实际上在堆内存创建的java对象是cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行cat对象的move()方法。这个过程属于运行阶段绑定。(运行阶段属于动态绑定。)

多态表示多种形态,编译的时候一种形态,运行的时候一种形态

//假如cat类里还有抓老鼠catchMouse这个方法
animal s1=new cat();
s1.catchMouse();//可以运行吗???

不行!!

因为分析要先分析编译阶段,编译时编译器认为s1是个animal,去animal字节码文件里找,找不到catchMouse方法,就报错了。

1.2向下转型

什么时候要向下转型????

父类中没有的,子类中特有的(猫抓老鼠)

需要访问子类中特有的方法时,必须向下转型

cat x=(cat)s1;//s1是上面代码块那个s1
x.catchMouse;//就可正常运行了
//s1是animal,将animal强制转化为cat,就可以抓老鼠了。

向下转型有风险吗

animal s2=new bird();
cat x=cat(s2);
x.catchMouse;

这段代码

先分析编译阶段:

没有任何问题,编译器认为s2是动物,然后转成猫,没问题。

运行阶段:

一个鸟怎么能转换成猫呢,猫和鸟没有继承关系。

这个异常十分重要::: java.lang.ClassCastException

1.3怎么避免ClassCastException的发生

运算符instanceof

第一:instanceof可以在运行阶段动态判断引用指向对象的类型。

第二:instanceof的语法

​ (引用 instanceof 类型)

第三:instanceof运算符的运算结果是true/false

第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。

​ 假设(c instanceof cat)为true,则c引用指向的堆内存中的java对象是cat

​ 反之,则不是cat。

因此,向下转型前需要判断

if(s2 instanceof cat){
    cat x=(cat)s2;
    x.catchMouse;
}

在向下转型前判断s2是cat才继续进行,确保不会造成从鸟转换成猫的悲剧。

任何时候向下转型时,必须使用instanceof,好习惯

1.4 为什么要用instanceof,自己判断是什么猫还是鸟不就行了吗
public void test(animal a){
    cat c=(cat)a;
    c.catchMouse();
    
}
//这个test方法,被传过来的不确定是猫还是鸟,所以像上面这么写就容易发生异常,所以需要用instanceof。

3.多态在开发中的作用

比如主人喂养宠物,今天主人可能喜欢狗,喂的是狗,明天主人喜欢猫了,如果不使用多态,那就得再写一次喂狗的方法,软件的扩展性很差。

使用多态,能提高扩展性。编译的时候编译器发现Pet是pet类,会去pet中找eat方法,结果找到了,编译通过

运行的时候,底层实际的对象是什么,就自动调用到该实际对象对应的eat(

方法上,这就是多态的作用。

面向父类型编程,面向更加抽象进行编程,不建议面向具体编程。

一些多态练习的例子: 乐手弹奏不同的乐器,主人喂养不同的宠物、

4.静态方法没有方法覆盖

假设没有多态机制,只有方法覆盖机制,你觉得有意义吗?

​ 没有多态机制的话,方法覆盖可有可无。

​ 没有多态机制的话,方法覆盖也可以没有,如果父类的方法无法满足子类业务需求的时候,子类完全可以定义一个新的方法。

Animal a=new Cat();
a.dosome();//dosome为静态方法
//这样,虽然用引用.调用,但是静态方法,所以还是animal. ,起不到多态的作用,方法覆盖也没有意义。
5.私有方法不能覆盖

私有不能覆盖,静态不谈覆盖。

6.方法覆盖时返回值类型问题
6.1 基本数据类型

方法覆盖前后的方法数据类型必须一致,该是int就是int

6.2 引用数据类型

父类时是ainimal,子类时是cat可以,java中允许。

父类时是animal 子类时是object,不行,java中不允许。

十六.super

1.super是一个关键字,全部小写。

2.super和this对比着study。

this:

​ this能出现在实例方法中和构造方法中。

​ this语法是 this.和this()

​ this不能用在静态方法中。

​ this大部分情况下可以省略、

​ this在区分局部变量和实例变量时候不能省略。

​ this()只能出现在构造方法第一行,通过当前的构造方法去调用“本类”中的其他构造方法,目的是:代码复用。

super:

可以出现在实例方法和构造方法中。

​ super语法是 super.和super()

不能用在静态方法中。

大部分情况下可以省略、

​ super什么时候不能省略呢?

​ super()只能出现在构造方法第一行,通过当前的构造方法去调用父类中的其他构造方法,目的是:创建子类对象的时候,先初始化父类型特征。

1.super()

表示通过子类的构造方法调用父类的构造方法。

模拟现实世界中的这种场景:要想有儿子,先有父亲。

2.重要结论

当一个构造方法第一行:

既没有this()也没有super(),会默认有一个super()

表示通过当前子类的构造方法调用父类的无参数构造方法

所以必须保证父类的无参数构造方法存在(不然就报错了)。

this()和super()不能共存!

class A{
    public A(){
        System.out.println("1");
    }
}
class B extends A{
    public B(){
         System.out.println("2");
    }
    public B(String name){
         System.out.println("3");
    }
}
class C extends B{
    public C(){
        this("zhangsan");
         System.out.println("4");  
    }
    public C(String name){
        this(name,20);
         System.out.println("5");
    }
    public C(String name,id){
        super(name);
         System.out.println("6");
    }
}

如果new一个c对象,输出结果是什么

13654

3.super(实参)是干啥的?

初始化当前对象的父类型特征,并不是创建新对象。[当你创建一个新对象,想初始化父类型特征,如果你在子类中用this.初始化,没用的,因为this.只能在本类(也就是父类)中用,因为引入super()来初始化父类型特征]

4.super关键字代表什么?

super关键字代表的就是“当前对象”的那部分父类型特征。

例如:

我继承了我父亲的一部分特征:

​ 例如:眼睛、皮肤等。

​ super代表的就是“眼睛、皮肤等”

​ “眼睛、皮肤等”虽然是继承了父亲的,但这部分是在我身上呢。

在这里插入图片描述

5.super.什么时候不能省略

父中有,子中又有,如果想在子中访问“父的特征”,super.不能省略。

java中

this.name :当前对象的name属性。

super.name:当前对象的父类型特征的name属性。

6.super和this

直接输出this,会自动引用this的toString方法

直接输出super,会报错

结论

super不是引用,super也不保存内存地址,super也不指向任何对象。

super只是代表当前对象内部的那一块父类型的特征。

标签:java,int,某站,class,2000,实例,Java,方法,public
From: https://blog.csdn.net/qq_73210658/article/details/142496914

相关文章

  • JavaEE——多线程
    接着上篇博客我们已经学习了进程的相关概念,了解到进程里面的相关重要属性,那么这篇博客呢我们要学习一个新的知识点——线程!一、引入进程的目的首先引入进程这个概念,是为了解决“并发编程”这样的问题。因为CPU再往小了做,比较困难了,这是因为CPU进入了多核心的时代,要想进一......
  • JavaEE——多线程Thread 类及常见方法
    目录 一、Thread(Stringname)二、是否后台线程isDeamon()三、是否存活isAlive()四、run()方法和start()方法的区别五、中断线程法一:法二:六、线程等待join()七、线程休眠sleep()一、Thread(Stringname)定义:这个东西是给线程(thread对象)起一个名字。起一个......
  • Java:排序算法
    Java中有很多种排序算法,每种算法都有其特点,适用于不同的场景。下面列举一些常见的排序算法,并简要介绍其特点:冒泡排序(BubbleSort)原理:通过重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素,这意......
  • Java BeanUtils使用介绍
    ‌JavaBeanUtils是一个广泛使用的Java工具类,提供了一系列方法来简化JavaBean对象之间的属性复制和操作。‌使用JavaBeanUtils可以方便地实现对象之间的属性复制、属性设置和获取、类型转换等功能。以下是使用JavaBeanUtils的基本步骤和注意事项:1.添加依赖‌:首先,你需要在项目......
  • java解析xml
    参考资料水平有限,欢迎交流千问ai阿伟的反射知识学习泛型知识学习软件设计模式(java版)程细柱目标xml<?xmlversion="1.0"encoding="UTF-8"?><conf><farm>SGFarm</farm><student><name>李四</name>&......
  • Java反射
    一、如何获取一个类:1、Classcls=Class.forName("java.lang.String");2、Stringstr="zhangsan";Classcls=str.getClass();3、Classcls=String.class;//注意String.class的class是小写的C二、利用反射访问类中的字段:1、getFields()方法:获得某个类中所有......
  • 基于Node.js+vue基于java的学生宿舍管理系统(开题+程序+论文) 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着高等教育规模的不断扩大,学生宿舍作为校园生活的重要组成部分,其管理效率与服务质量直接关系到学生的日常生活体验及学校的整体管理水平。传统的学生宿舍......
  • Java多线程
    文章目录前言1实现多线程1.1多线程概述1.2多线程的实现方式1.3线程休眠1.4线程优先级1.5守护线程2线程同步2.1同步代码块2.2Lock锁2.3死锁3生产者消费者3.1生产者和消费者模式概述3.2阻塞队列基本使用3.3阻塞队列实现等待唤醒机制前言"劝君莫惜金......
  • javawar包反解析代码
    一:概述在JavaEE开发中,WAR(WebApplicationArchive)文件是Web应用程序的标准打包格式。它允许开发者将Web应用的所有组件打包成一个单一的文件,以便于部署和分发。然而,有时我们可能需要从WAR文件中提取信息或修改内容,这就需要进行WAR包的反解析。本文将探讨几种实现JavaWAR包反解析......
  • Java面试指南(基础篇)
    文章目录前言01Java语言的特点02JVM、JRE及JDK的关系03Java和C++的区别04基本数据类型05类型转换06自动装箱与拆箱07String的不可变性08字符常量和字符串常量的区别09字符串常量池10String类的常用方法11String和StringBuffer、StringBuilder的区别12switch......