JAVA基础知识笔记
一个很好的在线运行java的环境
https://www.bejson.com/runcode/java/
一、java基础知识
1、DOS和Java特性
1、window中常见的Dos命令
输入cmd
- mkdir 创建目录
- cls 清除屏幕
- exit 退出当前Dos命令窗口
- dir 列出当前目录下所有的子文件/子目录
- cd 命令
*cd命令表示:change directory【改变目录】
*使用方法:cd 目录的路径 - cd… 回到上级目录
- cd/ 回到跟目录
- 怎么切换盘符?
c:回车
d:回车 - del 删除一个或多个文件
- ipconfig 查看本机IP地址
- ping IP地址 或 ping 域名:查看计算机是否可以正常通信
2、文件扩展名必须要以 .java结尾的扩展名
3、Java语言发展史
1 | |
4、Java语言特性【开源、免费、纯面向对象、跨平台】
简单性: 相对而言,例如Java中不在支持多继承,C++设计支持多继承的,多继承比较复杂。
C++中有指针,Java中屏蔽了指针的概念。所以Java是简单的。
Java语言的底层是C++实现的,不是c语言。
面向对象性: Java是纯面向对象的,更符合人的思维模式,更容易理解。
什么是面向对象?
封装:封装把一个对象的属性私有化、同时提供一些可以被外界访问的属性的方法。
继承:
java是多单继承的,
子类拥有父类非private的属性和方法
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
子类可以用自己的方式实现父类的方法
多态:父类的引用指向子类
可移植性:
什么是可移植性?(跨平台性)
1 | |
多线程性
健壮性: 和自动垃圾回收机制有关,自动垃圾回收机制简称GC机制。
Java语言运行过程中产生的垃圾是自动回收的,不需要程序员关心。
安全性
2、Java——集成开发环境
1 、什么是集成开发环境【简称:IDE】
- 集成开发环境讲究一站式开发,使用这个工具即可。有提示功能,有自动纠错功能。
- 集成开发环境可以让软件开发变得更简单。更高效
- 没有IDE工具:
需要安装JDK、需要配置环境变量、需要手动的将java源文件编译生成class字节码文件。
java源程序出错之后没有提示。
没有自动提示功能等。 - 有IDE工具:
不需要独立安装JDK【IDE中已经集成】。
不需要手动配置环境变量。
不需要使用javac命令对java源文件进行编译。
并且java源程序编写语法错误马上提示。
使用IDE工具有很多代码不需要写,自动生成了。
2、 java有哪些比较主流的IDE呢?
- eclipse(myeclipse)【最多】
- Intellij IDEA【上升趋势最大】
- Netbeans
- JBuilder
…
3 、IDE常见的知识
workspace: 工作区
在workspace工作区中有一个文件夹:.metadata:储存IDE工作状态。
企业级开发:JavaEE开发的专用布局 - Java Enterprise。
普通java开发:JavaSE的专用布局 - Java
工作区workspace中基本的单元是:Project(工程/项目)
重点窗口介绍:
- Package Explore / Navigator / Project Explore:可以看到java源文件
- Console:控制台窗口
3、java程序的编译和运行、JDK、JRE、JVM的关系、环境变量、HelloWorld程序
1、Java的加载与执行
*Java程序的运行包括两个非常重要的阶段
-编译阶段
-运行阶段
*编译阶段
编译阶段主要的任务是检查Java源程序是否符合Java语法
符合Java语法则能够生成正常的字节码文件(.class)
不符合Java语法规则则无法生成字节码文件
字节码文件不是单纯的二进制,这种文件无法在操作系统中直接执行。
javac.exe(Java编译程序,在JDK中自带)
怎么使用?在哪用?
在DOS命令窗口中使用。
**javac的使用规则:**javac java源文件的路径
class文件是最终要执行的文件,所以class文件生成之后,java源文件删除也不影响java程序执行。
*编译结束后,可以将class文件拷贝到其他操作系统当中运行。【跨平台性】
*运行阶段
JDK安装之后,除了自带的javac.exe之外,还有另外一个工具,叫做java.exe,其主要负责运行阶段。
java.exe在哪里用?怎么用?
-在DOS窗口使用
-使用方法 输入 java 类名
java.exe命令会启动Java虚拟机(JVM),JVM会启动类加载器ClassLoader
ClassLoader会去硬盘上搜索A.class文件,找到该文件则将该字节码文件装载到JVM中。
JVM将A.class字节码文件解释成二进制。
然后操作系统执行二进制和底层硬件平台进行交互。
2、JDK、JRE、JVM的关系
JDK:开发者提供的开发工具箱,是给程序开发者用的,它包括完整的jre、Java运行环境,还包括了其他供开发者使用的工具包。
JRE: 运行时所必须的包依赖的环境都在jre中
JVM: 当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理、垃圾回收和安全机制等,他独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因
JDK目录的介绍:
JDK/bin: 该目录下存放很多命令,例如javac.exe和java.exe
javac.exe负责编译
java.exe负责运行
开发HelloWorld程序
1 | |
将HelloWorld.java源程序通过javac工具编译:
首先需要解决的问题是:javac命令是否可用
打开DOS命令窗口,直接输入javac
*重要
PATH环境变量的配置
*注意:path环境变量和java语言没有关系,path环境变量是属于windows操作系统的一个知识点。path环境变量是专门给windows操作系统指路的。·
4、公开类、标识符、关键字
1、public class与class的区别
*一个java源文件中可以定义多个class*一个java源文件当中public的class不是必须的
*一个class会定义生成一个xxx.class字节码文件
*一个java源文件当中定义公开的类的话,public class只能有一个,且该类名称必须和java源文件名称一致。
*每一个class当中都可以编写main方法,都可以设定程序的入口,想执行B.class中的main方法:java B, 想执行X.class当中的main方法 :java X
注意:当在命令窗口中执行java Hello,那么要求Hello.class当中必须有主方法。没有主方法会出现运行阶段的错误。2、标识符
1 什么是标识符?-在java源程序中凡是程序员有权利自己命名的单词都是标识符。
-标识符在EditPlus编辑器中以黑色高亮显示2 标识符的命名规则?
*一个合法的标识符只能由“数字、字母、下划线_、美元符号$”组成,不能含有其他的符号。
*不能以数字开头
*严格区分大小写
*关键字不能做标识符
*理论上无长度限制,但是最好不要太长3 标识符的命名规范?(只是一种规范,不属于语法,编译器不报错)
*最好见名知意
*遵守驼峰命名规则
SystemService
UserService
CustomerService*类名、接口名:首字母大写,后面每个单词首字母大写。
*变量名、方法名:首字母小写,后面每个单词首字母大写。
*常量名:全部大写。4 合法和不合法的标识符
合法 不合法 _123Test 123Test HelloWorld Hello-World A_B_C Hello World $ABC HelloWorld# public1 public 3、关键字及含义
在Java中关键字都是小写
abstract 表明类或者成员方法具有抽象属性
assert 断言,用来进行程序调试
boolean 基本数据类型之一,声明布尔类型的关键字
break 提前跳出一个块
byte 基本数据类型之一,字节类型
case 用在switch语句之中,表示其中的一个分支 catch 用在异常处理中,用来捕捉异常
char 基本数据类型之一,字符类型
class 声明一个类
const 保留关键字,没有具体含义
continue 回到一个块的开始处
default 默认,例如,用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现
do 用在do-while循环结构中
double 基本数据类型之一,双精度浮点数类型
else 用在条件语句中,表明当条件不成立时的分支
enum 枚举
extends 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口
final 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量
finally 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
float 基本数据类型之一,单精度浮点数类型
for 一种循环结构的引导词
goto 保留关键字,没有具体含义
if 条件语句的引导词
implements 表明一个类实现了给定的接口
import 表明要访问指定的类或包
instanceof 用来测试一个对象是否是指定类型的实例对象
int 基本数据类型之一,整数类型
interface 接口
long 基本数据类型之一,长整数类型
native 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new 用来创建新实例对象
package 包
private 一种访问控制方式:私用模式
protected 一种访问控制方式:保护模式
public 一种访问控制方式:共用模式
return 从成员方法中返回数据
short 基本数据类型之一,短整数类型
static 表明具有静态属性
strictfp 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范
super 表明当前对象的父类型的引用或者父类型的构造方法
switch 分支语句结构的引导词
synchronized 表明一段代码需要同步执行
this 指向当前实例对象的引用
throw 抛出一个异常
throws 声明在当前定义的成员方法中所有需要抛出的异常
transient 声明不用序列化的成员域
try 尝试一个可能抛出异常的程序块
void 声明当前成员方法没有返回值
volatile 表明两个或者多个变量必须同步地发生变化
while 用在循环结构中
二、数据类型【编程基础】
1、字面值、变量、数据类型基础、编码方式
1、字面值
关于字面值
— 10/100
— 3.14
—“abc”
—‘a’
—true、false
字面值 就是数据。
字面值 是Java源程序的组成部分之一。包括标识符和它的关键字,他们都是Java源程序的组成部分。
数据在现实世界中是分类别的,所以数据在计算机编程语言中也是有类型的:【数据类型】
10、100 属于整数型
3.14 属于浮点型
true、false 属于布尔型
“abc”、“张广荣” 属于字符串型
‘A’、‘人’ 属于字符型
注意:
Java语言中所有字符串型字面值必须用双引号括起来,双引号是半角。
Java语言中所有字符型字面值必须用单引号括起来,单引号是半角。
2、变量
1 什么是变量?
变量本质上说是内存中的一块空间,这块空间“有数据类型”、“有名字”、“有字面值”。
变量包含三部分:数据类型、名称、字面值【数据】
变量是内存中存储数据的最基本的单元。
2 数据类型的作用?
不同的数据类型底层会分配不同大小的空间。
数据类型是指导程序在运行阶段应该分配多大的内存空间。
3 变量要求:变量中存储的具体的“数据”必须和变量的“数据类型”一致,当不一致的时候编译报错
4 声明/定义变量的语法格式:
数据类型 变量名
数据类型:概念在第三部分。
eg:int 整数型
变量名:只要是合法的标识符就行。规范中要求:首字母小写,后面每个单词首字母大写。
5 变量声明之后怎么赋值?
语法格式: 变量名=字面值
要求:字面值的数据类型必须和变量的数据类型一致。
=等号是一个运算符,叫做赋值运算符,赋值运算符优先运算等号右边的表达式,表达式执行结束之后的结果赋值给等式左边的变量。
6 声明和赋值可以放到一起完成。
int i=10
7 变量赋值之后,可以重新赋值,变量的值可以变化:
int i=10;
System.out.println(i);
i=20;
System.out.println(i);
i=100;
System.out.println(i);
8 有了变量的概念之后,内存空间得到了重复的使用:
int i=10;
System.out.println(i);
System.out.println(i);
System.out.println(i);
…
9 通常访问一个变量包括两种访问形式:
第一种:读取变量中保存的具体数据 get/获取
第二种:修改变量中保存的具体数据 set/设置
i=10; //set
System.out.println(i); //get
10 变量在一行可以声明多个
11 Java中的变量必须先声明,再赋值,才能访问。
int i; //程序执行到这里,内存空间并没有开辟出来,变量i并没有初始化。所以没有赋值之前是无法访问的。
12 关于Java变量的作用域
什么是作用域?
变量的作用域,其实描述的就是变量的有效范围。在什么范围之内是可以被访问的,只要出了这个范围该变量就无法访问了。
变量的作用域只要记住一句话:出了大括号就不认识了。
13 关于变量的分类:
关于变量声明的位置来分类:
局部变量: 在方法体中声明的变量。
成员变量: 在方法体【类体之内】声明的变量叫做成员变量。
在不同的作用域当中,变量名可以相同。
在同一个作用域当中,变量名不能重名。
3、数据类型
1 数据类型的作用是什么?
程序当中有很多数据,每一个数据都是有相关类型的,不同数据类型的数据占用空间大小不同。
数据类型的作用是指导JVM在运行程序的时候給该数据分配多少空间。
2 Java中的数据类型包括两种
基本数据类型
引用数据类型
3 关于基本数据类型
基本数据类型包括四大类八小种:
第一类:整数型 (byte,short,int,long)
第二类:浮点型 (float,double)
第三类:布尔型 (boolean)
第四类:字符型 (char)
4 字符串“abc”不属于基本数据类型,属于 “引用数据类型” ,字符属于基本数据类型:
字符串使用双引号 “ ”
字符使用单引号 ‘ ’
5 八钟基本数据类型各自占用空间大小是多少?
| 基本数据类型 | 占用空间大小 【单位:字节】 | 取值范围 |
|---|---|---|
| byte | 1 | -128~127 |
| short | 2 | ·32768~32767 |
| int | 4 | -2147483648~2147483647 |
| long | 8 | ·263~263-1 |
| float | 4 | 有效位6~7位 |
| double | 8 | 有效位15位 |
| boolean | 1 | true,false |
| char | 2 | 0~65535 |
| 6 计算机在任何情况下都只能识别二进制。例如:只认识010101010100101… |
文字与二进制通过字符编码的方式进行对照转换,最先出现的是ASCII码【采用一个字节编码】
支持简体中文的编码方式是GB2312< GBK< GB18030
支持繁体中文:< big5 >
后来出现了一种方式统一了全球所有文字,容量较大,这种编码方式叫做:Unicode编码
unicode编码方式有多种具体的实现:
- UTF-8
- UTF-16
- UTF-32
Java语言采用的编码方式是unicode编码方式,所以“标识符”可以用中文。
2、char、转义、整数型、精度损失
关于八种基本数据类型的默认值
| 数据类型 | 默认值 |
|---|---|
| byte,short,int,long | 0 |
| float,double | 0.0 |
| boolean | false【在C语言中true是1,false是0】 |
| char | \u0000 |
成员变量没有手动赋值系统会默认赋值【局部变量则不会】
八钟基本数据类型的默认值都是一切向0看齐。
1、char型
一个中文占用2个字节,char类型正好是2个字节
所以Java中的char类型变量可以存储一个中文字符
“ab”是字符串不能用单引号括起来
2、转义字符 (\)
\ :反斜杠在Java语言中具有转义功能
\n : 换行符
System.out.println与 print 的区别:println输出之后换行,print表示输出,但是不换行。
\t : 制表符 ,即Tab键
\ ’ : 普通的单引号
\ \ : 普通的反斜杠
\ “ : 普通的双引号
怎样在控制台输出“反斜杠字符”?
1 | |
第一个反斜杠具有转义功能,将后面的反斜杠转义为p普通的反斜杠字符
结论:因此在Java中两个反斜杠代表一个普通的反斜杠字符
怎样在控制台输出一个普通的单引号?
1 | |
3、整数型
1 Java语言当中的“整数型字面值”被默认当做int处理。要让这个“这个整数型字面值”被当做long类型处理的话,需要在“整数型字面值”后面添加1/L,建议使用大写的L。
2 Java语言当中的整数型字面值有三种表现方式:
第一种方式:十进制【是一种缺省默认的方式】 int i=10
第二种方式:八进制【在编写八进制整数型字面值的时候需要以0开始】 int i=010
第三种方式:十六进制【在编写十六进制整数型字面值的时候需要以0x开始】 int i=0x10
long y=2147483648L
long y=2147483648
2147483648是一个过大整数,被当做int类型4个字节处理,但是这个字面值超过int类型范围
解决方法:
在字面值后面添加L,将其当做long类型处理,y是类型变量,以上程序不存在类型转换。
4、精度损失
计算机二进制有三种表示形式:
原码
反码
补码
计算机在任何情况下底层表示和存储数据的时候采用了补码形式。
正数的补码:和原码相同。
负数的补码:负数的绝对值对应的二进制所有二进制位取反,再加1
当一个整数字面值没有超过byte,short,char的取值范围,这个字面值可以直接赋值给byte,short,char类型的变量。这种机制被SUN允许了,目的是为了方便程序员编程。
char cc=65535 //通过
char cc=65536 //编译报错
3、浮点型、布尔型、类型转换、算数运算符、关系运算符、逻辑运算符
1、浮点型
float 单精度【4个字节】
double 双精度【8个字节,精度较高】
在Java语言当中,所有的浮点字面值【3.0】,默认被当做double类型来处理。
要想该字面值当做float类型来处理,需要在字面值后面添加F/f
注意:
double和float在计算机内部二进制存储的时候存储的都是近似值。
在现实世界当中有一些数字是无限循环的,列如:3.3333333…
计算机资源是有限的,用有限的资源存储无限的数据只能存储近似值。
2、布尔型
关于布尔型数据类型:boolean
在Java语言当中boolean类型只有两个值:true、false,没有其他值。
不像C语言当中,0和1可以表示假和真。
在底层存储的时候boolean类型占用1个字节,因为实际存储的时候false底层是0,true底层是1.
布尔类型在实际开发当中非常需要,经常使用逻辑运算和条件控制语句当中。
3、类型转换
关于基本数据类型之间的互相转换:
转换规则:
1 八种基本数据类型当中除布尔类型之外剩下的7种类型之间都可以互相转换。
2 小容量向大容量转换,称为自动类型转换,容量从小到大排序:
byte < short ,char < int < long < float < double
注: 任何浮点类型不管占用多少个字节,都比整数型容量大。
char 和 short可表示的种类数量相同,但是char可以取更大的正整数 。
3 大容量转换成小容量,叫做强制类型转换,需要加强制类型转换符,程序才能编译通过,但是在运行阶段可能会损失精度,所以谨慎使用。
4 当整数字面值没有超出byte,short,char的取值范围,可以直接赋值给byte,short,char类型的变量。
5 byte,short,char混合运算的时候,各自先转换成int类型再做运算。
6 多种数据类型混合运算,先转换成容量最大的那种类型再做运算。
4、算数运算符
关于Java编程中的运算符之:算数运算符
| + | 求和 |
|---|---|
| - | 相减 |
| * | 乘积 |
| / | 商 |
| % | 取余数【取模】 |
| ++ | 自加1 |
| - - | 自减1 |
小结:++运算符可以出现在变量前,也可以出现在变量后,,无论是变量前还是变量后只要++运算结束,该变量中的值一定会自加1
++a:先自增1,在进行运算。
a++:先运算,再自增1。
5、关系运算符
| > | 大于 |
|---|---|
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| == | 等于 |
| != | 不等于 |
= 是赋值运算符;
== 是关系运算符;
关系运算符的结果一定是布尔类型。
1 | |
6、逻辑运算符
| 逻辑运算符 | 解释 |
|---|---|
| & | 逻辑与 (两边的算子都是true,结果才是true) |
| ! | 逻辑非(取反,!false就是true,!true就是假,这是一个单目运算符) |
| ^ | 逻辑异或(两边的算子只要不一样,结果就是true) |
| && | 短路与(第一个表达式执行结果是false,会发生短路与) |
| 逻辑或(两边的算子只要有一个是true,结果就是true)
|| 短路或(第一个表达式执行结果是true,会发生短路或)
1 逻辑运算符要求两边的算子都是布尔类型,并且逻辑运算符最终的运算结果也是一个布尔类型。
2 短路与和逻辑与最终的运算结果是相同的,只不过短路与存在短路现象。
3 短路或和逻辑或最终的运算结果是相同的,只不过短路或存在短路现象。
1 | |
从某个角度来看,短路与更智能。由于后面的表达式可能不执行,所以执行效率较高。这种方式在实际开发中使用较多。短路与比逻辑与使用的多。短路与更常用。
但是,在某些特殊的逻辑业务中,要求运算符两边的算子必须全部执行,此时必须使用逻辑与,不能使用短路与,使用短路与可能导致右边的表达式不执行。
4、赋值运算符、字符串连接运算符、三目运算符/三元运算符/条件运算符
1、赋值运算符
java中赋值类运算符包括两种:
基本的赋值运算符:=
扩展的赋值运算符:+=、-+、*=、/+、%=
赋值类的运算符优先级:先执行等号右边的表达式,将执行结果赋值给左边的变量。
注意以下代码:
byte x=10;
x += 5;//并不等同于:x =x+5,而等同于:x= (byte)(x+5)
int y=10;
y += 5; //等同于:y= (int)(y+5)
long a=10L;
int b=20;
b += a; // 等同于:b = (int)(b+a)
重要结论:扩展类的赋值运算符不改变运算结果类型,假设最初这个变量的类型是byte类型,无论怎么追加或追减,最终该变量的数据类型还是byte类型。
2、字符串连接运算符
关于java中的 “+” 运算符:
1 +运算符在java语言中当中有两个作用:
加法运算
字符串的连接运算
2 数字+数字 →数字【求和】
数字+“字符串” →“字符串”【字符串连接】
3 在一个表达式当中可以出现多个“+”,在没有添加小括号的前提下,遵循自左向右的顺序依次运算。
1 | |
String是引用数据类型,s是变量名,“abc”是String类型的字面值
String s=“abc”
3、三元运算符
三元运算符/三目运算符/条件运算符
1、语法规则:
布尔表达式?表达式1:表达式2
2、三元运算符的执行原理
当布尔表达式的结果是true的时候,选择表达式1作为整个表达式的执行结果
当布尔表达式的结果是false的时候,选择表达式2作为整个表达式的执行结果
三、流程控制语句
1、控制语句、java输入语句、if语句
Java控制语句可以分为7种:
控制选择结构语句
- if、if else
- switch
控制循环结构语句
- for
- while
- do while
改变控制语句顺序
- break
- continue
1、if语句
if语句属于选择结构,if语句又被称为分支语句/条件控制语句
1 if语句的语法结构:四种编写方式
第一种
if(布尔表达式){
java语句;
java语句;
…
}
第二种
if(布尔表达式){
java语句;
java语句;
…
} else {
java语句;
java语句;
…
}
第三种
if(布尔表达式){
java语句
java语句
…
}else if(布尔表达式){
java语句
java语句
…
}
else if(布尔表达式){
java语句
java语句
…
}else if(布尔表达式){
java语句
java语句
…
}…
第四种
if(布尔表达式){
java语句
java语句
…
}else if(布尔表达式){
java语句
java语句
…
}
else if(布尔表达式){
java语句
java语句
…
}else {
java语句
java语句
…
}…
2 重点 :对于java语句中的if语句来说,只要有一个分支执行,整个if语句全部结束。
3 注意:以上的第二种编写方式和第四种编写方式都带有else分支,这两种方式可以保证100%会有分支执行。
4 所有的控制语句都是可以嵌套使用的,只要合理嵌套就行。
注意:嵌套使用的时候,代码格式要保证完美。【该缩进的时候必须缩进,大部分情况下使用大括号包围的需要缩进】
5 if语句的分支中只有一条java语句的时候,大括号可以不写。
1 | |
6 接收用户键盘的输入语句:
1 | |
正确语句:
1 | |
错误语句:
1 | |
2、switch控制语句
关于switch语句:
1、witch语句也属于选择结构,也是分支语句
2、switch语句的语法结构:
一个比较完整的switch语句应该这样编写
1 | |
3、switch语句的执行原理:
switch语句后面小括号当中的“数据”进行一一匹配,匹配成功的分支执行按照自上而下的顺序依次匹配。
4、匹配成功的分支执行,分支当中最后有“break;”语句的话,整个switch语句终止。
5、匹配成功的分支执行,分支当中没有“break;”语句的话,直接进入下一个分支执行(不进行匹配),这种现象被称为 case穿透 现象。【提供break;语句可以避免穿透】
1 | |
6、所有分支都没有匹配成功,当有default的语句的话,会执行default分支当中的程序
7、switch后面和case后面只能是int或者String类型的数据,不能是探测其他类型。
- 当然byte,short,char也可以直接写到switch和case后面,因为它们可以进行自动类型转换。byte,short,char可以自动转换成int类型。
- JDK6的,switch和case后面只能探测int类型。
- JDK7之后包括7版本在内,引入新特性,switch关键字和case关键字后面可以探测 int 和string类型的数据。
8、case可以合并:
1 | |
10、switch语句例子
1 | |
11、switch确实可以探测String类型,这是Java7的新特性。
简单计算器系统实现:
1 | |
假设系统给定考生成绩,判断该考生的成绩等级:
- 有效成绩范围:【0-100】
- 考试成绩可以有小数
- 考试成绩和等级之间的对照关系:
【90-100】 A
【80-90】 B
【70-80】 C
【60-70】 D
【0-60】 E- 以上需求必须采用switch语句完成,不能采用if
窍门:(int)(成绩 / 10)
1 | |
3、循环结构、for控制语句
1、循环结构
在程序当中总有一些需要反复的执行的代码,假设没有循环结构,那么这段需要重复执行的代码自然是需要重复编写的。代码无法得到重复使用。所以多数编程语言都是支持循环结构的。将来把需要反复执行的代码片段放到“循环体”中,再联合“计数器”,共同控制这段需要反复执行的代码。
基本上的所有编程语言支持的循环包括三种:
- for 循环
- while 循环
- do…while 循环
2、for循环
1 语法结构:
1 | |
2 for循环的执行过程 / 执行原理?
2.1 初始化表达式、布尔表达式、更新表达式不是必须的!【但是两个分号是必须的】
2.2 初始化表达式最先执行,并且在整个for循环当中只执行一次。
2.3 布尔表达式必须是true/false,不能是其他值。
2.4 for的执行过程:
1、先执行初始化表达式,并且该表达式只执行一次。
2、判断布尔表达式的结果是true还是false:
- 布尔表达式为true:
1、执行循环体
2、执行更新表达式
3、判断布尔表达式的结果是true还是false
布尔表达式true
1、执行循环体
2、执行更新表达式
3、判断布尔表达式的结果是true还是false
…- 布尔表达式为false
循环结束
循环语句和条件判断语句嵌套使用【for和if的嵌套】
找出1~100所有的奇数
1 | |
求1~100所有奇数的和
1 | |
3 for循环也可以嵌套for循环
内层循环中的变量名和外层循环中的变量名不能重名
使用for循环输出九九乘法表
1 | |
编写for循环找出1~100中的所有素数
素数: 又被称为质数,能够被1和自身整除,不能被其他数字整除的数字称为素数。
1 | |
4、while控制语句、do…while控制语句、break、continue控制循环语句
1、while循环的语法结构
1 | |
2、while循环的执行原理:
判断布尔表达式的结果,如果结果为true就继续执行循环体,直到布尔表达式的结果为false,循环结束。
3、while循环的循环次数::0~N次
注意:while循环的循环体可能执行次数为0次。
1 | |
1、do…while循环控制语句
1、do…while循环的语法结构
1 | |
2、do…while循环的执行原理
先执行循环体,然后判断布尔表示是是否为true,true则执行,false则循环结束。
3、do…while循环的执行次数
do…while循环的循环体代码片段执行次数是:1~N次【至少一次】
4、使用do…while循环的注意事项
do…while循环语句最终有一个 “分号” 别丢了。
1 | |
2、break控制语句
- break是java语言当中的关键字,被翻译为“中断/折断”。
- break + “;”可以成为一个单独的完整的java语句: break;
- break语句使用在switch语句当中,用来终止switch的语句执行。
- break语句同样可以使用在循环语句当中,用来终止循环的执行。
- break终止哪个循环呢?
- break;语句使用在for、while、do…while循环语句当中用来跳出循环,终止循环的执行。因为当程序循环到某个条件时,后续的循环就没必要执行了,在执行也是耗费资源,所以可以终止循环,这样可以提高程序的执行效率。
- 在默认情况下:break语句终止的是离它最近的循环语句。
- 当然也可以指定终止某个循环,需要给循环起名,采用这种语法: break 循环名称;
以下以for循环为例解释 break; 语句
1 | |
3、continue控制语句
1、continue表示:继续/go on/ 下一个
2、continue也是一个continue关键字加一个分号构成一个单独的完整的java语句,主要出现循环语句当中用来控制循环的执行。
3、break和continue的区别:
- break表示循环不执行了。
- continue表示直接进入下一次循环继续执行。
1 | |
4、continue也有这样的语法:
continue 循环名称;
四、构造方法
1、方法的基础知识、语法及含义、方法调用规则
1、方法
在Java语言当中应该有这样一个机制:
- 某个功能代码只需要写一遍
- 要使用这个功能,只需要给这个功能传递具体的数据
- 这个功能完成之后返回一个最终的结果。
这样代码就可以重复使用了,提高代码的复用性。【这就是 “方法” 】
使用这个方法我们称为 “调用/invoke”
2、方法的本质
方法就是一段代码片段,并且这段代码片段可以完成某个特定的功能,并且可以被重复使用。
方法,对应的英语单词:Method,在C语言中叫做函数:Function、
方法定义在类体中,在一个类当中可以定义多个方法,方法编写的位置没有先后顺序,可以随意。
方法体中不能再定义方法!!!!!!
3、方法的基础语法
1、语法结构
1 | |
2、对以上语法结构进行解释说明:
2.1 关于修饰符列表
- 可选项,不是必须的
- 目前统一写成public static
- 方法的修饰符列表当中”有static关键字”的话,怎么调用这个方法?
类名.方法名(实际参数列表);
2.2 返回值类型
1、什么是返回值?
- 一个方法是可以完成某个特定功能的,这个功能结束之后大多数都是需要返回最终执行结果的,执行结果可能是一个具体存在的数据。而这个具体存在的数据就是返回值。
2、返回值类型?
- 返回值就是一个具体存在的数据,数据都是有类型的,此处需要指定的是返回值的具体类型。
3、返回值类型都可以指定哪些类型呢?
- java任意一种类型都可以,包括基本数据类型和所有的引用数据类型。
4、也可能这个方法执行结束之后不返回任何数据,java中规定,当一个方法执行结束之后不返回任何数据的话,返回值类型位置必须编写:*void 关键字*。
5、返回值类型可以是:
- byte,short,int,long,float,double,boolean,char,string,void…
6、返回值类型若不是 void,表示这个方法执行结束之后必须返回一个具体的数值。当方法执行结束的时候,没有返回任何数据的话,编译器报错。怎么返回值呢?代码怎么写呢?“return 值;”并且要求“值”的数据类型必须和“方法的返回值类型”一致,不然编译器报错。
7、返回类型是void的时候,在方法体当中不能编写“return 值;”这样的语句。但是要注意可以编写“return;”这样的语句。
8、只要带有关键字的语句执行,return语句所在的方法结束。【不是JVM结束,是return所在的方法结束】
2.3 方法名
- 只要是合法的标识符就行
- 方法名最好见名知意
- 方法名最好是动词
- 方法名首字母要求是小写,后面每个单词首字母大写
2.4 形式参数列表:简称形参
- 形参是局部变量:int a;double b;…
- 形参的个数可以是0~N个
- 多个形参之间用“逗号”隔开
- 形参中起决定性作用的是形参的数据类型,形参的名字就是局部变量的名字。
- 方法在调用的时候,实际给这个方法传递的真实数据被称为:实际参数,简称实参
- 实参列表和形参列表必须满足:
1 数量相同
2 类型对应相同
2.5 方法体必须由大括号括起来,方法体中的代码有顺序
实例:
1 | |
3、方法怎么调用?
方法只定义不去调用是不会执行的,只有在调用的时候才会执行。
语法规则:《方法的修饰符列表当中有static》
- 类名.方法名(实参列表);<这是一条java语句,表示调用某个类的某个方法,传递这样的实参。>
2、java的方法调用、调用的实参与形参类型、方法返回值类型不是void、深入return语句
1、Java语言的方法
实例
1 | |
以上代码了看出:方法的调用不一定在main方法当中。只要是程序可以执行到的位置,都可以调用其他的方法。
1、方法调用的时候实参和形参要求个数对应相同,数据类型对应相同。
类型不同的时候要求能够进行相应的自动类型转换
1 | |
2、方法调用
1、方法的修饰符列表当中有static关键字,完整的调用方法是:类名 . 方法名(实参列表)
2、但是,有时候 “ 类名.” 可以省略,什么情况下可以省略呢?
- 对于方法的修饰符列表当中有static关键字的:“类名.” 可以省略不写。
- 调用同个类中的方法可省略不写
1 | |
输出结果:
方法中的代码是自上而下的顺序依次执行的
1、方法的返回值类型不是void的时候
- 返回值类型不是void的时候:要求方法必须保证百分百的执行“return 值;”这样的语句来完成值的返回。没有这个语句编译器会报错。
- 一个方法有返回值的时候,当我们调用这个方法的时候,方法返回了一个值,对于调用者来说,这个返回值可以选择接收,也可以选择不接收。
1 | |
2、深入return语句
- 带有return关键字的java语句只要执行,所在的方法执行结束。
- 在“同一个作用域”当中,return语句下面不能编写任何代码,因为这些代码永远都执行不到,所以编译报错。
1 | |
在返回值类型是void的方法当中使用“return;”语句。“return;”语句当中出现在返回值为void的方法当中主要是为了终止方法的执行。
return;直接终止方法;break只是终止循环。
return更强
3、方法执行JVM内存分析
1、方法在执行过程当中,在JVM中的内存是如何分配的呢,内存是如何变化的?
- 方法只定义,不调用,是不会执行的,并且在JVM中也不会给该方法分配“运行所属”的内存空间。只有在调用这个方法的时候,才会动态的给这个方法分配所属的空间。
- 在JVM内存划分上有这样三块主要的内存空间:
1、方法区内存
2、堆内存
3、栈内存- 关于栈数据结构:
1、栈:stack,是一种数据结构
2、数据结构反应的是数据的存储形态。
3、数据结构是独立的学科,不属于任何编程语言的范畴,只不过在大多数编程语言当中要使用数据结构。
4、作为程序员需要提前精通:数据结构+算法【计算机专业必修的一门语言】
5、常见的数据结构:数组、队列、栈、链表、二叉树、哈希表…- 方法代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?
1、方法片段属于.class字节码文件的一部分,字节码文件在类加载的时候,将其放到了方法区当中。所以JVM中的三块主要的内存空间中的方法区内存最先有数据,存放了代码片段。
2、代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。每一次调用这个方法的时候,需要给该方法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属空间】- 方法在调用的时候,会给该方法分配独立的内存空间,在栈中分配,此时发生压栈动作,方法执行结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作。
压栈:给方法分配内存。
弹栈:释放该方法的内存空间。- 局部变量在栈中存储。局部变量在运行阶段内存在栈中分配。

栈内存:
1) 是为java方法提供运行空间的
2) 方法一旦被调用就会在栈中创建对应的栈帧,而方法的整个执行过程就是方法对应的栈帧从入栈到出栈的过程。换言之,就是方法被调用进栈(压栈 入栈),方法执行结束出栈(弹栈)。
3) 栈是先进后出后进先出(先被调用的方法最后结束,后被调用的方法最先结束)
4) 栈中的变量都是属于方法的,所以都是局部变量,且局部变量必须初始化值。
5) 栈生命周期与其所属线程的生命周期一致,可以认为栈具有自动销毁机制。
堆内存:
1) 是为实体对象来开辟空间的,换言之就是实体对象的空间都在堆中开辟。凡是被new出来的都是对象。
2) 堆中的变量是对象变量,因为是属于对象的,且是随着对象的创建而产生随着对象的销毁而销毁。
3) 堆中的变量(对象变量)都有默认值:
- 整数:0
- 浮点型:0.0
- 布尔型:false
- char型:’ ’
- 对象:null
4) 堆没有自动销毁机制,它里面的垃圾由垃圾回收器负责收集(通过收集算法判断哪个对象属于垃圾然后再清理)
5) 堆中的空间都有一个16进制的首地址作为地址进行区分。
方法区:
方法区中存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
常量是存放在方法区中的运行时常量池中的。
重点:方法调用的时候,在参数传递的时候,实际上传递得是变量中保存的那个“值”传过去了。
4、方法重载机制overload、方法递归
1、方法重载(overload)
- 功能虽然不同,但是“功能相似”的时候,方法重载机制可以让相似的方法就像在用一个方法一样。【Java支持这种机制而有些语言则不支持,例如JavaScript】
- 功能相似的时候,方法名可以相同。
1、什么时候考虑使用方法重载?
- 功能相似的时候,尽可能让方法名相同【但是功能不同/不相似的时候,尽可能让方法名不同】
2、什么条件满足之后构成了方法重载
- 在同一个类当中
- 方法名相同
- 参数列表不同:
1、数量不同:public static void m1(){} 与 public static void m1(int a){}
2、顺序不同:public static void m2(int a,double b){} 与 public static void m2(double a,int b){}
3、类型不同:public static void m3(int x){} 与 public static void m3(double x){}
1 | |
3、方法重载和什么有关系,和什么没有关系
- 方法重载和方法名+参数列表有关系
- 方法重载和返回值类型无关
- 方法重载和修饰符列表无关
2、方法递归
- 什么是递归?
即:方法自身调用自身。
1 | |
- 递归是很耗栈内存的,递归算法可以不用的时候尽量别用。
- 以下程序运行的时候发生这样的一个错误【不是异常,是错误error】:
java.lang.StackOverflowError
栈内存溢出错误
错误发生无法晚挽回,只有一个结果。就是JVM停止工作。 - 递归必须有结束条件,没有结束条件一定会发生栈内存溢出错误。
- 递归即使有了结束条件,即便结束条件是正确的,也可能会发生栈内存溢出错误,因为递归太深了。
注意:
- 递归可以不使用尽量别用。
- 但是有些情况下该功能的实现必须依靠递归方式。
例子:
1 | |
五、面向对象及封装
1、面向对象和面向过程的区别、面向对象、类和对象概念、类的定义
1、面向对象和面向过程的区别
- 面向过程: 主要关注点是:实现的具体过程,因果关系【集成显卡的开发思路】
优点: 对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低。
缺点: 采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间“耦合度”非常高,只要其中一环出现了问题,整个系统受到影响,导致最终的软件扩展力差。另外,由于没有独立体的概念,所以无法达到组件复用。 - 面向对象: 主要关注点是:主要关注对象【独立体】能完成哪些功能。【独立显卡的开发思路】
优点: 耦合度低,扩展能力强。更容易解决现实世界当中更复杂的业务逻辑,组件复用性强。
缺点: 前期投入成本高,需要进行独立体的抽取,大量的系统分析与设计。
- C语言是纯面向过程的,C++半面向对象、Java纯面向对象
2、面向对象的三大特征
- 封装
- 继承
- 多态
注:所有面向对象的编程语言都有这三大特征。
采用面向对象的方式开发一个软件,生命周期当中:【整个生命周期中贯穿使用OO面向对象方式】
- 面向对象的分析:OOA
- 面向对象的设计:OOD
- 面向对象的编程:OOP
3、类的对象的概念
1、什么是类?
- 类在现实世界当中是不存在的,是一个模板,是一个概念。是人类大脑思考抽象的结果。
- 类代表了一种事物。
- 在现实世界中,对象A与对象B之间具有共同特征,进行抽象总结出一个模板,这个模板被称为类。
2、什么是对象?
- 对象是实际存在的个体。现实世界当中实际存在。
4、软件开发的过程:
- 程序员先观察现实世界,从现实世界当中寻找对象
- 寻找了N个对象之后,发现所有的对象都有共同特征
- 程序员在大脑中形成一个模板【类】
- Java程序员可以通过java代码来表述这个类
- Java程序中有了类的定义
- 然后通过类就可以创建对象
- 有了对象之后,可以让对象直接协作起来形成一个系统。
类–【实例化】->对象
对象又被称为实例/instance
对象–【抽象】–>类
重点:
- 类描述的是对象的共同特征。
- 共同特征例如:身高特征
这个身高特征在访问的时候,必须先创建对象,通过对象去访问这个特征。因为这个特征具体的某个对象上之后,值不同。有的对象身高1.80,有的对象身高2.80.
5、一个类主要描述什么信息呢?
一个类主要描述的是:状态+动作。
状态信息:名字、身高、性别、年龄
动作信息:吃、喝、玩、乐
- 状态–>一个类的属性
- 动作–>一个类的方法
1 | |
注意:状态和动作当具体到某个对象上之后,发现最终的结果可能不一样。
4、类的定义【开始编写代码】
语法结构:
1 | |
重点:属性通常是采用一个变量的形式来完成定义的。
1 | |
2、对象的创建和使用、内存分析
1、对象的创建和使用
1 | |
- 通过一个类可以实例化N个对象,
- 实例化对象的语法:
new 类名() - new是java语言当中的一个运算符
- new运算符的作用是创建对象,在JVM堆内存当中开辟新的内存空间
- 方法区内存:在类加载的时候,class字节码代码片段被加载到该内存空间当中。
- 栈内存(局部变量):方法代码片段执行的时候,会给该方法分配内存空间,在栈内存中压栈。
- 堆内存:new的对象在堆内存中存储
对于Student s = new Student ();
- student是一个引用数据类型
- s是一个变量名
- new Student()是一个学生对象
- s是一个局部变量【在栈内存中存储】:引用
- 什么是对象? new运算符在堆内存中开辟的内存空间称为对象。
- 什么是引用? 引用是一个变量,只不过这个变量中保存了另一个java对象的内存地址。
- java语言当中,程序员不能直接操作堆内存,java中没有指针,不像C语言
- java语言当中,程序员只能通过“引用”去访问堆内存当中对象内部的实例变量。
2、访问实例变量的语法格式:
读取数据:引用.变量名
修改数据:引用.变量名 = 值int StuNo=s.no;
局部变量在栈内存中存储
成员变量中的实例变量在堆内存的java对象内部存储
实例变量是一个对象一份,100个对象有100份
1 | |
“实例”相关的数据表示:这个访问的时候必须有对象的参与。这种数据就是实例相关的数据。
重点: 实例变量必须先创建对象,通过引用的方式访问,不能直接使用类名.的方式访问
3、内存分析
1、JVM ( Java虚拟机)主要包括三块内存空间.分别是:栈内存、堆内存、方法区内存。
2、堆内存和方法区内存各有1个。一个线程一个栈内存。
3、方法调用的时候,该方法所需要的内存空间在栈内存中分配,称为压栈。方法执行结束之后,该方法所属的内存空间释放,称为弹栈。
4、栈中主要存储的是方法体当中的局部变量。
5、方法的代码片段以及整个类的代码片段都被存储到方法区内存当中,在类加载的时候这些代码片段会载入。
6、在程序执行过程中使用new运算符创建的java对象.存储在堆内存当中。对象内部有实例变量,所以实例变量存储在堆内存当中。
7、变量分类:
- 局部变量[方法体中声明]
- 成员变量[方法体外声明]
实例变量[前边修饰符没有static]
静态变量[前边修饰符中有static]
8、静态变量存储在方法区内存当中。[先背会]
9、三块内存当中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收器主要针对的是堆内存。
10、垃圾回收器[自动垃圾回收机制、GC机制]什么时候会考虑将某个java对象的内存回收呢?
- 当堆内存当中的java对象成为垃圾数据的时候.会被垃圾回收器回收。
11、什么时候堆内存中的java对象会变成垃圾呢?
- 没有更多的引用指向它的时候。这个对象无法被访问,因为访问对象只能通过引用的方式访问。
3、面向对象的封装性、static
1、封装的好处:
- 1、封装之后,对于那个事物来说,看不到这个事物比较复杂的一面,只能看到该事物简单的一面。复杂性封装,对外提供简单的操作入口。照相机就是一个很好的案例,照相机的实现原理比较复杂,但是对于使用照相机的人来说,操作起来是非常方便的。还有像电视机也是封装的,电视机内存实现非常复杂,但是对于使用者来说不需要关心内部的实现原理,只需要会操作遥控器即可。
- 2、封装之后会形成真正的“对象”,真正的“独立体”。
- 3、封装就意味着以后的程序可以重复使用。并且这个事物应该适应性比较强,在任何场合都可以使用。【可重用性】
- 4、封装之后,对于事物本身,提高了安全性【安全级别高】
2、封装的步骤:
1、所有属性私有化,使用private关键字进行修饰,private表示私有的,修饰的所有数据只能在本类中访问
2、对外提供简单的操作入口,也就是说外部程序要想访问age属性,必须通过这些简单的入口进行访问:
- 对外提供两个公开的方法,分别是set方法和get方法
- 想修改age属性,调用set方法
- 想读取age属性,调用get方法
3、set方法的命名规范:public void set + 属性名首字母大写(形参){ };
1 | |
4、get方法的命名规范:public 返回值类型 get + 属性名首字母大写(形参){ };
1 | |
5、需要背会的:
- setter和getter方法没有static关键字
- 有static关键字修饰方法怎么调用?
类名.方法名(实参); - 没有static关键字修饰方法怎么调用?
引用.方法名(实参);
例:
1 | |
4、java构造方法、构造器、对象与引用的概念、参数传递
1、关于java类中中的构造方法:
1、构造方法又被称为构造函数/构造器/Constructor
2、构造方法语法结构:[修饰符列表] 构造方法名(形式参数列表){ 构造方法体; }
3、回顾普通方法的语法结构:[修饰符列表] 返回值类型 方法名(形式参数列表){ 方法体; }
4、对于构造方法来说,“返回值类型”不需要指定,并且也不能写void。只要写上void,那么这个方法就成为普通方法了。
5、对于构造方法来说,构造方法的方法名必须与类名保持一致。
6、构造方法的作用?
- 构造方法存在的意义是,通过构造方法的调用,可以创建对象。
7、构造方法应该怎么调用?
- 普通方法是这样调用的:方法修饰符中有static的时候:
类名.方法名(实参列表)、方法修饰符列表中没有static的时候:引用.方法名(实参列表) new 构造方法名(实参列表)
8、构造方法调用执行之后,有返回值吗?
- 每一个构造方法实际上执行结束之后都有返回值,但是这个“return 值;”这样的语句不需要写。构造方法结束的时候java程序自动返回值。
- 并且返回值类型是构造方法所在类的类型。由于构造方法的返回值类型就是类本身,所以返回值类型不需要编写。
9、注释和取消注释:Ctrl + /、 多行注释:ctrl + shift +/\
10、当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数的构造方法,这个构造方法被称为缺省构造器。
11、当一个类显示的将构造方法定义出来了,那么系统则不再默认为这个类提供缺省构造器。建议开发中手动的为当前类提供无参数的构造方法。因为无参数的构造方法太常用了。
2、构造方法的作用
- 1、创建对象
- 2、创建对象的同时,初始化实例变量的内存空间。
成员变量之实例变量,属于队象级别的变量,这种变量必须先有对象才能有实例变量。
实例变量没有手动赋值的时候,系统默认赋值,那么这个系统默认赋值是在什么时候完成的呢?是在类加载的时候么?
- 不是的,因为类加载的时候只加载了代码片段,还没来得及创建对象。所以此时实例变量并没有初始化。
- 实际上,实例变量的内存空间是在构造方法执行过程当中完成开辟的,完成初始化的。
- 系统在默认赋值的时候,也是在构造方法执行过程当中完成的赋值。
3、对象与引用的概念
- 对象:目前在使用new运算符在堆内存中开辟的内存空间称为对象。
- 引用:是一个变量,不一定是局部变量,还可能是成员变量。引用保存了内存地址,指向了堆内存当中的对象。
- 所有访问实例相关的数据,都需要通过
“引用.”的方式访问,因为只有通过引用才能找到对象。 - 只有一个空的引用,访问对象的实例相关的数据会出现空指针异常。
4、参数传递
主要研究和学习的是方法在调用的时候,涉及到参数传递的问题,到底是怎样传递数据的呢?
- 值传递
int i = 10;
int j = i ; i传递给j,实际上只是将i变量中保存的10传递给了j,j实际上是一个全新的内存空间
User u = 0x1234;
User u2 = u;
u传递给u2,实际上是将0x1234这个值赋给u2了,u和u2实际上是两个不同的局部变量,但是他们这两个变量指向堆内存中的同一个java对象。
java语言当中方法调用的时候涉及到的参数传递的问题
参数传递实际上传递的是变量中保存的具体值
例一:
1 | |
编译结果:
例二:
1 | |
编译结果:
最终结论:
方法调用的时候,涉及到参数问题,传递的时候,java只遵循一种语法机制,就是将变量中保存的“值”传递过去了,只不过有时候这个值是一个字面值10,有的时候是另一个java对象的内存地址0x1234。
5、抽象类
抽象类为什么无法实例化,无法创建对象?
抽象类是:类和类之间有共同特征,将这些具有共同特征的类再进一步形成了抽象类。由于类本身是不存的,所以抽象类无法创建对象
1、什么是抽象类
类和类之间有共同特征,将这些具有共同特征的类再进一步形成了抽象类。由于类本身是不存的,所以抽象类无法创建对象【无法实例化】
2、抽象类属于什么类型?
抽象类也属于引用数据类型
3、抽象类怎么定义?
语法:
1 | |
4、抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。
5、final与abstract不能联合使用,这两个关键字是对立的
6、抽象类的子类可以是抽象类
7、抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的。
8、抽象类关联到一个概念:抽象方法。
抽象方法表示没有实现的方法,没有方法体的方法。
例如:public abstract void doSome(); 没有花括号
抽象方法特点是:
- 特点1:没有方法体,以分号结尾。
- 特点2:前面修饰符列表中有abstract关键字
9、抽象类中不一定有抽象方法,抽象方法必须在抽象类中。
重要结论:【必须背会】
一个非抽象的类继承抽象类,必须将抽象类中的抽象方法实现了。
这是java语法上强行规定的,必须的,否则编译器报错。
抽象类则不需要。
这里的覆盖或者重写,也可以叫做实现。(对抽象)
面向抽象编程
面向抽象编程,不要面向具体编程,降低程序的耦合度,提高程序的扩展力
这种编程思想符合OCP原则。
6、接口
1、接口
- 接口也是一种引用数据类型。
- 接口是完全抽象的。(抽象类是半抽象。)或者也可以说接口是特殊的抽象类。
1、接口的语法
1 | |
2、接口支持多继承,一个接口可以继承多个接口
1 | |
3、接口中只包含两部分内容
- 常量(不能修改)
- 抽象方法
接口没有其它内容,只有以上两部分。
4、接口中的所有元素都是public修饰的
5、接口中的抽象方法定义时:public abstract 修饰符可以省略
6、接口中的方法都是抽象方法,所以接口中的方法不能有结构体和方法体
7、接口中的常量的public 、static、final可以省略。
8、类实现接口实现方法
基础语法: 类和类之间叫做继承,类和接口之间叫做实现。
语法结构:
- 继承使用
extends关键字完成。 - 实现使用
implements关键字完成。
1 | |
9、当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(重点)
10、面向接口的编程
1 | |
11、一个类可以实现多个接口
这种机制弥补了java中的一个缺陷:java中的类和类只支持单继承。实际上单继承是为了简单而出现的,现实世界中存在多继承,java中的接口弥补了单继承带来的缺陷。
12、继承和实现同时存在(extends和implements)
extends 关键字在前 ,implements关键字在后。
1 | |
13、使用接口写代码时,可以使用多态(父类型引用指向子类型对象)
2、接口在开发中的作用(解耦合)
注意:接口在开发中的作用,类似于多态在开发中的作用。
多态:面向抽象编程,不要面向具体编程。降低程序的耦合度,提高程序的扩展力。
接口是不是完全的?
是的,而我们以后正好要求面向抽象编程。
面向抽象编程这句话以后可以修改为:面向接口编程。有了接口就有了可插拨。可插拔表示扩展力很强。不是焊接死的。
面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。
接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度)
接口可以解耦合,解开的是调用者和实现者的耦合,
任何接口都有调用者和实现者。
- 调用者面向接口调用。
- 实现者面向接口编写实现。
7、接口和抽象类的区别
仅语法上的区别
抽象类是半抽象的
接口是完全抽象的抽象类中有构造方法
接口中没有构造方法接口和接口之间支持多继承
类和类之间只能单继承一个类可以同时实现多个接口
一个抽象类只能继承一个类(单继承)接口中只允许出现常量和抽象方法。
接口一般都是对“行为”的抽象
8、Java中类和类之间的关系
一、继承关系
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。 在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
二、实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。 在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。
三、依赖关系
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。 比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。
四、关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。 在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记。
五、聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。 比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,聚合关系以空心菱形加实线箭头表示。
六、组合关系
组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束, 比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,组合关系以实心菱形加实线箭头表示。
9、is-a、is-like-a、has-a
1、has a
关联关系:聚合
凡是能够使用 has a 来描述的,统一以属性的方式存在
例如 Customer has a FoodMenu(顾客有一个食物菜单)
2、is a
继承关系:继承
凡是满足is a的表达式都可以设置为继承
例如 Cat is a Animal(猫是一种动物)
3、is like a
实现关系:接口
满足 is like a 的表达式也是一种继承
实现关系通常是:类实现接口
但是在继承中,仅覆盖了父类方法即为Is-a;若在覆盖父类方法基础上有新增方法,则为Is-like-a。
例子:
1.假设你确定两件对象之间是is-a的关系,那么此时你应该使用继承。比方菱形、圆形和方形都是形状的一种。那么他们都应该从形状类继承。
2.假设你确定两件对象之间是has-a的关系,那么此时你应该使用聚合。比方电脑是由显示器、CPU、硬盘等组成的。那么你应该把显示器、CPU、硬盘这些类聚合成电脑类。
3.假设你确定两件对象之间是like-a的关系,那么此时你应该使用组合。比方空调继承于制冷机,但它同一时候有加热功能。那么你应该把让空调继承制冷机类,并实现加热接口
10、访问控制权限、包和import
1、关于java语言当中的包机制:
- 1、包又称为package, java中 引入package这种语法机制主要是为了方便程序的管理。
不同功能的类被分门别类放到不同的软件包当中,查找比较方便,管理比较方便,易维护。 - 2、怎么定义package呢?
在java源程序的第一行上编写package语句。
package只能编写一 个语句.
语法结构:package 包名; - 3、包名的命名规范:
公司域名倒序+项目名+模块名+功能名;
采用这种方式重名的几率较低。因为公司域名具有全球唯一性。
例如:
com.bjpowernode . oa.user . service;
org . apache. tomcat.core ; - 4、包名要求全部小写,包名也是标识符,必须遵守标识符的命名规则
- 5、一个包对应的是一个目录
- 6、使用了package机制之后,应该怎样编译?怎么运行呢?
使用package机制后,类名不在是Test ,而是包名.类名
1 | |
2、import
1、import 语句用来完成导入其他类,同一个包下的类不需要导入,不在同一个包下需要手动导入。
2、import语法格式:import 类名;import 包名.* ;
1 | |
/3、java.lang.*不需要手动引入,系统自动引入
lang:language语言包,是java语言的核心类,不需要手动引入。
1 | |
4、什么时候需要import?
- 不是java.lang包下,并且不在同一个包下的时候,需要使用import进行引入
3、访问控制权限
访问控制权限修饰符:
1、访问控制权限修饰符来控制元素的访问范围
2、访问控制权限修饰符包括:
- public: 表示公开的,在任何位置都可以访问
- protected: 同包,子类
- 缺省default(系统默认状态): 同包
- private: 表示私有的,只能在本类中访问
3、访问控制权限修饰符可以修饰类、变量、方法…
4、当某个数据只希望子类使用,使用protected进行修饰
5、修饰符的范围:
private < 缺省 < protected < public
6、类只能采用public和缺省的修饰符进行修饰【内部类除外】
六、关键字
1、this关键字
关于java语言当中的this关键字
- 1 this是一个关键字,翻译为:这个
- 2 this是一个引用,this是一个变量,this变量中保存了内存地址指向了自身,this存储在JVM堆内存java对象内部。
- 3 创建100个java对象,每一个对象都有this,也就是说有100个不同的this
- 4 this可以出现在“实例方法”中,this指向当前正在执行这个动作的对象。(this代表当前对象)
- 5 this在大多数情况下都是可以省略不写的
- 6 this不能使用带有static的方法中
1 | |
编译结果:
实例方法必须有对象的存在
例:
1 | |
最终结论:
- 在带有static的方法当中不能直接访问实例变量和实例方法。
- 因为实例变量和实例方法都需要对象的存在。
- 而static的方法当中是没有this的,也就是说当前对象不存在。
- 自然也是无法访问当前对象的实例变量和实例方法。
this.什么时候不能省略?
用来区分局部变量和实例变量的时候,“this.”不能省略。
1 | |
this可以使用在哪里?
- 1 实例方法中,代表当前对象【语法格式:
this.】 - 2 构造方法中,通过当前的构造方法调用其他的构造方法【语法格式:
this(实参);】
重点记忆:this()这种语法只能出现在构造函数第一行。
什么时候程序在运行的时候出现空指针异常呢?
- 空引用访问实例相关的数据,因为实例相关的数据就是对象相关的数据,这些数据在访问的时候必须有对象的参与,当空引用的时候,对象不存在,访问这些实例数据一定会出现空指针异常。
实例相关的数据包括:
- 实例变量【对象需要存在】
- 实例方法【对象需要存在】
2、static关键字
- 1 static:静态的
- 2 static修饰的方法是静态方法
- 3 static修饰的变量是静态变量
- 4 所有static修饰的元素都称为静态的,都可以使用
类名.的方式访问,当然也可以采用引用.【但不建议】 - 5 static修饰的所有元素都是类级别的特征,和具体的对象无关
1 | |
什么时候成员变量声明为实例变量呢 ?
- 所有对象都有这个属性,但是这个属性的值会随着这个对象的变化而变化【不同对象的这个属性具体的值不同】
什么时候成员变量声明为静态变量呢 ?
- 所有对象都有这个属性,并且所有对象这个属性的值是一样的,建议定义为静态变量,节省内存的开销。
静态变量在类加载的时候初始化,内存在方法区中开辟。访问的时候不需要创建对象,直接使用类名.静态变量名的方式访问。
所有静态的数据都是可以采用类名.,也可以采用引用.,但是建议采用类名.的方式访问。
采用引用.的方式访问的时候,即使引用的是null,也不会出现空指针异常,因为访问静态的数据不需要对象的存在。
可以使用static关键字来定义“静态代码块”:
1、语法格式:
1 | |
2、静态代码块在类加载时执行,并且只执行一次。
3、静态代码块在一个类中可以编写多个,并且遵循自上而下的顺序依次执行。·
4、静态代码块的作用是什么?怎么用?用在哪?什么时候用?
- 这当然和具体的需求有关,例如项目中要求在类加载的时候执行代码完成日志的记录。那么这段记录日志的代码就可以编写到静态代码当中,完成日志记录
- 静态代码块是java为程序员准备的一个特殊的时刻,这个特殊的时刻被称为类加载时刻。若希望在此刻执行一段特殊的程序,这段程序可以直接放到静态代码当中。
5、通常在静态代码块当中完成预备工作,先完成数据的准备工作,例如:初始化连接池,解析XML配置文件…
方法什么时候定义为静态的?
方法描述的是动作,当所有的对象执行这个动作的时候,最终产生影响是一样的。那么这个动作已经不再属于某一个对象动作了,可以将这个动作提升为类级别的动作,模板级别的动作。
静态方法中无法直接访问实例变量和实例方法。
大多数方法都定义为实例方法,一般一个行为或者一个动作在发生的时候,都需要对象的参与。但是也有例外,例如:大多数“工具类”中的方法都是静态方法,因为工具就是方便编程,为了方便方法的调用,自然不需要new对象是最好的。
3、final关键字
1、final是一个关键字,表示最终的,不可变的
2、final修饰的类无法被继承
3、final修饰的方法无法被覆盖
4、final修饰的变量一旦被赋值之后,不可重新赋值【不可二次赋值】
5、final修饰的实例变量,必须手动赋值,不能采用系统默认值 final变量必须手动赋值,不能采用系统默认值
1 | |
6、final修饰的引用,一旦指向某个对象之后,不能在指向其他对象,那么被指向的对象无法被垃圾回收器回收。
final修饰的引用虽然指向某个对象之后不能指向其他对象,但是所指向的对象内部的内存是可以被修改的。
1 | |
7、final修饰的实例变量,一般和static联合使用,被称为变量。
final修饰的实例变量是不可变的,这种变量一般和static联合使用,被称为常量。
常量定义的语法格式【常量名全部大写,每个单词之间使用下划线连接】public static final 类型 常量名 = 值
4、super关键字
super的使用
super.属性名:【访问父类的属性】super.方法名(实参):【访问父类的方法】super(实参):【调用父类的构造方法】
super应用
1、super是一个关键字,全部小写
2、
- super能出现在实例方法中。
- super的语法是:
super.或super() - super不能使用在静态方法中
super.大部分情况下是可以省略的super.什么时候不可以省略?
父类和子类中有同名属性,或者说有同样的方法,想在子类中访问父类的,super.不可以省略。super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。
3、super():
表示通过子类的构造方法调用父类的构造方法。
模拟现实世界中的这种场景:要想有儿子,需要先有父亲
4、当一个构造方法第一行:
既没有this()又没有super()的话,默认会有一个super();表示通过当前子类的构造方法调用父类的无参数构造方法。所以必须保证父类的无参数构造方法是存在的。
5、注意:this()和super()不能共存,他们都是只能出现在构造方法的第一行。
6、无论怎样,父类的构造方法是一定会执行的。
super.什么时候不能省略?
父中有,子中又有,如果想在子中访问“父的特征”,super.不能省略。
this.name:当前对象的name属性super.name:当前对象的父类型特征中的name属性
七、继承、多态
1、继承、方法覆盖
1、继承
关于java语言当中的继承:
- 1 继承是面向的对象三大特征之一,三大特征是封装、继承、多态。
- 2 继承“基本”的作用是:代码复用。但是继承最“重要”的作用是:有了继承才有了以后的“方法覆盖”和“多态机制”
- 3 继承语法格式:
1 | |
- 4 java语言当中的继承只支持单继承,一个类不同同时继承很多类,只能继承一个类。【在C++中支持多继承】。
- 5 关于继承中的一些术语:
B类继承A类,其中:
A类称为:父类、基类、超类、superclass
B类称为:子类、派生类、subclass - 6 在Java语言当中子类继承父类都继承那些数据?
私有的不支持继承
构造方法不支持继承
其他数据都可以被继承 - 7 虽然java语言当中只支持单继承,但是一个类也可以间接继承其他类,例如:
1 | |
- 8 java语言中假设一个类没有显示的继承任何类,该类默认继承JavaSE库中提供的java.lang.Object类。java语言中任何一个类中都有Object类的特征。
2、方法覆盖
1 方法覆盖又称方法重写(override[官方]、overwrite)
2 什么时候使用方法覆盖?
当父类中的方法已经无法满足当前子类的业务需求,子类有必要将父类中继承过来的方法进行重新编写,这个编写过程称为方法重写/方法覆盖。
3 什么条件满足之后会发生重写呢?
代码满足什么条件之后,就构成方法的覆盖呢?
- 方法重写发生在具有继承关系的父子类之间
- 返回值类型相同,方法名相同,形参列表相同
- 访问权限不能更低,可以更高。
- 抛出异常不能更多,可以更少
建议方法重写的时候尽量复制粘贴,容易导致没有产生覆盖。
注意
- 私有方法不能继承,所以不能覆盖
- 构造方法不能继承,所以不能覆盖
- 静态方法不存在覆盖
- 覆盖只针对方法,不谈属性
2、多态
1、基础语法
向上转型(upcasting)
子类型–>父类型
又被称为:自动类型转换
向下转型(downcasting)
父类型–>子类型
又被称为:强制类型转换。【需要加强制类型转换符】
无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
没有继承关系,程序是无法编译通过的。
1 | |
- java程序永远都分为编译阶段和运行阶段
- 先分析编译阶段,在分析运行阶段,编译无法通过,根本是无法运行的。
- 编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有move()方法,所以编译通过了。这个过程我们称为静态绑定,编译阶段绑定。只有静态绑定成功了才有后续的运行。
- 在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定。
- 无论是Cat类有没有重写move的方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实对象就是Cat对象
- 父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这种机制可以称为一种多态语法机制。
1 | |
怎么解决 ClassCastException异常呢?
使用instanceof运算符可以避免出现以上异常。
instanceof运算符怎么使用?
语法格式:引用 instanceof 数据类型名
以上运算符的执行结果类型是布尔类型,结果可能是true/false
关于运算结果true/false:
假设:(a instanceof Animal)
- true 表示:a这个引用指向的对象是一个Animal类型
- false表示:a这个引用指向的对象不是一个Animal类型。
Java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免ClassCastException异常的发生。 这是一种好习惯。
1 | |
2、多态在实际开发中的作用
- 降低耦合度【解耦合】,提高程序的扩展力【软件开发的一个很重要的目标】
- 能使用多态尽量使用多态
- 父类型引用指向子类型对象
八、数组
1、数组Array
1、数组相关基础知识
- 1、Java语言中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是Object。
- 2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)。
数组:字面意思是“一组数据”。 - 3、数组当中可以存储基本数据类型的数据,也可以存储“引用数据类型” 的数据。
- 4、数组因为是引用类型,所以数组对象是堆内存当中的。(数组是存储在堆当中的)。
- 5、数组当中如果存储的是“java对象” 的话,实际上存储的是java对象的“引用(内存地址)”,数组不能直接存储java对象。
- 6、数组的分类:一维数组、二维数组、三维数组、多维数组…(一维数组较多,二维数组偶尔使用)。
- 7、数组一旦创建,在java规定,长度不可变。(数组长度不可变)。
- 8、所有数组对象都有length属性(java自带的),用来获取数组中元素的个数。
- 9、java中的数组要求数组中的元素类型统一。
- 10、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续。这是数组存储元素的特点。数组实际上是一种简单的数据结构。
- 11、所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象的内存地址)
- 12、数组中的每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标:length - 1
下标非常重要,因为我们对数组中的元素进行“存取”的时候都需要下标;来进行。
2、数组的优缺点
优点:
查询、检索某个下标上的元素时效率极高。可以说查询效率最高的一个数据结构。
为什么检索效率高?
- 第一:每一个元素的内存地址在空间存储上是连续的。
- 第二:每一个元素类型相同,所以占用空间大小一样。
- 第三:如果知道一个元素内存地址,知道每一个元素占用空间大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上的元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。
缺点:
- 第一:由于为了保证数组中每个元素的内存地址连续,所以在数组上随即删除或者增加元素的时候效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
- 第二:数组不能存储大量数据,因为很难在空间上找到一块特别大的连续的内存
注意:对于数组中最后一个元素的增删是没有效率影响的
2、一维数组
1、怎样声明一个一维数组
语法格式:int[] array;double[] array;boolean[] array;String[] array;Object[] array;
2、怎样初始化一个一维数组呢?
包括两种方式:静态初始化一维数组、动态初始化一维数组
静态初始化一维数组:int[] array = {100,200,300};、String[] str = {"as","cs" ,"ds"};
也可以使用 int array[] = {1,2,3},这是C++风格,不建议在java中使用
动态初始化一维数组:int[] array = new int [5]; 这里的5表示数组元素个数,初始化一个5个长度的int类型数组,每个元素默认值为0
String[] names = new String[6]; 初始化6个长度的String类型数组,每个元素默认值null
3、对一维数组中的元素访问
1 | |
输出结果:
4、 一维数组的遍历
遍历写法:
提示:下标越界会出现异常:
1 | |
结果:
5、静态存储Object类
1 | |
6、动态初始化一维数组
1 | |
输出结果:
7、什么时候采用静态初始化方法/动态初始化方法?
当你创建数组的时候,确定数组中存储哪些具体的元素的时候,采用静态初始化。
当你创建数组的时候,不确定数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。
8、方法的参数为数组
1 | |
9、数组中存储引用数据类型
对于数组来说,实际上只能存储java对象的“内存地址”。数组中存储的每个元素都是“引用”
提示:不能存放别的引用数据类型
如果继承该引用数据类型的数据类型可以使用该数组
1 | |
运行结果:
10、输出数组的方法
错误示范:System.out.println(array); //这样输出的是数组的首地址,而不能打印出数组数据
正确写法:int[] array= {1,2,3,4,5,6};
方式一:for循环
1 | |
输出为:(自动换行格式的)
方式二:for each
1 | |
输出格式同方式一。
方式三:Arrays类中的toString方法(注意,是Arrays不是Array,Arrays类位于java.util包下)
需要导入import java.util.Arrays包
1 | |
11、数组扩容
java中对数组的扩容是:
先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数据当中。
结论:数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。可以在创建数组对象的时候预估计以下多长合适,最好预估准确,这样可以减少数组的扩容次数,提高效率。
第一种方法,建立一个新的数组,通过for循环来进行拷贝扩容
1 | |
第二种方法:固定的写法。System.Arrays.copy(a,0,b,0,a.length);
1 | |
方法三:利用函数方法直接扩容
1 | |
12、数组拷贝
数组拷贝的方法:System.arraycopy( Object src,int srcPos,Object dest, int destPos,int length)
Object src:源数组
int srcPos:源数组起点下标
Object dest:目标数组
int destPos:目标数组起点下标
int length:拷贝的源数组的长度
数组中存储的元素是引用也可以拷贝。且拷贝的是对象的内存地址
1 | |
运行结果:
3、 排序
1、冒泡排序算法
思想:
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2、对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3、针对所有的元素重复以上的步骤,除了最后一个。
4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
举例:
(1)要排序数组:[10,1,35,61,89,36,55]
(2)第一趟排序:
第一次排序:10和1比较,10大于1,交换位置 [1,10,35,61,89,36,55]
第二趟排序:10和35比较,10小于35,不交换位置 [1,10,35,61,89,36,55]
第三趟排序:35和61比较,35小于61,不交换位置 [1,10,35,61,89,36,55]
第四趟排序:61和89比较,61小于89,不交换位置 [1,10,35,61,89,36,55]
第五趟排序:89和36比较,89大于36,交换位置 [1,10,35,61,36,89,55]
第六趟排序:89和55比较,89大于55,交换位置 [1,10,35,61,36,55,89]
第一趟总共进行了六次比较,排序结果:[1,10,35,61,36,55,89]
(3)第二趟排序:
第一次排序:1和10比较,1小于10,不交换位置 [1,10,35,61,36,55,89]
第二次排序:10和35比较,10小于35,不交换位置 [1,10,35,61,36,55,89]
第三次排序:35和61比较,35小于61,不交换位置 [1,10,35,61,36,55,89]
第四次排序:61和36比较,61大于36,交换位置 [1,10,35,36,61,55,89]
第五次排序:61和55比较,61大于55,交换位置 [1,10,35,36,55,61,89]
第二趟总共进行了5次比较,排序结果:[1,10,35,36,55,61,89]
(4)第三趟排序:
1和10比较,1小于10,不交换位置 [1,10,35,36,55,61,89]
第二次排序:10和35比较,10小于35,不交换位置 [ 1,10,35,36,55,61,89]
第三次排序:35和36比较,35小于36,不交换位置 [1,10,35,36,55,61,89]
第四次排序:36和61比较,36小于61,不交换位置 [1,10,35,36,55,61,89]
第三趟总共进行了4次比较,排序结果:[1,10,35,36,55,61,89]
到目前位置已经为有序的情形了。
平均时间复杂度:O(n²)
最好情况:O(n)
最坏情况:O(n²)
空间复杂度:O(1)
1 | |
2、选择排序
思想:
简单选择排序采用最简单的选择方式,从头至尾顺序扫描序列找出最小的一个关键字,和第一个关键字交换,接着从剩下的关键字中继续这种选择和交换,最终使序列有序。
图解:
示例代码:
1 | |
4、查找
1、二分查找法
思想:
假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。
示例:
假如有一组数为3,12,24,36,55,68,75,88要查给定的值24.可设三个变量front,mid,end分别指向数据的上界,中间和下界,mid=(front+end)/2.
1、开始令front=0(指向3),end=7(指向88),则mid=3(指向36)。因为mid>x,故应在前半段中查找。
2、令新的end=mid-1=2,而front=0不变,则新的mid=1。此时x>mid,故确定应在后半段中查找。
3、令新的front=mid+1=2,而end=2不变,则新的mid=2,此时a[mid]=x,查找成功。
如果要查找的数不是数列中的数,例如x=25,当第三次判断时,x>a[mid],按以上规律,令front=mid+1,即front=3,出现front>end的情况,表示查找不成功。
代码:
1 | |
5、一维数组的应用
一维数组模拟栈数据结构
要求:
- 1、这个栈可以存储java中任何引用数据类型。
- 2、在栈中提供push方法模拟压栈。(栈满会有提示信息)
- 3、在栈中提供pop方法模拟弹栈。(栈空了也要有提示信息)
- 4、编写测试程序,new栈对象,调用push、pop方法来模拟压栈弹栈动作。
MyStack类:
1 | |
StackTest类:
1 | |
输出结果:
6、二维数组的应用
为某个酒店编写程序:酒店管理系统,模拟订房、退房、打印所有房间状态等功能
要求:
- 1、该系统的用户是:酒店前台。
- 2、酒店中所有房间使用一个二维数组来模拟:
Room[][] rooms; - 3、酒店中的每一个房间应该是一个java对象:Room。
- 4、每一个房间Room应该有:房间编号、房间类型属性、房间是否空闲
- 5、系统应该对外提供哪些功能:
可以预定房间:用户输入房间编号,订房。
可以退房:用户输入房间编号,退房。
可以查看所有房间的状态:用户输入某个指令应该可以查看所有房间状态。
HotelSystem: 酒店前台系统
1 | |
Hotel类: 酒店对象,包含二维数组
1 | |
Room类: 房间信息
1 | |
运行截图:
九、异常处理
1、异常的基本概念
1、什么是异常, java提供异常处理机制有什么用?
异常就是程序执行过程中的不正确情况。
以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
java语言是很完善的语言,提供了异常的处理方式,以下程序执行过程中出现了不正常情况,
java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对
程序进行修改,让程序更加的健壮。
1 | |

观察到异常信息之后,对程序进行修改,更加健壮
1 | |
2、上述程序出现了异常信息:
1 | |
这个信息被我们称为:异常信息。这个信息是JVM打印的
2、java中异常以类和对象形式存在
1、异常在Java中以类的形式存在,每一个异常类都可以创建异常对象
1 | |

2、异常对应的现实是怎样的?
火灾(异常类):
2008年8月8日,小明家着火了(异常对象)
2008年8月9日,小刚家着火了(异常对象)
2008年9月8日,小红家着火了(异常对象)
类是模板,对象是实际存在的个体
2、异常的继承结构(UML图)
1、Object下有Throwable(可抛出的)
不管是错误还是异常都是可以抛出的。
2、Throwable下有两个分支:Error(不可处理的,直接退出JVM)和Exception(可处理的)
所有的错误只要发生,Java程序只有一个结果,那就是终止程序的执行。退出JVM,错误是不能处理的。
3、Exception下有两个分支:
- Exception的直接子类(Exceptionsubclass):编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常)
- RuntimeException: 运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管)

3、编译时异常和运行时异常区别
1、编译时异常因为什么而得名
编译时异常和运行时异常,都发生在运行阶段。编译阶段异常是不会发生的。编译时异常因为什么而得名?
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。
所有异常都是运行阶段发生的。因为只有程序运行阶段才可以new对象。
因为异常的发生都是new异常对象。
2、编译时异常和运行时异常区别
- 编译时异常一般发生的概率比较高。
对于一些发生概率较高的异常,需要在运行之前对其进行预处理。 - 运行时异常一般发生的概率比较低。
假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常,所有的异常都需要在编写程序阶段对其进行预处理,将会是怎样的效果呢?
如果代码是这样的话,程序肯定是绝对安全的。但是程序员编写程序太累,代码到处都是处理异常的代码
3、编译时异常还有其他名字
- 受控异常 (CheckException)
- 受检异常
4、运行时异常还有其他名字
- 未受检异常(UnCheckException)
- 非受控异常
5、所有异常都发生在运行阶段的
4、Java两种异常处理的方式及原理
1、异常上抛
在方法声明的位置上,使用throws关键字,抛给上一级
谁调用我,我就抛给谁,抛给上一级。
2、异常捕捉
使用try..catch语句进行异常的捕捉
这件事发生了,谁也不知,因为我给抓住了。
举例:
我是某集团的一个销售员,因为我的失误,导致公司损失了1000块钱。”损失1000块”这可以看做一个异常发生了。我有两种处理方式。
- 第一种方式:我把这件事告诉我的领导。【异常上抛】
- 第二种方式:我把自己的掏腰包把这个钱补上。【异常的捕捉】
同样的异常发生之后,如果选择上抛,抛给了调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
注意:
Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常的发生,只有只有一个结果。终止java程序的执行。
1、运行时异常编写程序时可以不处理
1 | |
2、方法声明上使用throws
以下代码报错的原因是什么?
因为doSome()方法声明位置上使用了:throws ClassNotFoundException
而ClassNotFoundException是编译时异常。必须编写代码时处理,没有处理编译器报错。
1 | |

3、异常处理的具体方式
第一种处理方式:在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者
上抛类似于推卸责任。(继续把异常传递给调用者)
这种处理异常的态度:上报
1 | |
第二种方式:try ... catch进行异常捕捉
捕捉等于把异常拦下了,异常真正的解决了。(调用者不知道的)
1 | |
一般不建议在main方法上使用throws,因为这个异常如果真正发生了,一定会抛给JVM。JVM只有终止。一般采用try...catch进行捕捉。
4、异常捕捉和上报的联合使用
1 | |

编译报错的原因:
- 第一:这里调用了一个构造方法:
FileInputStream(String name) - 第二:这个构造方法的声明位置上有:throws FileNotFoundException
- 第三:通过类的继承结构看到:FileNotFoundException父类是IOException,IOException的父类是Exception。
最终得知,FileNotFoundException是编译时异常。编译时异常要求程序员编写阶段必须对它进行处理,不处理编译器就报错
进行异常处理后
1 | |
上抛异常也可以 throws Exception
注意:只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行。
5、上报和捕捉怎么选择?
- 如果希望调用者来处理,选择throws上报。
- 其他情况可以使用捕捉的方式
5、try…catch语句
1、catch后面的小括号的类型可以是具体的异常类型,也可以是该异常类的父类型
1 | |

也可以写为多态:
1 | |
或
1 | |
2、catch可以写多个
建议catch的时候,精确的一个一个处理。这样有利于程序的调试
1 | |
3、catch后面异常中可以加 | 号(JDK8新特性,7不支持)
1 | |
4、catch写多个的时候,从上到下,必须遵守从小到大
6、获取异常对象的方法:getMessage和printStackTrace()
1、getMessage
获取异常简单的描述信息。
语法格式:String msg = exception.getMessage();
2、printStackTrace()
打印异常追踪的堆栈信息,比较适合于程序的调试阶段
语法格式:exception.printStackTrace();
3、两个方法的实例
1 | |

4、查看异常的追踪信息
我们应该怎么看,可以快速的调试程序呢?
异常信息追踪信息,从上往下一行一行看。
但是需要注意的是:SUN写的代码就不用看了。主要问题是出现在自己编写的代码上。
即从运行结果中的at ExceptionText3.m3(ExceptionText3.java:27)这一行看
1 | |

因为27行出现了问题导致23行
23行出问题导致19行
19行出问题导致7行
应该先查看27行的的代码。27行是错误的根源,从根上看问题
防止误导再加一条!!!!:
尽量不要使用e.printStackTrace(),而是使用log打印。
反例:
1 | |
正例:
1 | |
理由:
printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的,通常排查异常日志不太方便。
e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,那么,用户的请求就卡住啦~
7、finally关键字
关于try…catch中的finally子句
1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
- finally子句必须和try一起出现,不能单独编写。
2、finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭
因为finally中的代码比较有保障
即使try语句块中的代码出现异常,finally中的代码也会正常执行
1 | |
fis.close未被注释前:
修改后:
3、try和finally,也可以没有catch
- try不能单独使用。
- try 可以和finally联合使用。
以下代码的执行顺序:
先执行try…
再执行finally
最后执行return(return语句只要执行方法必然结束)
1 | |
运行结果:
4、退出JVM,finally语句不执行
1 | |
运行结果:
5、关于finally的一道面试题
1 | |
最后的结果为 100:
解释: Java语法中有这样一条规则:
- 方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
Java中还有一条语法规则:
- return语句一但执行,整个方法必须结束(亘古不变的语法!)
反编译之后的结果:
1 | |
8、final、finalize和finally的区别
1、final
final是一个关键字。表示最终的,不可变的。
1 | |
2、finalize
finalize()方法JVM的GC垃圾回收器负责调用
当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法。如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法中(留遗嘱)
1 | |
3、finally
finally是一个关键字,和try联合使用,使用在异常处理机制中。
1 | |
9、如何自定义异常及手动抛出异常
1、自定义异常的方法
需要两步:
- 第一步:编写一个类继承Exception或者RunTimeException
- 第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
MyException类
1 | |
main类
1 | |
运行结果:
2、使用的时候需要手动抛出异常(throw)
语法格式:throw + 异常类的对象
10、异常与方法覆盖
在方法覆盖时,重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少(更小)的异常
1 | |
十、反射机制
1、Java之反射机制详解
1、反射机制
反射机制在java.lang.reflect.*包下
反射机制相关的重要的类有
java.lang.Class:代表字节码文件,代表一个类型,代表整个类。java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(局部变量)
2、反射机制有什么用
通过java语言中的反射机制可以操作字节码文件。有点类似于黑客(可以读和修改字节码文件。)通过反射机制可以操作代码片段。(class文件)让程序更加灵活。
3、获取Class文件的三种方式
1、Class.forName()
1 | |
如果只希望一个类的静态代码块执行,其他代码一律不执行,可以使用:Class.forName("完整类名");
这个方法的执行会导致类加载,类加载时,静态代码块执行。
2、getClass()方法
java中任何一个对象都有一个方法:getClass()
1 | |


3、java类型的.class属性
java语言中任何一种类型,包括基本数据类型,它都有.class属性
1 | |
4、通过反射实例化对象
获取.class能干什么?
通过Class的newInstance()方法来实例化对象。
注意:newInstance()方法内部实际上调用了无参数的构造方法,必须保证无参数构造存在才可以。
使用反射机制的方式来创建对象:
1 | |

5、通过读属性文件实例化对象
验证反射机制的灵活性。java代码只写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。非常之灵活(符合OCP开闭原则:对扩展开放,对修改关闭)
classinfo.properties文件:
1 | |
6、资源绑定器
java.util包下提供了一个资源绑定器,便于获取配置文件中的内容。使用以下方法的时候,属性配置文件×××.properties必须放到类路径下
1 | |
7、类加载器
1、什么是加载器
专门负责加载类的命令/工具。ClassLoader
2、JDK中自带的类加载器
- 启动类加载器:
rt.jar - 扩展类加载器:
ext/*.jar - 应用类加载器:
classpath
假如执行以下代码:
1 | |
代码在开始执行以前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找string. class文件,找到就加载,那么是怎么进行加载的呢?
- 首先通过 “启动类加载器” 加载。
注意:启动类加载器专门加载:C: \Program Files\Java\jdk1.8.0_ 101\jre\lib\rt.jar
rt. jar中都是JDK最核心的类库。 - 如果通过 “启动类加载器”加载不到 的时候,会通过 “扩展类加载器” 加载。
注意:扩展类加载器专门加载:C: \Program Files\Java\jdk1.8.0_ 101\jre\lib\ext\*.jar - 如果 “扩展类加载器” 没有加载到,那么会通过 “应用类加载器” 加载。
注意:应用类加载器专门加载:classpath中的类。
3、双亲委派机制
java中为了保证类加载的安全,使用了双亲委派机制。优先从启动类加载器中加载,这个称为 “父”,”父”无法加载到,再从扩展类加载器中加载,这个称为**“母”。双亲委派。*如果都加载不到*,才会考虑从**应用类加载器中加载。直到加载到为止。
8、Field
1、获取Field
反射属性Field,Field翻译为字段,其实就是属性/成员。
4个Field,分别采用了不同的访问控制权限修饰符
1 | |

2、反编译Field
通过反射机制,反编译一个类的属性Field
1 | |
运行结果:
3、通过反射机制访问java对象属性(重点掌握)
给属性赋值set
获取属性的值get
1 | |

9、Method
1、可变长度参数
1 | |
- 1、可变长度参数要求的参数个数是:0~N个。
- 2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个。
1 | |
运行结果:
2、反射Method
1 | |
3、反编译Method
1 | |
4、通过反射机制调用方法(重点掌握)
1 | |
10、Constructor
1)得到某个类所有的构造方法
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
2)得到某一个构造方法
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
实例
1 | |
打印结果:c
11、获取父类和父接口
1 | |

十一、注解
1、java之注解详解
1、注解如何定义?怎么用?
注解,或者说叫做注释类型,英文单词是:Annotation
注解Annotation是一种引用数据类型。编译之后也是生成XXX.class文件。
自定义注解:
1 | |
注解怎么使用,用在什么地方?
- 第一:注解使用时的语法格式是:
@ 注解类型名 - 第二:注解还可以出现在类上、属性上、方法上、变量上等…注解还可以出现在注解类型上。
JDK内置了哪些注释?java.lang包下的注释类型:
- 掌握:
Deprecated,用@Deprecated注释的程序元素。不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。 - 掌握:
Override,表示一个方法声明打算重写超类的另一个方法声明。 - 不用掌握:SuppressWarnings,指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
2、自定义注解
注解以@开头
在idea中new一个自定义注解
自定义注解的依法格式:
【修饰符列表】 @interface 注解名称{…}
1 | |
使用注解:@注解名称 如:@Override
1 | |
2、Override注解
关于JDK lang包下的Override注解
源代码:
1 | |
@Override只能注解方法。@Override这个注解是给编译器参考的,和运行阶段没有关系- 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。
4、元注解
1、什么是元注解?
用来标注”注解类型的”注解”。称为元注解
2、常见的元注解有哪些?
- Target
- Retention
3、关于Target注解:
这是一个元注解,用来标注”注解类型”的注解”
这个Target注解用来标注”被标注的注解”可以出现在哪些位置上@Target (ElementType .METHOD):表示w被标注的注解只能出现在方法上.
4、关于Retention注解:
这是一个元注解,用来标注”注解类型”的”注解”
这个Retention注解用来标注”被标注的注解”最终保存在哪里。
@Retention(RetentionPolicy .SOURCE):表示该注解只被保留在java源文件中。@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以
5、Deprecated注解
表示已过时的。
源码:
1 | |
6、使用反射获取注解信息
需求:创建一个自定义注解,在Person类上及Person的方法上使用注解,在测试类中通过反射获取标注在Person类和方法上的注解信息。
注意:自定义注解必须用@Retention(RetentionPolicy.RUNTIME)标注,这样自定义注解才能被反射机制读取到。
1 | |
测试类:
1 | |
测试结果: