Java学习笔记(尚硅谷宋红康老师)持续更新中.........
java知识学习
- 第一部分:Java基础编程
- 一、Java语言概述
- 1. Java基础图解:
- 2. 软件开发介绍:
- 2.1 软件开发:
- 2.2 人机交互方式:
- 2.3 常用DOS命令:
- 2. 计算机编程语言介绍:
- 2.1 语言分类:
- 3. Java语言概述:
- 3.1 各领域的应用:
- 3.2 主要诞生:
- 4. 运行机制及运行过程:
- 4.1 Java语言的特点:
- 4.2 Java两种核心机制:
- 5. Java语言环境搭建:
- 5.1 JDK、JRE、JVM介绍:
- 5.2 JDk与JRE下载和安装:
- (1)JDK下载:
- (2)JDk安装:
- 5.3 Java环境配置:
- (1)配置步骤:
- 6. 开发体验——helloWorld:
- 6.1 编写、编译和运行:
- 7. 常见问题及解决方法:
- 8. 注释(Comment):
- 8.1 单行和多行注释:
- 8.2 文档注释:
- 9. Java API文档:
- 9.1 文档解释:
- 9.2 API基本组成:
- 10. 编程风格:
- 11. 常用java开发工具:
- 二、Java基本语法:
- 1. 关键字和保留字:
- 1.1 关键字(keyword)定义和特点:
- 1.2 保留字(reserved word):
- 2. 标识符(Identifier):
- 2.1 标识符定义:
- 3. 变量:
- 3.1 变量解释:
- 3.2 变量分类:
- (1)按数据类型分类:
- (2)按声明位置分类:
- 3. 3 变量使用规则:
- (1)基本数据类型:
- (2)引用数据类型:
- 3.4 基本数据类型转换:
- (1)自动类型提升转换:
- 3. 5 强制类型转换:
- 3.6 变量之进制:
- 4. 运算符:
- 4.1 算术运算符:
- 4.2 赋值运算符:
- 4.3 比较(关系)运算符:
- 4.4 逻辑运算符:
- 4.5 位运算符:
- 4.6 三元运算符:
- 5. 程序流程控制:
- 5.1 顺序结构:
- 5.2 分支结构:
- (1)分支语句一:if-else
- (2)键盘获取不同类型值(使用Scanner类)
- (3)随机数产生:
- (4)分支语句二:switch-case
- (5)if-esle和switch-case语句对比:
- 5.3 循坏结构:
- (1)循环结构一:for循环
- (2)循环结构二:while循环
- (3)循环结构三:do-while循环
- (4)嵌套循环:
- (5)特殊关键字使用:break和continue
- 三、数组
- 1. 数组的概述:
- 2. 一维数组的使用:
- 2.1 数组声明:
- 2.2 数组的初始化:
- (1) 动态初始化:
- (2) 静态初始化:
- 2.3 数组元素的应用:
- 2.4 数组元素的默认初始化值:
- 2.5 一维数组内存解析:
- 3. 多维数组使用:
- 3.1 多维数组的介绍:
- 3.2 二维数组初始化:(二维数组:数组中的数组)
- 3.3 二维数组内存解析:
- 4. 数组中常见的算法:
- 4.1 数组元素的赋值:
- 4.2 数组元素查找:
- (1)线性查找:
- (2)二分查找(折半查找):
- 4.3 数组元素排序算法:
- (1)排序算法的介绍:
- 4.4 排序算法的实现:
- (1)交换排序(冒泡排序(掌握)、快速排序(了解)):
- (2)插入排序(直接插入排序、希尔排序):
- (3)选择排序(简单选择排序(掌握)、堆排序):
- (4)归并排序:
- (5)排序算法性能对比:
- (6)排序算法的选择:
- 5. Arrays工具类的使用:
- 6. 数组使用中的常见异常:
- 四、面向对象(上)
- :smile:面向对象三条主线:
- 1. 面向过程与面向对象的简单介绍:
- 1.1 面向过程(POP) 与 面向对象(OOP):
- (1)二者的思想理解:
- 1.2 面向对象的三大特性(后面会介绍):
- 1.3 面向对象思想概述:
- 2. Java语言的基本元素: 类和对象
- 2.1 面向对象的思想概述:
- 2.2 Java类及类的成员:
- 3. 对象的创建(类的实例化)和使用:
- 3.1 对象创建与使用:
- 3.2 类的访问机制:
- 4. 类的成员之一: 属性(field)
- 4.1 变量的分类:成员变量与局部变量
- (1)基本概念:
- (2)二者在初始化值方面的异同:
- (3)成员变量vs局部变量的内存位置:
- (4)对象属性的默认初始化赋值:
- 5. 类的成员之二:方法(method)
- 5.1 方法(method)的解释:
- 5.2 类中方法的声明和使用:
- (1)例如:
- (2)举例:
- (3)方法声明:
- (4)return关键字:
- (5)方法中可以调用当前类的属性和方法:
- 5.3 对象数组的内存解析:
- (1)举例:
- 5.4 匿名对象使用:
- 5.5 理解万事万物皆对象:
- 6. 再谈方法:
- 6.1 方法的重载(overLoad):
- (1)重载的概念 :
- (2)重载的特点:
- (3)重载的细节:
- (4)重载示例:
- 6.2 可变个数形参的方法 :
- (1)背景:
- (2)具体使用:
- 6.3 方法参数的值传递机制(重点):
- (1)变量赋值:
- (2)方法的值传递机制:
- 6.4 递归(recursion)方法:
- 7. 类的成员之三: 构造器(或构造方法、constructor)
- 7.1 构造器的特征:
- 7.2 构造器的作用:
- 7.3 说明:
- 7.4 构造器重载:
- 7.5 总结:属性赋值过程
- 7.6 拓展知识:JavaBean
- 7.7 拓展知识:UML类图
- 8. 面向对象特征之一: 封装与隐藏
- 8.1 问题引入:
- (1)代码演示:
- 8.2 封装性体现:
- 8.3 权限修饰符:
- (1)封装性体现的条件:
- (2):fire:总结封装性:
- 8.4 高内聚,低耦合:
- (1)我们程序设计追求的:
- 9. 关键字:this的使用
- 9.1 this关键字理解:
- (1)this修饰属性或方法:
- (2)this调用构造器:
- 10. 关键字:package、 import的使用
- 10.1 关键字: package
- (1)package关键字解释:
- (2)包的作用:
- (3)包的应用:MVC设计模式:
- (5)JDK中主要的包介绍:
- 10.2 关键字:import
- (1)import关键字理解:
- (2)import关键字使用:
- 五、面向对象(中)
- 1. 面相对象特征之二:继承性
- 1.1 继承性的图解:
- 1.2 继承性的理解:
- (1)为什么要有继承性:
- 1.3 继承性的作用:
- 1.4 继承性的格式:
- 1.5 Java中关于继承性的规定:
- 2. 方法的重写 (override/overwrite)
- 2.1 方法重写的定义:
- 2.2 方法重写作用:
- 2.3 方法重写规定:
- 3. 四种访问权限修饰符:
- 4.关键字:super
- 4.1 super关键字的使用:
- (1)super关键字理解:
- (2)super关键调用属性和方法:
- (3)super关键调用构造器:
- (4)super和this关键字的区别:
- 5. 子类对象实例化过程:
- 5.1 对象的内存图解:
- 5.2 子类对象实例化过程理解:
- (1)从结果上来看:(继承性)
- (2)从过程上来看:
- 6.:fire:面向对象特征之三: 多态性
- 6.1 多态性理解:
- (1)多态性:
- (2)对象的多态:
- (3)多态性应用案例:
- (4)虚拟方法调用(Virtual Method Invocation):
- 6.2 多态性总结:
- (1)多态作用:
- (2)多态前提:
- (3)成员方法:
- (4)成员变量:
- 6.3 对象向下转型:使用强制类型转换符
- (1)背景:基本数据类型的转换(Casting):
- (2)问题引入:
- (3)转型格式:
- (4)instanceof关键字使用:
- (5)子父类多态总结:
- 7. Object类的使用:
- 7.1:Object类的解释:
- 7.2:Object类中的主要结构:
- 7.3:== 和 equals()区别:
- (1)回顾==的使用:
- (2)equals()方法使用:
- (3)== 和equals()两者区别:
- 7.4:toString()方法的使用:
- (1)toString()方法理解:
- 8.包装类的使用:
- 8.1:包装类解释:
- 8.2:包装类需要考虑的问题:
- (1) 装箱:基本数据类型包装成包装类的实例:
- (2)拆箱:获得包装类对象中包装的基本类型变量
- (3)字符串(String)——>基本数据类型、包装类:
- (4)基本数据类型、包装类——–>字符串(Str):
- (5)总结:基本类型、包装类与String类间的转换:
- 8.3:包装类的用法举例:
- 9. java中JUnit单元测试:
- 9.1: 使用步骤:
- 9.2 使用说明:
- 六、面向对象(下)
- 1.关键字:static(静态的)
- 1.1 static关键字使用:
- (1)static修饰的结构:
- 1.2 static修饰属性:静态变量(类变量)
- (1) 静态属性与非静态属性(实例变量):
- (2) static修饰属性的其他说明:
- (3) 表格说明静态变量和非静态变量调用:
- (4)静态属性举例:
- (5) 类变量和实例变量的内存解析:
- 1.3:使用static修饰方法:静态方法(类方法)
- (1)类方法的说明:
- 1.4:关于static关键字的注意点:
- 1.5 :如何确定属性和方法要声明为static:
- (1)确定一个属性声明为static:
- (2)确定一个方法声明为static的:
- 1.6 单例(Singleton)设计模式:
- (1)设计模式理解:
- (2)单例设计模式解释:
- (3)单例模式的两种方式:饿汉式和懒汉式
- (4)单例模式的优点:
- (5)单例模式应用场景:
- 2 理解main方法的语法:
- 第二部分:Java高级编程
第一部分:Java基础编程
一、Java语言概述
1. Java基础图解:

2. 软件开发介绍:
2.1 软件开发:
软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。
2.2 人机交互方式:
图形化界面(Graphical User Interface GUI):
- 这种方式简单直观,使用者易于接受,容易上手操作。
命令行方式(Command Line Interface CLI):- 需要有一个控制台,输入特定的指令,让计算机完成一些操作。较为麻烦,需要记录住一些命令。
2.3 常用DOS命令:
(1)常用命令:
- md :创建目录
- rd: 删除目录
- cd : 进入指定目录
- cd… : 退回到上一级目录
- ==cd ==: 退回到根目录
- del : 删除文件
- exit: 退出 dos 命令行
- 补充:echo javase>1.doc
(2)常用快捷键:
- ← → :移动光标
- ↑ ↓:调阅历史操作命令
- Delete和Backspace:删除字符
2. 计算机编程语言介绍:
2.1 语言分类:
(1)第一代语言:
机器语言:
- 指令以二进制代码形式存在;
(2)第二代语言
汇编语言:
- 使用助记符表示一条机器指令;
(3)第三代语言:
高级语言:
- C、Pascal、Fortran面向过程的语言
- C++面向过程/面向对象
- Java跨平台的纯面向对象的语言
- .NET跨语言的平台
- Python、Scala…
3. Java语言概述:
3.1 各领域的应用:
- 企业级应用
- Android平台应用
- 大数据平台开发
3.2 主要诞生:
- Java语言是易学的
- Java语言是强制面向对象的
- Java语言是分布式的
- Java语言是健壮的
- Java语言是安全的
- Java语言是体系结构中立的
- Java语言是编译型的
- Java是性能略高的
- Java语言是原生支持多线程的
4. 运行机制及运行过程:
4.1 Java语言的特点:
(1)特点一:面向对象
两个基本概念:类、对象
三大特性:封装、继承、多态
(2)特点二:健壮性
程序在遇到意外错误或异常时能够正常运行而不崩溃的能力。换句话说,一个健壮的Java程序能够处理意外情况或错误,保持正常运行。
(3)特点三:跨平台性
- write once,run anywhere;
- 原理:JVM(Java虚拟机);
- 理解:Java的源码可以在不同的操作系统(Windows、Linux、OS…)上运行;
4.2 Java两种核心机制:
(1)Java虚拟机 (Java Virtal Machine):
JVM(Java虚拟机)是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器;
(1)垃圾收集机制 (Garbage Collection):
- 它是自动释放程序不再使用的内存的机制。它可以防止内存泄漏,并确保程序的高效运行。垃圾收集器定期检查程序的内存,识别不再使用的对象,然后释放它们占用的内存。这个过程是自动的,不需要程序员进行显式的操作。
- 但是在C语言中需要自己手动的释放内存,容易方法内存泄漏;
5. Java语言环境搭建:
5.1 JDK、JRE、JVM介绍:
(1)JDK(Java Development Kit Java开发工具包):
JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了 JRE。 所以安装了JDK,就不用在单独安装JRE了。
(2)JRE(Java Runtime Environment Java运行环境):
包括Java虚拟机(JVM :Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
(3)jdk、JRE、JVM之间关系:

JDK=JRE+开发工具集(例如javac/Javac编译工具等)
JRE= JVM+Java SE标准类库
5.2 JDk与JRE下载和安装:
(1)JDK下载:
①官网:Oracle.com

②进入products(进入后拉到底部)——>java(进入后拉到底部)——>oracleJDK:




(2)JDk安装:
建议:安装路径不要有中文或者空格等特殊符号
- ①步骤如下:(以jdk17.0.6为例)



注意:一台电脑上可以安转多个不同版本的JDK
5.3 Java环境配置:
注意:
- 环境配置决定jdk使用版本
- path :windows系统执行命令时要搜寻的路径
(1)配置步骤:
①步骤一:
- 打开【此电脑】的属性页面,在【系统】窗口中选择【高级系统配置】在【高级】窗口中选择【环境变量】进行配置:

②步骤二:
在系统变量(s)创建(W)-----> (输入变量名、和设置变量值)-----> 确定

③步骤三:
将新建的变量JAVA_HOME 添加到path路径下:
- path :windows系统执行命令时要搜寻的路径;


④环境配置补充:

6. 开发体验——helloWorld:
6.1 编写、编译和运行:
(1)编写:
public class HelloWorld{ //注意:当修饰符为public时,文件名必须与类型相同public static void main(String [] args){System.out.println("hello world!");}
}
(2)编译和运行:
- ①步骤:
将 Java 代码编写到扩展名为 .java 的文件中。
通过 javac 命令对该 java 文件进行编译。
- 代码:
javac XxxYyy.java(此时文件不区分大小写,因为在Windows下不区分大小写)
通过 java 命令对生成的 class 文件进行运行。代码:java XxxYyy(XxxYyy:类名)
- ②图解:

7. 常见问题及解决方法:
(1)总结:
学习编程最容易犯的错是语法错误。Java要求你必须按照语法规则编写代码。
如果你的程序违反了语法规则,例如:忘记了分号、大括号、引号,或者拼错了单词,
java编译器都会报语法错误。尝试着去看懂编译器会报告的错误信息。
8. 注释(Comment):
分类:单行注释、多行注释、文档注释
8.1 单行和多行注释:
(1)单行注释:
//这是一个int类型的变量
int m;
(2)多行注释格式:
/*这是一句多行注释
*/
(3)作用:
对所写程序进行解释说明,增强可读性。方便自己和他人;
调试所写的代码;
(4)特点:
单行注释和多行注释,注释了的内容不参与编译;
换言之,编译以后生成的,class结尾的字节码文件中不包含注释掉的信息;
(5)注意:
多行注释不能嵌套使用;
8.2 文档注释:
(1)文档注释格式: /** */
/**@author 指定java程序的作者@version 指定源文件的版本
*/
(2)特点:
注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档;
- ①操作方式:(类必须是public)
前提:在源文件(.java文件)中使用了文档注释
//举例代码
public class Test{public static void main(String[] args) {System.out.println("Hello World!");}/**这是一个我说的方法....*/public static void say(String language){System.out.println("我正在用"+language+"交谈....");}
}

- ②生成的文件:
点击打开产生的day01目录 ------> 双击(源文件名).html文件


9. Java API文档:
9.1 文档解释:
API (Application Programming Interface,应用程序编程接口)是 Java 提供的基本编程接口。
Java语言提供了大量的基础类,因此 Oracle 也为这些基础类提供了相应的 API文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法。
- 下载API文档
网址:http://www.oracle.com/technetwork/java/javase/downloads/index.html
Additional Resources-Java SE 8 Documentation下载


9.2 API基本组成:

(1)API文档:
API文档打开之后,点击显示 ------> 索引 -----> 查找自己想查找的结构
例如:Scanner类
- 在API文章中,会存在属性、构造器和方法三部分-----> 找到这些类成员可以点击查看(方法)内部结构


10. 编程风格:
(1)正确注释和注释风格:
使用文档注释来注释整个类或整个方法。
如果注释方法中的某一个步骤,使用单行或多行注释。
(2)正确的缩进和空白:
使用一次tab操作,实现缩进。
运算符两边习惯性各加一个空格。比如:2 + 4 * 5。
(3)书写代码块的风格:
Java API 源代码选择了行尾风格

11. 常用java开发工具:
(1)文本编辑工具:
记事本;
UltraEdit;
EditPlus;(开始将会使用一段时间)
TextPad;
NotePad;
(2)Java集成开发环境:
ntegrated Development Environment:IDE
- JBuilder
- NetBeans
- Eclipse (会使用一段时间)
- MyEclipse
- IntelliJ IDEA(以后将会常使用)
二、Java基本语法:
1. 关键字和保留字:
1.1 关键字(keyword)定义和特点:
(1)定义:
被Java语言赋予了特殊含义,用做专门用途的字符串(单词);
(2)特点: 官方地址
关键字中所有字母都为小写;
1.2 保留字(reserved word):
Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使 用。自己命名标识符时要避免使用这些保留字 goto 、const;
2. 标识符(Identifier):
2.1 标识符定义:
Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符;
- 技巧:凡是自己可以起名字的地方都叫标识符。
(1)定义标识符的规则:
- 由26个英文字母大小写,0-9 ,_或 $ 组成 ;
- 数字不可以开头。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制。
- 标识符不能包含空格。
(2)定义标识符的规范:
**包名:**多单词组成时所有字母都小写:xxxyyyzzz
**类名、接口名:**多单词组成时,所有单词的首字母大写:XxxYyyZzz
**变量名、方法名:**多单词组成时;第一个单词首字母小写,第二个单词开始每个单词首字母大 :xxxYyyZzz
**常量名:**所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
(3)注意:
在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
3. 变量:
3.1 变量解释:
(1)变量概念:
内存中的一个存储区域 ;
该区域的数据可以在同一类型范围内不断变化;
变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值;

(2)变量作用:
用于在内存中保存数据;
(3)变量使用注意:
Java中每个变量必须先声明,后使用。
使用变量名来访问这块区域的数据 ;
变量的作用域:其定义所在的一对**{ }**内 ’
变量只有在其作用域内才有效 ;
同一个作用域内,不能定义重名的变量;
(4)变量声明:
语法:<数据类型> <变量名称>
例如:int var;
(5)变量赋值:
语法:<变量名称> = <值>
例如:var = 10;
(6)变量声明与赋值:
语法: <数据类型> <变量名> = <初始化值>
例如:int var = 10;
3.2 变量分类:
(1)按数据类型分类:

①基本数据类型(primitive type):
- 数值型:
整数类型:byte、short、int、long
浮点类型:float、double
- 字符型:char
- 布尔型:boolean
②应用数据类型 (reference type):
类:class
接口:interface
数组:[ ]
(2)按声明位置分类:
在方法体外,类体内声明的变量称为成员变量。 在方法体内部声明的变量称为局部变量。
①成员变量:
实例变量(不以static修饰)
类变量(以static修饰)
②局部变量:
形参(方法、构造器中定义的变量)
方法局部变量(在方法内定义)
代码块局部变量(在代码块内定义)
③注意:二者在初始化值方面的异同:
同:都有生命周期(作用域),都需要先声明后使用。
异:局部变量除形参外,需显式初始化,才能进行运算,成员变量会有默认值。
3. 3 变量使用规则:
(1)基本数据类型:
- 数值型:
- i. 整数类型:
(bit: 计算机中的最小存储单位。byte:计算机中基本存储单元。)

- byte:1字节=8bit (-128-127)
byte b1=-128; //声明byte变量并赋值
byte b2=127;//byte b3=128; //编译不通过,超出byte变量的容量大小;
- short:2字节=16bit (-215—215-1)
short s1=332; //声明short变量并赋值
short s2=333;
- int:4字节=32bit(-231—231-1)
//声明并赋值
int i1=334; //声明int变量并赋值//先声明,后赋值
int i2; //声明int变量
i2=44; //int变量赋值;
- long:8字节=64bit(-263—263-1)
注意:定义long变量时,其赋于值必须以“L”或“l”结尾;
//声明long类型数据,赋值时,必须使用L或者l后缀
long l1=333L; //声明long变量并赋值;
long l2=847L;
总结:java中整型默认常量是int,声明long常量其后需要加“L”或“l”
- ii. 浮点型:

- float:单精度[4字节= 32bit(-3.403E38 ~ 3.403E38)]
注意:定义float变量时,其赋于值必须以“f”或“F”结尾;
float f1=23.23f; //声明float变量并赋值;//float f2=23.23; //类型不匹配,编译不通过(浮点型变量的默认类型为double类型)
-double:双精度(8字节 = 64bit)
//注意:初始化过程中不能超出其类型的最大存储范围
double d1=88.34;
总结:java中浮点型默认常量是double,声明float常量其后需要加“f”或“F”
- 字符型:
- char: 一个字符=2字节
使用:①一个字符、②一个转义字符、③直接使用Unicode值表示字符常量
//普通字符
char c1='a';
char c2='中';
char c3='3';//转义字符
char c4='\t'; //\t是制表符
char c5='\n'; //\n:换行符//使用Unicode码
char c6='\u0094'; //Unicode值(Unicode码包含ASCII码)
char c7=97; //表示字符a;
注意:
- Unicode:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一 无二的编码,使用 Unicode 没有乱码的问题。
- UTF-8 是在互联网上使用最广的一种 Unicode 的实现方式。
- 布尔型:
boolean:boolean类型数据只允许取值true和false,无null;
boolean 类型用来判断逻辑条件,一般用于程序流程控制:
- if条件控制语句;
- 三目运算符:(表达式1>表达式2)? 取值1:取值2;
- while循环控制语句;
- do-while循环控制语句;
- for循环控制语句;
//注意:boolean类型变量初始化,只能时true或者false
//boolean类型变量的默认值是false(属性,数组元素)
boolean b1 = true;
boolean b2 = false;
(2)引用数据类型:
- 类:class
字符串数据类型:String
- 使用方式与基本数据类型一致。例如:String str = “abcd”( 或String str = new String(“abcd”); );
- 一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据;其连接得到的结果都为字符串类型。
- 举例:
//其他数据类型变量与字符串类型变量进行连接时,其结果都为字符串;
String s1="str";
String s2="ing";String s3=s1+s2;
System.out.println(s3); //stringint i1=33;
String s4=s1+i1;
System.out.println(s4); //str33
注意:String类型变量与其它八种基本数据类型进行“+”操作,表示连接;
- 接口:interface(其后会介绍)
//接口的定义方式
public interface Test{ }
//接口需要其他类来实现(略)
- 数组:[ ](其后会介绍)
//一维数组的声明方式:
int [] arr = new int[3]; //动态赋值
//.......
3.4 基本数据类型转换:
(1)自动类型提升转换:
容量小的类型自动转换为容量大的数据类型。数据类型按容量大小排序为:
- 注意:此时容量大小指:表示可数的范围大小;

- ②注意:
- byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。
- boolean类型不能与其它数据类型运算。
- 当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为字符串(String)类型。
- ③代码演示:
byte b1=22;byte b2=21;short s1=33;char c1='a';//byte、short、char之间不会相互转换int i4=b1+b2;int i3=c1+s1; int i2=b1+s1; int i1=23;long l1=44l;float f1=32.3f;double d1=23.3;//自动类型提升:容量小的->容量大的i1=b1;i1=s1;i1=c1;long l2=i1;float f2=i1;double d2=f1;
3. 5 强制类型转换:
- 自动类型提升逆过程,表示容量大到容量小的;(此时容量大小指:表示可数的范围大小)
- 使用时要加上强制转换符:(),但可能造成精度降低或溢出,格外要注意;
- (1)格式:
- 数据类型 变量名 = (数据类型)变量值;
int i1=33;
char c1=22;
long l1=33l;
float f1=44.3f;char c2=(char)i1;
int i2=(int)l1;
int i3=(int)f1;
- (2)注意:
- boolean类型不可以转换为其它的数据类型。
- 字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可 以实现把字符串转换成基本类型。
String s="233";
int i1=Integer.parseInt(s); //调用包装类中的parseInt()方法
3.6 变量之进制:
- 所有数字在计算机底层都以二进制形式存在;
(1)对于整数,有四种表示方式:
- 二进制(binary):0,1 ,满2进1.以0b或0B开头。
- 十进制(decimal):0-9 ,满10进1。
- 八进制(octal):0-7 ,满8进1. 以数字0开头表示。
- 十六进制(hex):0-9及A-F,满16进1. 以0x或0X开头表示。此处的A-F不区分大小写。 如:0x21AF +1= 0X21B0
- ① 二进制:
原码:直接将一个数值换成二进制数。最高位是符号位 ;
负数的反码:是对原码按位取反,只是最高位(符号位)确定为1。
负数的补码:其反码加1。
计算机以二进制补码的形式保存所有的整数。
正数的原码、反码、补码都相同 。
负数的补码是其反码+1注意:补码—->原码(过程相反:补码—[除符号位其他位取反]—-> 反码—[+1]——>原码)
- ② 进制之间的转换:(二进制作为转换的桥梁)

二进制——>八进制或十六进制:
- 将二进制的三位或四位转换为八进制或十六进制的一位,逆转换只需逆过程

4. 运算符:
- 运算符的种类:算术、赋值、比较(关系)、逻辑、位、三元运算符
4.1 算术运算符:
- 运算符号:+(负号)、-(正号)、+、-、/、%、++、–、+(字符串连接)

(1)算术运算符需要注意:
- 取模时,模数的正负号取决于被模数。
- %:如果对负数取模,可以把模数负号忽略不记,如:5%-2=1。 但被模数是负数则不可忽略。此外,取模运算的结果不一定总是整数;
- (前)++:先自增一,后再运算;
- (后)++:先运算,后自增一;
- (前)–:先自减一,后再运算;
- (后)–:先运算,后再自减一;
int num1=22;
int num2=num1++;
System.out.println(num2); //22
int num3=++num1;
System.out.println(num3); //23
4.2 赋值运算符:
运算符号:=、-=、+=、*=、/=、%=
(1)注意:
- 当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。支持连续赋值;
- +=、-=、+=、*=、/=、%=运算时,并不会改变其原来的数据类型;
int num1=10;
num1+=2; //int型的12byte b1=12;
// b1=b1+2; //类型不匹配,编译不通过(由式子的右边为int类型)
b1+=2; //编译通过,值为byte型14;int num2=15;
num2%=2; //int型的1;
4.3 比较(关系)运算符:
运算符号:== 、!=、>、<、>=、<=、interface

- 运算结果:都是boolean型,也就是要么是true,要么是false。
- >、<、>=、<=:只能使用在数值类型数据之间。
- ==、!=:既可以使用在数值类型数据之间,也可以使用在引用数据类型之间;
(1)代码演示:
//创建两个对象:account1 和 account2
Account account1= new Account(100);
Account account2= new Account(200);
boolean result= (account1==account2); //false:地址值不同
int num1=23;
boolean b1=true;
if(b1==true){System.out.println(“结果为真”);
}else{System.out.println(“结果为假”);
}
if(num1>10){System.out.println(“结果为真”);
}else{System.out.println(“结果为假”);
}
4.4 逻辑运算符:
运算符号:& 、&&、|、||、!、^

(1)注意:
- ① 逻辑运算符所运算的对象为boolean型;
- ② &(逻辑与)和&&(短路与)的异同点:
- 两者运算结果一致;
- 若左边结果为true,&和&&都会执行右边;
- 若左边运算结果为false,&会继续执行右边,而&&不会继续执行右边运算;
int num1=12;
boolean b1=true;//b1=false
if(b1==true & ++num1>11){}else{}
System.out.println(num1); //13(b1为true) 13(b1为false)int num2=12;
boolean b2=true;//b2=false
if(b2==true && ++num2>11){}else{}
System.out.println(num2); //13(b2为true时) 12(b2为false时)
- ③ |(逻辑或)和 ||(短路或)的异同点:
- 两者运算结果一致;
- 若左边结果为false,| 和 || 都会执行右边;
- 若左边结果为true,| 会继续执行右边,而 || 不会继续执行右边运算;
int num3=10;
boolean b3=true;
if(b3==true | ++num3>9){}else{}
System.out.println(num3); //11int num4=10;
boolean b4=true;
if(b4==true || ++num4>9){}else{}
System.out.println(num4); //10
- 注意:异或( ^ )与或( | )的不同之处是:当左右都为true时,结果为false。
4.5 位运算符:
运算符号:<<、>>、>>>、&、|、^、~:位运算是直接对整数的二进制进行的运算。

(1)位运算符操作的对象都是整型的数据:
- ① <<(左移):
- 在一定范围内,左移多少为,就是乘于2的多少次方;
- 8<<2 ——> 8*22
- ② >>(右移):
- 在一定范围内,右移多少为,就是除以2的多少次方;
- 8>>2 ——> 8/22
- ③ 位运算的过程:

^(异或运算):
注意:
- num3=num1^num2;
- mum1=num3^num2;
4.6 三元运算符:
(1)格式:
(条件表达式)?表达式1:表达式2;
- 条件表达式为true时,则执行表达式1;否则执行表达式2;
(2)注意:
- ① 表达式1和表达式2为同种类型;
- ② 三元运算符与if-else的联系与区别:
- 三元运算符可简化if-else语句 。
- 三元运算符要求必须返回一个结果。
- if后的代码块可有多个语句。
- ③ 能使用三元运算符的就定可以使用if-else,反之不一定;
- ④三元运算符与if-else都可以使用时,选择三元运算符;(原因:简洁并且效率高)
5. 程序流程控制:
5.1 顺序结构:
- 程序从上到下逐行地执行,中间没有任何判断和跳转。

5.2 分支结构:
- 根据条件,选择性地执行某段代码。有if…else和switch-case两种分支语句。
(1)分支语句一:if-else
- ① 第一种:单分支
if(条件表达式){//执行语句;
}
- ② 第二种:双分支
if(条件表达式){//执行语句1;
}else{//执行语句2;
}
- ③ 第三种:多分支
if(条件表达式1){//执行语句1;
}else if(条件表达式2){//执行语句2;
}else if(条件表达式3){//执行语句3;
}else if(条件表达式4){//.............}else{//执行语句n;
}
- ④ 注意:if-else使用说明
- 条件表达式必须是布尔表达式(关系表达式或逻辑表达式)、布尔变量;
- 语句块只有一条执行语句时,一对{ }可以省略,但建议保留;
- if-else语句结构,根据需要可以嵌套使用;
- 当if-else结构是“多分支”时,最后的else是可选的,根据需要可以省略;
- 当多个条件是“互斥”关系时,条件判断语句及执行语句间顺序无所谓 ,当多个条件是“包含”关系时,“小上大下 / 子上父下”;
(2)键盘获取不同类型值(使用Scanner类)
- ① 基本步骤:
- 导包:import java.util.Scanner;
- Scanner类的实例化:Scanner scanner = new Scanner(System.in);
- 调用Scanner类的方法:**scanner.nextXxxx( ); ** ,来获取某一类型的值;
- ② 说明:
- 获取byte、short、int、long、float、double值,只需Scanner对象调用Scanner类的方法;格式:对象名.nextXxx( );
- 获取String类型的值,只需对象名.next( ); 即可,注意,在键盘中我们不能直接获取char类型的值,而需要先获取String类型的值,在调用String类中的方法来获取char类型的值;格式:String对象名.charAt( 索引号);
(3)随机数产生:
- ① 方式一:Math.random();
- Math.random( ); ——> 所得double型变量数值的范围:[ 0.0 , 1.0)
- 若需得到int型变量[a , b]范围的数:需要,(int)(Math.random( )*(b-a+1)+a)
- ② 方式二:Random类
Random ran = new Random();
int num=ran.nexInt(number); //此时生成的随机数为0~number-1
(4)分支语句二:switch-case
- ① 格式:
switch(表达式){case 常量1:语句1;//break;case 常量2:语句2;//break;case 常量3:语句3;//break;.......default ://break;
}
- ② 使用说明:
- switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);
- case子句中的值必须是常量,不能是变量名或不确定的表达式值;
- 同一个switch语句,所有case子句中的常量值互不相同;
- break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有break,程序会顺序执行到switch结尾 ;
- default子句是可任选的。同时,位置也是灵活的。当没有匹配的case时, 执行default;
(5)if-esle和switch-case语句对比:
- 如果判断的具体数值不多,而且符合byte、short 、char、int、String、枚举等几 种类型。虽然两个语句都可以使用,建议使用swtich语句。因为效率稍高。
- 其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。 也就是说,使用switch-case的,都可以改写为if-else。反之不成立。
5.3 循坏结构:
- 主要结构:while、do…while、for
- 根据循环条件,重复性的执行某段代码。
- 注意:JDK1.5提供了foreach循环,方便的遍历集合、数组元素。
循环四个组成部分:
- ①初始化条件;
- ②循环条件;——boolean型
- ③循环体;
- ④迭代条件;
(1)循环结构一:for循环
- 语法格式:
for(①初始化条件; ②循环条件 ; ④迭代条件){③循环体;
}
-
执行过程:
①——②——③——④——②——③——④——②——③——④…——②——③——④ -
说明:(以下序号为循环组成部分代号)
- ②循环条件部分为boolean类型表达式,当值为false时,退出循环 ;
- ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔;
- ④可以有多个变量更新,用逗号分隔;
- brake关键字:
- 在循环中一旦执行到break关键字就跳出循环;
(2)循环结构二:while循环
- 语法格式:
①初始化条件;while(②循环条件){③循环体;④迭代条件;}
-
执行过程:
①——②——③——④——②——③——④——②——③——④……——②——③——④ -
说明:
- 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
- for循环和while循环可以相互转换。
(3)循环结构三:do-while循环
- 语法格式:
①初始换条件;do{③循环体;④迭代条件;}while(②循环条件);
-
执行过程:
①——③——④——②——③——④——②……——③——④——② -
说明:
- do-while循环结构中的循环体至少执行一次;
(4)嵌套循环:
- 将一个循环放在另一个循环体内,就形成了嵌套循环。其中, for ,while ,do…while均可以作为外层循环或内层循环。
- 实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的 循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开 始下一次的循环。
- 设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
- 外层循环表示行,内层循环表示列;
(5)特殊关键字使用:break和continue
- ① break关键字使用:
break语句用于终止某个语句块的执行
{ …… break;…… }
break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块:
label1: { ……
label2: { ……
label3: { ……
break label2;
……
}
}
}
- ② continue关键字使用:
- continue只能使用在循环结构中;
- continue语句用于跳过其所在循环语句块的一次执行,继续下一次循环;
- continue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环;
public class ContinueTest {public static void main(String args[]){for (int i = 0; i < 10; i++) {if (i%2==0){continue; //跳出当次循环} System.out.print(i+" ") ; // 1 3 5 7 9 }}
}
- ③ break和continue关键字的说明:
- break只能用于switch语句和循环语句中。
- continue 只能用于循环语句中。
- 二者功能类似,但continue是终止本次循环,break是终止本层循环。
- break、continue之后不能有其他的语句,因为程序永远不会执行其后的语句。
- 标号语句必须紧接在循环的头部。标号语句不能用在非循环语句的前面。
- 很多语言都有goto语句,goto语句可以随意将控制转移到程序中的任意一条语句上,然后执行它。但使程序容易出错。Java中的break和continue是不同于goto的。
- ④ 附加:return的使用:
- return: 并非专门用于结束循环的,它的功能是结束一个方法。 当一个方法执行到一个return语句时,这个方法将被结束。
- 与break和continue不同的是,return直接结束整个方法,不管这个return处于多少层循环之内;
三、数组
1. 数组的概述:
(1) 数组(Array):
- 是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
(2) 数组的常见概念:
- 数组名;
- 下标(或索引);
- 元素;
- 数组长度;
(3) 数组介绍:
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
- 创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。
- 数组的长度一旦确定,就不能修改。
- 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- ① 数组的分类:
- **按照维度:**一维数组、二维数组、三维数组、…。
- **按照元素的数据类型分:**基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)。
2. 一维数组的使用:
2.1 数组声明:
- (1) 一维数组的声明方式:type var[ ]或 type[] var
int arr [];
int []arr1;
char []arr2;
double []b;
String []arr3; //字符串类型变量数组(引用类型变量数组)
2.2 数组的初始化:
(1) 动态初始化:
- 数组声明且为数组元素分配空间与赋值的操作分开进行:
//例如:
int []arr=new int[4];
int [0]=1;
int [1]=2;
int [2]=3;
int [3]=4;String [] names;
names= new String[3];
names[0]="于敏";
names[1]="钱学森";
names[2]="程开甲";
(2) 静态初始化:
- 在定义数组的同时就为数组元素分配空间并赋值。
//例如:
int [] arrs= new int[]{1,2,3,4};
//int []arrs={1,2,3,4}String []names= new String[]{"于敏","钱学森","程开甲"};
//String []names={"于敏","钱学森","程开甲"};
2.3 数组元素的应用:
(1)定义并用运算符new为之分配空间后,才可以引用数组中的每个元素;
(2)数组元素的引用方式:数组名[数组元素下标]
- 数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
- 数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]。
(3)每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长度(元素个数)。
- 数组一旦初始化,其长度是不可变的;
2.4 数组元素的默认初始化值:
- 数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。
public class Array{public static void main(String [] args){int [] arr= new int[3];System.out.println(arr[2]); //arr[2]默认值为0}
}
- 对于基本数据类型而言,默认初始化值各有不同;
- 对于引用数据类型而言,默认初始化值为null(注意与0不同!)

2.5 一维数组内存解析:
栈(stack)和堆(heap)结构:
- 栈: (stack):局部变量;
- 堆:(heap):new出来的结构:对象、数组;


3. 多维数组使用:
3.1 多维数组的介绍:
(1)Java 语言里提供了支持多维数组的语法。
(2)如果说可以把一维数组当成几何中的线性图形, 那么二维数组就相当于是一个表格,像右图Excel 中的表格一样。
(3)对于二维数组的理解,我们可以看成是一维数组 array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组。
(4)数组的length属性;(获取数组的长度,即一维数组的个数和一维数组中元素的个数)
(5) 二维数组值中数组元素的初始化值和一维数组类似;
- byte、short、int、long——>0;
- float、double——>0.0;
- char——> 0 (ascll码中的’\u0000’);
- boolean——> false;
- 引用数据类型——> null或者地址;
3.2 二维数组初始化:(二维数组:数组中的数组)
(1)动态初始化:
- ① 格式一:
int [][]arr= new int[3][2];//代码说明://定义了名称为arr的二维数组//二维数组中有3个一维数组//每一个一维数组中有2个元素//一维数组的名称分别为arr[0], arr[1], arr[2]//给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;
- ② 格式二:
int [][]arr= new int [3][];//代码说明:// 二维数组中有3个一维数组。//每个一维数组都是默认初始化值null (注意:区别于格式1)//可以对这个三个一维数组分别进行初始化arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];// 注意:// int[][]arr = new int[][3]; //非法
(2)静态初始化:
- ① 格式三:
int [][]arr= new int[][]{{3,8,2},{2,7},{9,0,1,6}};//代码说明://定义一个名称为arr的二维数组,二维数组中有三个一维数组//每一个一维数组中具体元素也都已初始化//第一个一维数组 arr[0] = {3,8,2};//第二个一维数组 arr[1] = {2,7};//第三个一维数组 arr[2] = {9,0,1,6};//第三个一维数组的长度表示方式:arr[2].length;//注意特殊写法情况://int[] x,y[]; x是一维数组,y是二维数组。//Java中多维数组不必都是规则矩阵形式
3.3 二维数组内存解析:
- 栈(stack):存放局部变量;
- 堆(heap):存放new出来的结构(数组、对象);
- 方法区:静态域+常量池;
(1)举例一:
//举例代码:
int[][] arr1 = new int[4][];
arr1[1] = new int[]{1,2,3};
arr1[2] = new int[4];
arr1[2][1] = 30;

(2)举例二:
//举例代码:
int[][] arr1 = new int[4][];
arr1[0] = new int[3];
arr1[1] = new int[]{1,2,3};
arr1[0][2] = 5;
arr1 = new int[2][];

4. 数组中常见的算法:
- 数组元素的赋值(杨辉三角、回形数等) ;
- 求数值型数组中元素的最大值、最小值、平均数、总和等;
- 数组的复制、反转、查找(线性查找、二分法查找) ;
- 数组元素的排序算法;
4.1 数组元素的赋值:
(1)杨辉三角、回形数(略)等
- ① 杨辉三角:

//代码实现:
int [][]yanghui= new int[10][];//2.给数组元素赋值:for(int i=0;i<yanghui.length;i++) {//yanghui[i]= new int[i+1]; //方式一: //2.1:给首末尾元素赋值:yanghui[i][0]=yanghui[i][i]=1;//2.2:给非首末元素赋值:for(int j=1;j<yanghui[i].length-1;j++) {yanghui[i][j]=yanghui[i-1][j-1]+yanghui[i-1][j];}//方式二:/*for(int j=0;j //3.遍历数组:for(int j=0;j<yanghui[i].length;j++) {System.out.print(yanghui[i][j]+" ");} System.out.println();}
4.2 数组元素查找:
(1)线性查找:
注意:
- 是将数组元素依次遍历下去,再查找是否有符合的元素;对于数值类型的变量而言,这样的查找效率非常低,而应该考虑二分查找的方式;
//代码演示:
int []array=new int []{1,2,3,5,4};
int dest=1; //需要查找的元素
boolean isFlag=true; //记录元素最终是否被查到的标签
for(int i=0;i<array.length;i++){ //遍历数组if(dest==array[i]){isFlag=false;System.out.println("找到了,索引为:"+i);break;}
}
if(isFlag){System.out.println("抱歉未找到");
}
(2)二分查找(折半查找):
- 前提: 数组元素排好序;
- 说明:是通过比较数组元素的中间值来进行查找元素的,这样大大的提高查找的效率;
- 局限:只适用于数值数据类型;
代码演示:
int []arr= new int []{1,2,3,4,5,6,7};
boolean isFlag=true;
int dest=6; //目的值
int head=0; //初始首索引
int end=arr.length-1; //初始尾索引;
while(head<=end){int middle=(head+end)/2;//if条件分支中,进行数组元素的中间值和目的值进行比较if(dest=arr[middle]){ System.out.println("找到了,索引位置为:"+middle);isFlag=false;break;}else if(dest>arr[middle]){head=middle+1;}else{end=middle-1;}
}
if(isFlag){System.out.println("未找到");
}
4.3 数组元素排序算法:
(1)排序算法的介绍:
① 排序解释:
- 假设含有n个记录的序列为{R1,R2,…,Rn},其相应的关键字序列为 {K1,K2,…,Kn}。将这些记录重新排序为{Ri1,Ri2,…,Rin},使得相应的关键 字值满足条Ki1<=Ki2<=…<=Kin,这样的一种操作称为排序。
- 通常来说,排序的目的是快速查找。
② 衡量排序算法的优劣:
- 时间复杂度:分析关键字的比较次数和记录的移动次数;
- 空间复杂度:分析排序算法中需要多少辅助内存;
- 稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
③ 排序算法分类:内部排序和外部排序:
- 内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
- 外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
④ 十大内部排序算法:
-
i. 选择排序:
直接选择排序、堆排序 -
ii. 交换排序:
冒泡排序、快速排序 -
iii. 插入排序 :
直接插入排序、折半插入排序、Shell排序 -
iv. 归并排序
-
v. 桶式排序
-
vi. 基数排序
-
⑤ 算法介绍:
算法的五大特征:

- 说明:满足确定性的算法也称为:确定性算法。现在人们也关注更广泛的概念,例如考虑各种非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终止的计算描述,这种描述有时被称为过程(procedure)。
4.4 排序算法的实现:
(1)交换排序(冒泡排序(掌握)、快速排序(了解)):
①冒泡排序:
- 介绍:
- 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
- 排序思想:
- 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。
- 代码实现:
//冒泡排序:
for(int i=0;i<array.length-1;i++) { //1.需要比较的轮数:for(int j=0;j<array.length-1-i;j++) {//2.每一轮中需要比较的次数:if(array[j]>array[j+1]) {int temp=array[j];array[j]=array[j+1];array[j+1]=temp;}}}
② 快速排序:
- 介绍:
- 快速排序(Quick Sort)由图灵奖获得者Tony Hoare发明,被列为20世纪十 大算法之一,是迄今为止所有内排序算法中速度最快的一种。冒泡排序的升 级版,交换排序的一种。快速排序的时间复杂度为O(nlog(n))。
- 排序思想:
- 从数列中挑出一个元素,称为基准(pivot)
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准 值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后, 该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
- 递归的最底部情形,是数列的大小是0或1,也就是永远都已经被排序好 了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代 (iteration)中,它至少会把一个元素摆到它最后的位置去。
- 代码实现:
//交换数组元素的值
private static void swap(int[] data, int i, int j) {int temp = data[i];data[i] = data[j];data[j] = temp;}private static void subSort(int[] data, int start, int end) {if (start < end) {int base = data[start];int low = start;int high = end + 1;while (true) {while (low < end && data[++low] - base <= 0);while (high > start && data[--high] - base >= 0);if (low < high) {swap(data, low, high);} else {break;}}swap(data, start, high);subSort(data, start, high - 1);//递归调用subSort(data, high + 1, end);}}public static void quickSort(int[] data){subSort(data,0,data.length-1);}
- 快速排序演示:


- 举例:

(2)插入排序(直接插入排序、希尔排序):
① 直接插入排序:
- 排序思想:
- 把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

- 代码实现:
//直接插入排序
for(int i=0;i<array.length-1;i++) { //1.需要比较的轮数:for(int j=i+1;j > 0;j--) {//2.每一轮中需要比较的次数:if(array[j]< array[j-1]) { //需要插入的元素是否小于有序元素int temp=array[j];array[j]=array[j-1];array[j-1]=temp;}else{break; //当无序中的元素大于有序元素的元素时,直接介绍比较}}}
② 希尔排序:
- 排序思想:
- 希尔排序的基本思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上有较大提高。

(3)选择排序(简单选择排序(掌握)、堆排序):
① 简单选择排序:
- 排序思想:
- 在一组元素R[i]到R[n]中选择具有最小关键码的元素若它不是这组元素中的第一个元素,则将它与这组元素中的第一个元素对调。除去具有最小关键字的元素,在剩下的元素中重复第(1)、(2)步,直到剩余元素只有一个为止。

- 代码演示:
//假定第一元素为最小;则将第一个元素依次和其后的元素进行比较,选出最小的元素,方法第一个元素处;以此循环,选出第二小的元素放于第二个元素处。
//注意:将该嵌套循环封装在方法中
for(int i=0;i<arr.length-1;i++){ //需要比较的轮数for(int j=i+1;j<arr.length;j++){ //每轮需要比较的次数if(arr[i]>arr[j]){ //该处元素依次和其后的元素进行比较int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}
}
② 堆排序:
- 排序思想:
建立初始堆:
- 将排序码k1,k2k3,…,kn,表示成一棵完全二叉树,然后从第[n/2]个排序码(即树的最后一个非终端结点)开始筛选,使由该结点作根结点组成的子二叉树符合堆的定义,然后从第(n/2) -1个排序码重复刚才操作,直到第一个排序码止。这时候,该二叉树符合堆的定义,初始堆已经建立。
堆排序:
- 将堆中第一个结点(二叉树根结点)和最后一个结点的数据进行交换(k1与kn),再将k1~ kn-1重新建堆,然后k1和kn-1交换,再将k1~kn-2 重新建堆,然后k1,和kn-2交换,如此重复下去,每次重新建堆的元素个数不断减1,直到重新建堆的元素个数仅剩一个为止。这时堆排序已经完成,则排序码k1,k2,k3,…,kn已排成一个有序序列。
(4)归并排序:
- 排序思想:
是多次将了两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序的子表合并成一个有序的表,即二路归并。
- 二路归并排序:
- 是将R[0……n-1]看成是n长度为1的有序序列,然后进行两两归并,得到[n/2]个长度为2(最后一个有序序列的长度可能为2)的有序序列,再进行两两归并,得到[n/4]个长度为4(最后一个有序序列的长度可能为4)的有序序列,……,直到得到长度为n的有序序列。

(5)排序算法性能对比:

- 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归 并排序。
- 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较 简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序 算法,其算法比较复杂,认为是复杂排序。
- 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排 序、快速排序、 Shell排序和堆排序是不稳定排序。
- 从待排序的记录数n的大小看:n较小时,宜采用简单排序;而n较大时宜采用改进排序。
(6)排序算法的选择:
- 若n较小(如n≤50),可采用直接插入或直接选择排序。 当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。
- 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
- 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或 归并排序。
5. Arrays工具类的使用:
- java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。

(1)代码演示:
import java.util.Arrays;//工具类Arrays中方法的使用: int []arr=new int[] {1,23,3,4,5,5,8};int []arr1=new int[] {-12,23,-88,3,78,98,4,-34,-13};//1. boolean equals(int[] a,int[] b):判断两个数组是否相等。System.out.println(Arrays.equals(arr, arr1));//2. String toString(int[] a):输出数组信息。System.out.println(Arrays.toString(arr1));System.out.println(Arrays.toString(arr));//3. void fill(int[] a,int val) :将指定值填充到数组之中。Arrays.fill(arr, 8);System.out.println(Arrays.toString(arr));//4. void sort(int[] a):对数组进行排序。Arrays.sort(arr1);System.out.println(Arrays.toString(arr1));//[-88, -34, -13, -12, 3, 4, 23, 78, 98]//5. int binarySearch(int[] a,int key):对排序后的数组进行二分法检索指定的值。int number=Arrays.binarySearch(arr1, -88); //值返回为负数并为找到;System.out.println(number);
6. 数组使用中的常见异常:
(1)数组脚标越界异常(ArrayIndexOutOfBoundsException):
int[] arr = new int[2];
//System.out.println(arr[2]); //运行不通过,数组角标越界
//System.out.println(arr[-1]); //运行不通过,数组角标越界//访问到了数组中的不存在的脚标时发生。
(2)空指针异常(NullPointerException):
//举例说明:
int[] arr = null;
//System.out.println(arr[0]);int [] [] arr2 = new int [3][4];
arr[2] = null;
//arr[2].length; //报异常//arr引用没有指向实体,却在操作实体中的元素时。
四、面向对象(上)
😄面向对象三条主线:
- java类和类的成员(属性、方法、构造器、代码块、内部类);
- 面向对象三大特性:封装性、继承性、多态性;
- 其他关键字使用:this、super、static、abstract;
1. 面向过程与面向对象的简单介绍:
1.1 面向过程(POP) 与 面向对象(OOP):
- 面向对象:Object Oriented Programming
- 面向过程:Procedure Oriented Programming
(1)二者的思想理解:
① 二者都是一种思想,面向对象是相对于面向过程而言的。
- 面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
- 面向对象:将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
② 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
③ 举例说明:人把大象装进冰箱
- 面向过程:
- 打开冰箱;
- 把大象放入冰箱;
- 把冰箱门关住;
- 面向对象:
人{打开(冰箱){冰箱.开门();}操作(大象){大象.进入(冰箱)}关闭(冰箱){冰箱.关门();}
}
大象{进入(冰箱){}
}
冰箱{开门(){}关门(){}
}
1.2 面向对象的三大特性(后面会介绍):
- 封装 (Encapsulation)
- 继承 (Inheritance)
- 多态 (Polymorphism)
1.3 面向对象思想概述:
(1)程序员从面向过程的执行者转化成了面向对象的指挥者;
(2)面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序 语言,把类构造成计算机能够识别和处理的数据结构。
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
2. Java语言的基本元素: 类和对象
2.1 面向对象的思想概述:
(1)类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义 ;
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
(2)“万事万物皆对象”:
- 在java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构;(例如Scanner、String、文件:File、网络资源:URL类)
- 涉及到java语言与前端HTML、后端的数据库交互时,前后端的结构在java层面交互时,都体现类、对象;
(3)面向对象程序设计的重点是类的设计;
- 类的设计:其实就是类的成员(属性和方法)的设计;
2.2 Java类及类的成员:
(1)属性:(Field = 属性 = 成员变量)
(2)行为:(Method = (成员)方法 = 函数)
(3)类的格式:
/*
修饰符 class 类名 {属性声明;方法声明;
}说明:修饰符public:类可以被任意访问类的正文要用{ }括起来
*/ //举例:
public class Person{private int age ; //声明私有变量 agepublic void showAge(int i) { //声明方法showAge( )age = i;}}
- ① 步骤:
- 定义类(考虑修饰符、类名);
- 编写类的属性(考虑修饰符、属性类型、属性名、初始化值);
- 编写类的方法(考虑修饰符、返回值类型、方法名、形参等);
- ② 代码演示:
//以下类的成员将会一次介绍;
public class Student { //Person类//属性的声明String name; //姓名int age; //年龄char sex; //性别//构造器(构造方法)public Student() { //无参构造器super();}public Student(String n, int a, char s) {//有参构造器name = n; age = a;sex = s;}//方法public void study() {System.out.println("学生"+name+"正在学习......");}//代码块{name = "小明";age = 17;sex = '男';}//内部类class Pet{ //内部类的属性String name; //姓名float weight; //体重}}
3. 对象的创建(类的实例化)和使用:
3.1 对象创建与使用:
- 创建对象语法:类名 对象名 = new 类名();
- 使用对象名.对象成员的方式访问对象成员(包括属性和方法)
(1)代码演示:
public class Zoo{public static void main(String args[]){//创建对象Animal xb=new Animal();xb.legs=4;//访问属性System.out.println(xb.legs);xb.eat();//访问方法xb.move();//访问方法}}public class Animal {public int legs;public void eat(){System.out.println(“Eating......”);}public viod move(){System.out.println(“Move......”);}
}
注意:
- 如果创建了一个类的对象,则每个对象都独立的拥有一套类的属性;(非static)意味着:如果我们修改一个对象的属性a,则不影响另外的对象的属性a值;
(2)说明:
① 对象生成:
- Person p1 = new Person(); 执行完后则产生一个Person的对象p1。其中类定义如下:
class Person{int age;void shout(){System.out.println(“oh,my god! I am ” + age);}
}
② 对象使用:
- 当对象被创建后,可以调用类中的属性和方法;
class PersonTest{
public static void main(String[] args) { //程序运行的内存布局如下图Person p1 = new Person();Person p2 =new Person();p1.age = -30;p1.shout();p2.shout();}
}
③ 对象的生命周期:
- 一个对象从产生到被清除的过程:

④ 类的内存解析:
- 栈(stack):存放局部变量;
- 堆(heap):存放new出来的结构(对象,数组、接口);
- 注意:属性(成员变量)置于堆中;

⑤ 类的内存解析:
- i. 举例一:
class Car{String color = "red";int num = 4;void show(){System.out.println("color="+color+"..num="+num);}
}class CarTest {public static void main(String[] args) {Car c1 = new Car(); //建立对象c1Car c2 = new Car(); //建立对象c2c1.color = "blue"; //对对象的属性进行修改c1.show(); //使用对象的方法c2.show();}
}

- ii. 举例二:
class Person{//人类
//1.属性String name;//姓名int age = 1;//年龄boolean isMale;//是否是男性
}
class PersonTest{main(){Person p = new Person();Person p1 = new Person();p1.name = “Tom”;Person p2 = p1;}
}

⑥ 匿名对象:
我们也可以不定义对象的引用,而直接调用这个对象的方法。这样的对象叫做匿名对象。
- 如:new Person().shout();
- 使用情况 :
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 我们经常将匿名对象作为实参传递给一个方法调用。
3.2 类的访问机制:
(1)在一个类中的访问机制:
- 类中的方法可以直接访问类中的成员变量。 (例外:static方法访问非static,编译不通过。)
(2)在不同类中的访问机制:
- 先创建要访问类的对象,再用对象访问类中定义的成员。
4. 类的成员之一: 属性(field)
4.1 变量的分类:成员变量与局部变量
(1)基本概念:
- 在方法体外,类体内声明的变量称为成员变量。
- 在方法体内部声明的变量称为局部变量。

(2)二者在初始化值方面的异同:
① 相同点:
- 定义变量的格式:数据类型 变量名 = 变量值 ;
- 先声明,后使用 ;
- 变量都有其对应的作用域 ;
② 不同点:
- i. 在类中声明的位置的不同:
- 属性:直接定义在类的一对{ }内;
- 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量;
- ii. 关于权限修饰符的不同:
属性:可以在声明属性时,指明其权限,使用权限修饰符。
- 常用的权限修饰符:private、public、缺省、protected —>封装性
- 目前,大家声明属性时,都使用缺省就可以了。
局部变量:不可以使用权限修饰符。

(3)成员变量vs局部变量的内存位置:
- 成员变量:堆空间 或 静态域内;
- 局部变量:栈空间;
① 图片说明:

(4)对象属性的默认初始化赋值:
- 当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型,如上面的Person及前面讲过的数组。
① 属性返回值类型(和数组元素的默认值一致):
- 整型(byte、short、int、long):0;
- 浮点型(float、double):0.0;
- 字符型(char):0 或 ‘\u0000’
- 布尔型(boolean):false;
- 引用数据类型(类、数组、接口):null

5. 类的成员之二:方法(method)
5.1 方法(method)的解释:
- (1)方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
- (2)将功能封装为方法的目的是,可以实现代码重用,简化代码;
- (3)Java里的方法不能独立存在,所有的方法必须定义在类里。
public class Person{private int age;public int getAge() { //声明方法getAge()return age; }public void setAge(int i) { //声明方法setAgeage = i; //将参数i的值赋给类的成员变量age}}
5.2 类中方法的声明和使用:
(1)例如:
- Math类:sqrt()\random();……
- Scanner类:nextXxx()…
- Arrays类:sort()\binarySearch()\toString()\equals()…
(2)举例:
//代码如下:
public void eat() {}
public void sleep(int hour) {}
public String getName() {}
public String getNation(String nation) {}
(3)方法声明:
权限修饰符 返回值类型 方法名 (形参列表){方法体;}
① 说明:
权限修饰符:默认方法的权限修饰符为缺省;
- 权限修饰符:private、缺省、protected、public---->封装性(可见性大小)
② 返回值类型:有返回值 与 无返回值
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定的类型的变量和常量,“return 数据”;
- 如果方法没有返回值,在方法声明时,使用void来表示。通常,没有返回值的方法中,就不使用return关键字。但是,如果使用的话,只能“return”表示结束方法;
我们定义方法该不该有返回值:
- 题目要求;
- 凭经验:具体问题具体分析;
- 代码演示:
public void say(){ //无返回值的方法
}public int sum(int [] arr){ //由返回值的方法int sum = 0; for(int i=0;i<arr.length;i++){sum += arr[i];}return sum;
}
③ 方法名(标识符):
- 遵循标识符的规则规范,“见名知意”;
④ 形参列表:
- 可以声明0个、1个,或者多个形参;
- 格式:数据类型 形参1,数据类型 形参2,数据类型 形参3…
我们定义方法时,该不该定义形参:
- 题目要求;
- 凭经验,具体问题具体分析;
- 代码演示:
//以下的方法也构成了方法的重载(后面会学)
public int sum(int a){return a;
}
public int sum(int a,int b){return a+b;
}
⑤ 方法体:方法功能的体现;
(4)return关键字:
① 使用范围:使用在方法体中;
② 作用:
- 结束方法;
- 针对有返回值类型的方法,使用“return 数据”方法返回所要数据;
③ 注意:
- return关键字后面不能声明执行语句;(其后的语句不可达)
(5)方法中可以调用当前类的属性和方法:
- 特殊的:方法A中又调用方法A------>递归
- 方法中,不可以定义方法。
5.3 对象数组的内存解析:
(1)举例:
① 代码如下:
class Student {String name;int state;int score;
}
public class TestStudent{Student [] stus = new Student [5];stus[0] = new Student();System.out.println(stus[0].state); //0(以下图片处有误)System.out.println(stus[1]); //nullSystem.out.println(stus[1].number); //空指针异常stus[1] = new Student();System.out.println(stus[1].number); //0}
② 内存图解如下:

5.4 匿名对象使用:
(1)概念理解:
- 理解:我们创建的对象,没有显式的赋给一个变量名。即为匿名对象;
- 特征:匿名对象只能调用一次;
(2)使用(一般使用到方法的传值):
PhoneMall p = new PhoneMall();p.show(new phone()); //匿名对象的使用
}class PhoneMall{public void show(Phone phone){phone.sendEmail();phone.playGame();}}class Phone{}
5.5 理解万事万物皆对象:
(1)理解:
① 在java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构;
- Scanner,String等;
- 文件:File
- 网络资源:URL;
② 涉及到java语言与前端HTML、后端的数据库交互时,前后端的结构在java层面交互时,都体现类、对象;
6. 再谈方法:
6.1 方法的重载(overLoad):
(1)重载的概念 :
① 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
② 两同一不同:
- 两同:同一个类,方法名不同;
- 一不同:参数列表不同(参数的个数、参数的类型);
(2)重载的特点:
- 与返回值类型、权限修饰符和方法体无关,只看参数列表,且参数列表必须不同。(参数个数或参数类 型)。调用时,根据方法参数列表的不同来区别。
(3)重载的细节:
① 判断是否构成重载:
- 和方法的权限修饰符、返回值类型、形参变量名、方法体没有关系;
② 在通过对象调用方法时,如何确定某一个指定的方法:
- 方法名————>参数列表
(4)重载示例:
注意:以下所有的方法构成重载
- ① 返回两个整数的和:
public int add(int x,int y){return x+y;}
- ② 返回三个整数的和:
public int add(int x,int y,int z){return x+y+z;
}
- ③ 返回两个小数的和:
public double add(double x,double y){return x+y;
}
6.2 可变个数形参的方法 :
(1)背景:
- ① JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变个数的实参。
- ② JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books){}
- ③ JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books){}
(2)具体使用:
- ① 声明格式:方法名 (形参类型…形参变量名){ }
- ② 说明:
- 当调用可变个数形参的方法时,传入的参数个数可以是0个,1个,2个……;
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载;
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换言之,就是二者不可以同时存在;
- 可变个数形参在方法的形参中,必须声明在末尾;
- 可变个数形参在方法的形参中,最多只能声明一个可变形参;
6.3 方法参数的值传递机制(重点):
(1)变量赋值:
- ①基本数据类型的变量,此时赋值是变量所储存的数据值;
- ②引用数据类型的变量,此时赋值是变量所储存数据的地址值;
注意:地址值类型一致时,才可以相互赋值。
- ③ 举例:
public class Test{public static void main(String [] args){int m=10;int n=m;System.out.println("m="+m+",n="+n);n=20;System.out.println("m="+m+",n="+n);Order o1= new Order();o1.m=10;o1.n=20;System.out.println("m="+o1.m+",n="+o1.n);Order o2=o1; //将o1的地址值赋给o2 ,则o1和o2两个引用对象指向同一块堆空间o2.m=20;System.out.println("m="+o1.m+",n="+o1.n); }
}class Order{int m;int n;
}
(2)方法的值传递机制:
- ① 如果参数是基本数据类型,则实参赋给形参的是实参真实的数据值;
- ② 如果参数是引用数据类型,则实参赋给形参的是实参储存数据的地址值;
- 实参:对象调用方法时,所传过来的值;
- 形参:在方法的一对小括号内定义的;
- ③ 举例:
public class Test{
public static void main(String [] args){Date date = new Date();date.m=10;date.n=20;System.out.println("m="+date.m+",n="+date.n);Test test = new Test();//基本数据类型:int sum=test.sum(3,4); //基本数据类型,传的为数据值;System.out.println(sum);//引用数据类型:test.swap(date);//date:引用数据类型,传的为地址值;System.out.println("m="+date.m+",n="+date.n);}
//两数交换的方法
public void swap(Date date) {int temp = date.m;date.m=date.n;date.n=temp;}
//求两数和的方法
public int sum(int m,int n){return m*n;
}
}
class Date{int m;int n;
}
- ③ 代码内存解析分析:
//代码如下:
public class TransferTest3 {public static void main(String args[]) {TransferTest3 test = new TransferTest3();test.first();}public void first() {int i = 5;Value v = new Value();v.i = 25;second(v, i);System.out.println(v.i);}public void second(Value v, int i) {i = 0;v.i = 20;Value val = new Value();v = val;System.out.println(v.i + " " + i);}
}
class Value {int i = 15;}

6.4 递归(recursion)方法:
(1)递归方法使用:
- 一个方法体内调用它自身。
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
(2)举例:
//举例代码如下://计算1-100之间所有自然数的和
public int sum(int num){if(num == 1){return 1;}else{return num + sum(num - 1);}
}
//计算1-n的乘积:
public int getMultiply(int n) {if(n==1) {return 1;}else {return n*getMultiply(n-1);}
}
7. 类的成员之三: 构造器(或构造方法、constructor)
7.1 构造器的特征:
- (1)它具有与类相同的名称;
- (2)它不声明返回值类型。(与声明为void不同);
- (3)不能被static、final、synchronized(同步)、abstract、native修饰,不能有return语句返回值;
7.2 构造器的作用:
- (1)创建对象(类的实例化);
- (2)给对象进行初始化;
//以下代码调用构造器创建对象,并进行对象初始化;
Order o = new Order();
Person p = new Person(“Peter”,15)
7.3 说明:
- (1)🔥构造器的定义格式:权限修饰符 类名(形参列表){ }
- 如果没有显式的定义类的构造器的话,系统默认提供一个空参的构造器;
- 一个类中定义多个构造器,构造器之间构成重载;
- 一旦我们显式定义了构造器,则系统不会提供空参的构造器;
- 每个类中至少有一个构造器;
- 一个类可以创建多个重载的构造器;
- 父类的构造器不可被子类继承;
- (2)举例:
public class Animal {int legs;
// 构造器;这些构造器之间构成重载
public Animal() {legs = 4;} public Animal(int l) {legs = l;}
//方法
public void setLegs(int i) {legs = i;}
public int getLegs() {return legs;}
}//创建Animal类的实例:Animal a = new Animal(); 调用构造器,将legs初始化为4。
- (3)根据参数不同,构造器可以分为如下两类:
- 隐式无参构造器(系统默认提供);
- 显式定义一个或多个构造器(无参、有参)
7.4 构造器重载:
构造器重载,参数列表必须不同;
- (1)构造器一般用来创建对象的同时初始化对象。
//以下的类就是有参构造器进行属性的初始化(对象初始化)
class Person{String name;int age;public Person(String n , int a){ name=n; age=a;}}
- (2)构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
//以下构造器构成重载:
public class Person{public Person(String name, int age, Date d) {this(name,age); //…}public Person(String name, int age) {//…}public Person(String name, Date d) {//…}public Person(){//…}
}
- (3)构造器重载举例:
public class Person { private String name;private int age;private Date birthDate;//以下构造器之间构成重载;public Person(String n, int a, Date d) {name = n;age = a;birthDate = d;}public Person(String n, int a) {name = n;age = a;}public Person(String n, Date d) {name = n;birthDate = d;}public Person(String n) {name = n;age = 30;}
}
7.5 总结:属性赋值过程
截止到目前,我们讲到了很多位置都可以对类的属性赋值。现总结这几个位置,并指明赋值的先后顺序。
(1)赋值的位置:
- ① 默认初始化 ;
- ② 显式初始化 ;
- ③ 构造器中初始化;
- ④ 通过“对象.属性“或“对象.方法”的方式赋值;
- 赋值的先后顺序: ① - ② - ③ - ④
7.6 拓展知识:JavaBean
- (1)JavaBean是一种Java语言写成的可重用组件:
- 类是公共的;
- 有一个无参的公共的构造器;
- 有私有属性,且有对应的get、set方法;
- (2)JavaBean举例:
public class JavaBean { //public的类private String name; // 属性一般定义为privateprivate int age;public JavaBean() { //无参构造器}//set和get方法public int getAge() { return age;}public void setAge(int a) {age = a;}public String getName() {return name;}public void setName(String n) {name = n;}}
7.7 拓展知识:UML类图

(1)+表示 public 类型, - 表示 private 类型,#表示protected类型;
(2)方法的写法:
- 方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型
8. 面向对象特征之一: 封装与隐藏
8.1 问题引入:
- 当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受属性的数据类型和储存范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能再属性声明时体现,我们只能通过方法进行限制制约的添加。(比如:setLegs())同时,我们需要避免用户在使用“对象.属性”的方式对属性进行赋值。则需要将属性声明为私有的(private)------>此时,针对属性就体现了封装性;
(1)代码演示:
//属性封装性的体现:
public class Person{private int legs; //腿的个数,设置为private,避免对象直接调用public void setLegs(int l) {if(l>=0 && l%2==0) { //检测设置的属性值是否符合要求legs=l;return;}legs=0;}public int getLegs() { //获取属性legs的值return legs; }
}
8.2 封装性体现:
(1)我们设置类的属性xxx私有化(private),同时,提供公共的(public)方法来设置(setXxx())和获取(getXxx())属性值;
(2)拓展:封装性体现:①属性私有化,②不对外暴露的方法,③单例模式…;
8.3 权限修饰符:
(1)封装性体现的条件:
- ① java规定了4种权限(从小到大):private、缺省、protected、public;
- ② 4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类;
- ③ 具体的,4种都可以用来修饰类的内部结构:属性、方法、构造器、内部类;
- ④ 修饰类的话,只能使用:缺省、public;

(2)🔥总结封装性:
- Java提供4种权限来修饰类及类的内部结构,体现类及类的内部结构在被调用时可见性的大小;
8.4 高内聚,低耦合:
(1)我们程序设计追求的:
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅对外暴露少量的方法用于使用。
- 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
9. 关键字:this的使用
9.1 this关键字理解:
- 在Java中,this关键字比较难理解,它的作用和其词义很接近。
- 它在方法内部使用,即这个方法所属对象的引用。
- 它在构造器内部使用,表示该构造器正在初始化的对象。
- this可以用来修饰和调用:属性、方法、构造器。
- 理解:
- 当前对象;
- 正在创建对象;
(1)this修饰属性或方法:
① 在类的方法中,我们可以使用“this.属性” 或“this.方法”的方式,调用当前对象属性和方法。但是,通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量为属性,而非形参;
- 代码演示:
public class Person{private String name; //私有属性public void setName(String name) {this.name=name; //this.name:调用当前类的属性name}public String getName() {return name;}
}
② 在类的构造器中,我们可以使用“this.属性” 或“this.方法”的方式,调用正在创建对象属性和方法。但是,通常情况下,我们都选择省略“this.”。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量为属性,而非形参;
- 代码演示:
public class Person{String name; //属性//构造器public Person(String name) {this.name=name;}public Person(int age) {this.age=age ;}public Person(String name,int age) {this.name=name;//this.age=age ;}
}
(2)this调用构造器:
- ① 我们在类的构造器中,可以显式的使用“this(形参列表)”方式,调用本类中指定的其他构造器;
- ② 构造器中不能使用“this(形参列表)”方式来调用自身构造器;
- ③ 如果一个类中有n个构造器,则最多有n-1个构造器中有“this(形参列表)”结构; (避免出现调用循环)
- ④ 规定:“this(形参列表)”必须声明在当前构造器的首行;
- ⑤ 构造器内部,最多只能声明一个“this(形参列表)”结构,来调用其他构造器;
//this调用构造器表示正在创建的对象
public class Person{public Person(){//this.eat(); System.out.println("Person初始化时,需要考虑如下的1,2,3,4......");//Person初始化时,需要考虑如下的1,2,3,4...... }public Person(String name) {this(); //调用Person类中的空参构造器 Person()this.name=name;}public Person(int age) {this();this.age=age ;}public Person(String name,int age) {this(age); //调用Person类中的一个有参构造器Person(String name)this.name=name;//this.age=age ;}
}
10. 关键字:package、 import的使用
10.1 关键字: package
(1)package关键字解释:
① package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。
② 它的格式为:
package 顶层包名.子包名 ;
- 举例:
package com.athuawei.java2; //类所在包的包名(全类名)public class PackageTest {public static void main(String[] args) { }
}
③ 包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次;
④ 包通常用小写单词标识。通常使用所在公司域名的倒置:com.atguigu.xxx;
(2)包的作用:
- 包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
- 包可以包含类和子包,划分项目层次,便于管理。
- 解决类命名冲突的问题。
- 控制访问权限。
- 为了更好的实现项目中类的管理,提供包的概念。
- 使用package声明类或接口所属的包,声明在源文件的首行。
- 包,属于标识符,遵守标识符的命名规则、规范(xxxyyyyzzz)、“见名知意”。
- 每“.”一次,就代表一层文件目录。
① 补充:
- 同一个包下,不能命名同名的类和接口;
- 不同的包下,可以命名同名的类、接口;
(3)包的应用:MVC设计模式:
- MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,与 数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
① 模型层 model 主要处理数据:
数据对象封装 model.bean/domain
数据库操作类 model.dao
数据库 model.db
② 视图层 view 显示数据:
- 相关工具类 view.utils
- 自定义view view.ui
③ 控制层 controller 处理业务逻辑:
- 应用界面相关 controller.activity
- 存放fragment controller.fragment
- 显示列表的适配器 controller.adapter
- 服务相关的 controller.service
- 抽取的基类 controller.base

(5)JDK中主要的包介绍:
- java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和 Thread,提供常用功能 。
- java.net----包含执行与网络相关的操作的类和接口。
- java.io----包含能提供多种输入/输出功能的类。
- java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日 期日历相关的函数。
- java.text----包含了一些java格式化相关的类 。
- java.sql----包含了java进行JDBC数据库编程的相关类/接口。
- java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这 些类被用来构建和管理应用程序的图形用户界面(GUI)。 B/S C/S。
10.2 关键字:import
(1)import关键字理解:
- 为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类( . *)。import语句告诉编译器到哪里去寻找类。
(2)import关键字使用:
① 格式:import 包名.类名
- 在源文件中显式的使用import结构导入指定包下的类、接口;
- 声明在包声明和类声明之间;
- 如果需要导入多个结构,则并列写出即可;
- 可以使用“xxx.*”的方式,表示可以导入xxx包下的所有结构;
- 如果使用的类和接口是本包下定义的,则可以省略import结构;
- 如果使用的类和接口是java.lang包下定义的,则可以省略import结构;
- 如果在源文件中,使用不同包下的同名的类,则必须至少有一个类需要时全类名的方式显式;
- 使用“xxx.*”方式可以导入包内所有的类和结构,但是并不能导入此包子包下的结构;
② 举例:
import com.athuawei.exer4.Customer;
import com.athuawei.exer4.Account;
import com.athuawei.exer4.Bank;
//以下省略其他包下的类声明
public class Test{
public static void main(String [] args){Bank bank = new Bank();ArrayList list = new ArrayList();HashMap map = new HashMap();Customer cust1= new Customer(); //Customer是com.athuawei.exer4包下的类Account accout1= new Account(); // Account是com.athuawei.exer4包下的类}
}
③ import static:导入指定类或接口中的静态结构:属性、方法;
- 举例:
import static java.lang.System.out;
import static java.lang.Math.random;
public static void main(String [] args){ //某个类中的main方法out.println(); //out为System类中的静态(static)属性 // import static java.lang.System.out;random(); //random()是Math类下的静态方法; // import static:import static java.lang.Math.random;
}
五、面向对象(中)
1. 面相对象特征之二:继承性
1.1 继承性的图解:

1.2 继承性的理解:
(1)为什么要有继承性:
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
1.3 继承性的作用:
- 继承的出现减少了代码冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
- 注意:不要仅为了获取其他类中某个功能而去继承。
1.4 继承性的格式:
(1)继承格式说明:
class Student extends Person{}
- Student:子类、派生类、subclass;
- Person:父类、超类、基类、superclass;
(2)继承的体现:
- 一旦子类A继承父类B之后,子类A就获取了父类B中声明的结构:属性、方法;
- 特别的:父类中声明为私有的(private)属性和方法,子类继承父类以后,仍然认为子类获取了父类的结构,只是因为权限修饰符的封装性影响,使得子类不能直接调用继承父类私有的属性和方法;
(3)子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的拓展。子类和父类的关系,不同于子集和集合的关系;
(4)在Java 中,继承的关键字用的是extends,即子类不是父类的子集, 而是对父类的扩展。
1.5 Java中关于继承性的规定:
(1)一个类可以被多个类给继承;
(2)子父类是相对的概念;
(3)子类直接继承的父类:直接父类;子类间接继承的父类:间接父类;
(4)子类继承父类以后,就获取直接父类和所有间接父类的属性和方法;
(5)子类不能直接访问父类中私有的(private)的成员变量和方法。
(6)Java只支持单继承和多层继承,不允许多重继承(一个类只能有一个父类);
- ①一个子类只能有一个父类
- ② 一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok class SubDemo extends Demo1,Demo2...//error

(7)注意:
- ① 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类;
- ② 所有的Java类(除java.lang.Object)都直接或者间接继承于java.lang.Object类;、
- ③意味着,所有的java类都具有java.lang.Object类声明的功能;
2. 方法的重写 (override/overwrite)
2.1 方法重写的定义:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
2.2 方法重写作用:
重写以后,当创建子类对象后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法;
2.3 方法重写规定:
方法声明:权限修饰 返回值类型 方法名(形参列表) throws 异常类型 {方法体}约定俗称:子类中的叫做重写方法,父类中的叫做被重写方法;
(1)子类重写方法的方法名和形参列表与父类中被重写方法的方法名和形参列表相同;
(2)子类重写的方法的权限修饰符不小于父类中被重写方法的权限修饰符; 特殊情况:子类不能重写父类中声明为private权限方法;
(3)返回值类型:
- 父类中被重写方法返回值类型是void,则子类重写方法的返回值类型也是void;
- 父类被重写方法返回值类型是A类型,则子类重写的方法返回值类型可以是A类或者A类的子类;
- 父类被重写方法返回值类型如果是基本数据类型,则子类重写的方法返回值类型必须是相同的基本数据类型;
(4)子类重写方法抛出的异常类型不大于父类被重写方法抛出的异常类型;(异常处理)
🔥注意:
- 子类和父类中的同名同参数方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
3. 四种访问权限修饰符:
理解:
- Java权限修饰符public、protected、 (缺省)、 private置于类的成员定义前,用来限定对象对该类成员的访问权限。

(1)对于class的权限修饰只可以用public和default(缺省)。
- public类可以在任意地方被访问。
- default类只可以被同一个包内部的类访问。
(2)总结:
- 同一包中的其他类,不可以调用Order类中的私有属性、方法;
- 在不同包下的子类不能调用父类中私有的和缺省的属性和方法;
- 不同包下的普通类(非子类)要使用Order类,不可以调用private、缺省、protected权限的属性和方法;
4.关键字:super
4.1 super关键字的使用:
(1)super关键字理解:
- super:父类的;
- super可以用来调用:属性、方法、构造器;
(2)super关键调用属性和方法:
- 我们可以在子类的方法构造器中,通过“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性和方法;但是,通常情况下,我们省略“super.”
- 代码演示:
//由父类和子类中都有属性id,子类继承了父类的name、age属性
public void show() {System.out.println("name:"+this.name+",age:"+super.age+",major:"+major);System.out.println("id="+this.id);System.out.println("id="+super.id);}
- 特殊情况1:
- 当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中声明的属性。
- 特殊情况2:
- 当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式“super.方法名( )”的方式,表明调用的是父类中被重写的方法;
//由子类重写了父类中的eat()方法:@Overridepublic void eat() { //由此重写父类中方法System.out.println("学生:多吃有营养的东西");}public void show() { super.eat();}
(3)super关键调用构造器:
- 我们可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中声明指定的构造器;
- “super(形参列表)”的使用,必须声明在子类构造器的首行;
- 我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现;
- 在构造器的首行,没有显式的声明“this(形参列表)”或“super(形参列表)”,则系统默认调用的是父类中空参的构造器:super();
- 在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器;
- 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
//Person父类和Students子类声明省略
public Students(String name,int age,String major) {
// this.name=name;
// this.age=age;super(name,age); //调用Students子类的Person父类的两个参数构造器;this.major=major;}public Students(){super(); //调用Students子类的Person父类的无参构造器;
}
(4)super和this关键字的区别:

5. 子类对象实例化过程:
5.1 对象的内存图解:
Dog dog = new Dog("小花","小红"); //实例化Dog类

5.2 子类对象实例化过程理解:
(1)从结果上来看:(继承性)
- 子类继承父类以后,就获取了父类中声明的属性和方法。
- 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性;
(2)从过程上来看:
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或者间接的调用其父类的构造器,进而调用父类的父类构造器……;直到调用java.lang.Object类中空惨的构造器为止。正因为加载过所有的父类结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
- 明确:虽然创建子类对象时,调用了父类的构造器,但是至始至终就创建了一个对象,即为new的子类对象;
6.🔥面向对象特征之三: 多态性
6.1 多态性理解:
(1)多态性:
是面向对象中最重要的概念,在Java中的体现:
①对象的多态性:父类的引用指向子类的对象
- 可以直接应用在抽象类和接口上
② Java引用变量有两个类型:编译时类型和运行时类型。- 编译时类型由声明该变量时使用的类型决定。
- 运行时类型由实际赋给该变量的对象决定。
- 简 称:编译时,看左边;运行时,看右边。
①若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
②多态情况下
- “看左边” :看的是父类的引用(父类中不具备子类特有的方法)
- “看右边” :看的是子类的对象(实际运行的是子类重写父类的方法)
(2)对象的多态:
①在Java中,子类的对象可以替代父类的对象使用:
- 一个变量只能有一种确定的数据类型 ;
- 一个引用类型变量可能指向(引用)多种不同类型的对象;
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
② 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
③一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法.
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
- 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
(3)多态性应用案例:
- 代码演示:
public class Test {
public void method(Person e) { //由Student类的父类为Person类
// ……e.getInfo();}
public static void main(Stirng args[]) {Test t = new Test();Student m = new Student();t.method(m); // 子类的对象m传送给父类类型的参数e}
}
//举例二:
public static void main(String[] args) {AnimalTest test = new AnimalTest();test.function(new Dog());//Animal animal=new Dog()test.function(new Cat());//Animal animal=new Cat()}public void function(Animal animal) {animal.eat();animal.shout();}//举例三:
class Order{public void method(Object obj) {}}
//举例四:
class Driver{public void doData(Connection conn) { //conn= new MySQLConnection() // conn= new OracleConnectin();//规范的步骤操作数据:// conn.method1();
// conn.method2();}
}
(4)虚拟方法调用(Virtual Method Invocation):
①正常的方法调用:
//Person类是Student类的父类
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
②虚拟方法调用(多态情况下):
- 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法;
- 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
- 🔥多态的使用:虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
- 总结:编译看左边,运行看右边;
③编译时类型和运行时类型:
动态绑定:
- 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。
④虚拟方法调用举例:

- 前提:
Person类中定义了welcome()方法,各个子类重写了welcome()。
- 执行:
多态的情况下,调用对象的welcome()方法, 实际执行的是子类重写的方法。
6.2 多态性总结:
(1)多态作用:
- 提高了代码的通用性,常称作接口重用;
(2)多态前提:
- 需要存在继承或者实现关系;
- 有方法的重写;
(3)成员方法:
- 编译时:要查看引用变量所声明的类中是否有所调用的方法。
- 运行时:调用实际new的对象所属的类中的重写方法。
(4)成员变量:
- 不具备多态性,只看引用变量所声明的类。
6.3 对象向下转型:使用强制类型转换符
(1)背景:基本数据类型的转换(Casting):
- 自动类型转换:小的数据类型可以自动转换成大的数据类型
long g=20; double d=12.0f
- 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
float f=(float)12.0; int a=(int)1200L
(2)问题引入:
- ①有了对象的多态性以后,内存中实际上是加载了子类持有的属性和方法,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用;
- ②如何才能调用子类特有的属性和方法?
————>向下转型:使用强制类型转换符
(3)转型格式:
子类名 子类对象名 = (子类名)父类对象名; 强转符:( )
class Person{ }
class Man extends Person{}
class PersonTest{Person p = new Man(); //多态体现(向上转型)Man m = (Man) p; //向下转型
}
①对Java对象的强制类型转换称为造型:
- 从子类到父类的类型转换可以自动进行;
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现;
- 无继承关系的引用类型间的转换是非法的;
- 在造型前可以使用instanceof操作符测试一个对象的类型;

②强转带来的异常:
ClassCastException ----->可能出现的异常
- 举例说明:
class Person{ }class Man extends Person{}class Woman extends Person{ }class PersonTest{Person p = new Man(); //多态体现(向上转型)Man m = (Man) p; //向下转型Woman w = (Woman) p; //运行时异常:ClassCastException 因对象类型不匹配
}
(4)instanceof关键字使用:
- ①关键字解释:
a instanceof A :判断对象是否是类的实例。如果是,返回true,如果不是,返回false
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
- 如果x属于类A的子类B对象,x instanceof A值也为true。
- ②关键字使用情景:
为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型;
- ③使用拓展:
如果a instanceof A返回true,则a instanceof B 也返回true。
———–>其中,类B是类A的父类;
- ④举例说明:
//前提:Person类是Man和Woman两个类的父类;Person p2= new Man();
if(p2 instanceof Woman) { Woman m2 = (Woman)p2;m2.goShopping();System.out.println("*******Woman**********");}
if(p2 instanceof Man) {Man m3 = (Man)p2;System.out.println("*******Man**********");}
if(p2 instanceof Person) {System.out.println("*******Person**********");}
if(p2 instanceof Object) {System.out.println("*******Object**********");}//结果输出:*******Man**********// *******Person**********//*******Object**********
- ⑤向下转型练习:
//前提:Man 和Woman类是Person类的子类//问题一:编译时通过,运行时不通过//举例一:Person p2 = new Man(); Woman w = (Woman)p2;//ClassCastException//举例二:Person p3 = new Person();Man m1 = (Man)p3;//ClassCastException//问题二:编译时通过,运行时通过Man m = (Man)p2;Object o = new Man();Person p = (Person)o;Man m2 = (Man)o;//问题三:编译不通过:Woman w1 = new Man();
(5)子父类多态总结:
- ① 子类继承父类
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。---->编译看左边,运行看右边
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。----->编译和运行都看左边;
- ② 代码演示:
class Base {int count = 10;public void display() {System.out.println(this.count);}
}
class Sub extends Base {int count = 20;public void display() { //重写父类的方法System.out.println(this.count);}
}
public class FieldMethodTest {public static void main(String[] args) {Sub s = new Sub();System.out.println(s.count); // 20s.display(); // 20Base b = s;// ==:对于引用数据类型,比较的是两个引用数据类型变量的地址值是否相同;System.out.println(b == s); // trueSystem.out.println(b.count);// 10b.display();// 20 虚拟方法的调用(调用父类的方法,运行子类中重写方法)}
}
7. Object类的使用:
7.1:Object类的解释:
(1)Object类是所有Java类的根父类。
(2)如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类。
① 举例:
public class Person {
...
}
//等价于:
public class Person extends Object {
...
}//例:
method(Object obj){…} //可以接收任何类作为其参数
Person o=new Person();
method(o);
7.2:Object类中的主要结构:
(1)属性:无
(2)方法:
equals()/toString()/getClasss()/hashCode()/clone()/finalize()/wait()/notify()/notifyAll()

7.3:== 和 equals()区别:
(1)回顾==的使用:
① ==:比较运算符
②可以使用在基本数据类型变量和引用数据类型变量中。
基本数据类型:
- 比较的是两个变量保存的数据是否相等。(不要求是同类型的变量)
引用数据类型:
- 比较的是两个对象的地址值是否相等。即两个引用是否指向同一个对象实体;
补充:
- = = 符号使用时,必须保证符号两边的变量类型一致(数值型、字符型、布尔型、字符串型);
- 代码演示:
int j=1;double m=2;int i=2;System.out.println(i==j); //falseSystem.out.println(m==i); //trueboolean b=true;// System.out.println(m==b);//引用数据类型Students s = new Students("Tom",23);Students s1 = new Students("Tom",23);System.out.println(s==s1); //falseString str1 = new String("Tom");String str2 = new String("Tom");System.out.println(str1==str2); //false
(2)equals()方法使用:
① equals()说明:
所有类都继承了Object,也就获得了equals()方法。还可以重写。
只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象(格式:obj1.equals(obj2))。
② equals()方法的再次说明:
- 是一个方法,而非运算符;
- 只能适用于引用数据类型;
- Object类中equals()的定义:
public boolean equals(Object obj){return (this==obj);}
Object类中定义的equals()和==的作用是相同的:比较的是两个对象的地址值是否相同。即两个引用是否指向同一个对象实体;
③特殊情况:
像String、Date、File、包装类都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同。
④ equals()方法使用:
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同,那么,我们就需对Object中的equals()方法进行重写;
⑤重写原则:比较两个对象的实体内容(即属性)是否相同;
- 对称性:如果x.equals(y)返回是“true” ,那么y.equals(x)也应该返回是 “true”。
- 自反性:x.equals(x)必须返回是“true”。
- 传递性:如果x.equals(y)返回是“true” ,而且y.equals(z)返回是“true” , 那么z.equals(x)也应该返回是“true”。
- 一致性:如果x.equals(y)返回是“true” ,只要x和y内容一直不变,不管你 重复x.equals(y)多少次,返回都是“true”。
- 任何情况下,x.equals(null),永远返回是“false” ; x.equals(和x不同类型的对象)永远返回是“false”。
⑥代码演示:
//像String、Date、File、包装类都重写了Object类中的equals()方法。
class Students{@overridepublic boolean equals(Object obj){}
}
Students s = new Students("Tom",23);
Students s1 = new Students("Tom",23);
System.out.println(s.equals(s1)); //false---->trueString str1 = new String("Tom");
String str2 = new String("Tom");
System.out.println(str1.equals(str2)); //trueDate date1 = new Date(234444244L);
Date date2 = new Date(234444244L);
System.out.println(date1==date2);//false
System.out.println(date1.equals(date2)); //true
(3)== 和equals()两者区别:
- ① ==既可以比较基本数据类型,也可以比较引用数据类型;对于基本数据类型比较的是数据值,对于引用数据类型比较的是内存地址值;
- ② equals的话,它是属于java.lang.Object类里面的方法,只针对引用数据类型而言;如果该方法没有被重写过默认也是比较内存地址值,如果被重写比较的就是变量的“实体内容”(属性);
7.4:toString()方法的使用:
(1)toString()方法理解:
- ① toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。
- ② java.lang.Object类中定义的toString()方法格式:
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
- ③ 在进行String与其它类型数据的连接操作时,自动调用toString()方法:
Date now=new Date();
System.out.println(“now=”+now);
//相当于
System.out.println(“now=”+now.toString());
- ④ 像String、Date、File、包装类等重写了Object类中的toString()方法。使得在调用对象的toString()时,返回“实体内容”信息;
//举例说明:
Date d = new Date(23332L);
System.out.println(d); //Thu Jan 01 08:00:23 CST 1970
System.out.println(d.toString()); //Thu Jan 01 08:00:23 CST 1970String str = "name";
Systme.out.println(str); //name //等价于sysout(str.toString());
- ⑥ 基本类型数据转换为String类型时,调用了对应包装类的toString()方法。
int a=10;
System.out.println(“a=”+a); //自动装箱
8.包装类的使用:
8.1:包装类解释:
- (1)针对八种基本数据类型定义相应的引用类型–—>包装类(封装类)
- (2)有了类的特点,就可以调用类中的方法,Java才是真正的面向对象;

8.2:包装类需要考虑的问题:
(1) 装箱:基本数据类型包装成包装类的实例:
- ① 通过包装类构造器实现:
int i =4;
Integer in = new Integer(i);float f = 12.3f;
Float fl = new Float(f);
- ②通过字符串参数构造包装类对象:
Integer in =new Integer("1234");//错误
//Integer in1 = new Integer("123aa"); ///NumberFormatException
(2)拆箱:获得包装类对象中包装的基本类型变量
- ①调用包装类的.xxxValue()方法:
Integer in1= new Integer(12);
int i1= in1.intValue();Float fl = new Float(12.2f);
float f = fl.floatValue();
- ② 总结:
JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
//代码演示://基本数据类型转为包装类:自动装箱
int i=1;
Integer in =i;//包装类转为基本数据类型:自动拆箱
Integer in1= new Integer(123);
int i =in1;
(3)字符串(String)——>基本数据类型、包装类:
- ①通过包装类的构造器实现:
int i = new Integer("1234");
float f= new Float("12.9f");
- 🔥②通过包装类的parseXxx(String s)静态方法:
int i =Integer.parseInteger("1234");
float f = Float.parseFloat("12.4f");
char c = Character.parseCharacter("true");
(4)基本数据类型、包装类——–>字符串(Str):
- 🔥①调用字符串重载的valueOf()方法:
String s = String.valueOf(123);
String s1 = String.valueOf(12.3);
- ②更直接的方式:
int i =2;
String s = i +"";
(5)总结:基本类型、包装类与String类间的转换:

8.3:包装类的用法举例:
int i = 500;
Integer t = new Integer(i);
String s = t.toString(); // s = “500“,t是类,有toString方法
String s1 = Integer.toString(314); // s1= “314“ 将数字转换成字符串。
String s2=“4.56”;
double ds=Double.parseDouble(s2); //将字符串转换成数字//拆箱:将数字包装类中内容变为基本数据类型。
int j = t.intValue(); // j = 500,intValue取出包装类中的数据//包装类在实际开发中用的最多的在于字符串变为基本数据类型。
String str1 = "30" ;
String str2 = "30.3" ;
int x = Integer.parseInt(str1) ; // 将字符串变为int型
float f = Float.parseFloat(str2) ; // 将字符串变为int型//将基本数据类型转为String类型;
int i=2;
String str3 = String.valueOf(i); //"2"
注意:
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //falseInteger m = 1;
Integer n = 1;
System.out.println(m == n);// trueInteger x = 128; //相当于new了一个Integer对象;
Integer y = 128;//相当于new了一个Integer对象;
System.out.println(x == y);//false
- 上述结果的原因:(int类型数据缓存)
🔥Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用去new了。目的:提高效率;
9. java中JUnit单元测试:
9.1: 使用步骤:
- 选中当前工程-右键选择:build path -add libraries - JUnit 4-下一步
- 创建java类(此类是public的 ;此类提供公共的无参构造器;),进行单元测试。
- 此类中声明单元测试方法:此时的单元测试方法:方法的权限是public,没有返回值,没有形参;
- 此单元测试方法上需要声明注解:@Test;并在单元测试类中导入:import org.junit.Test;
- 声明好单元测试方法以后,就可以在方法体内测试相关的代码;
- 写完代码以后,左键双击单元测试方法名,右键;run as - JUnit Test
(1)举例:
public class InterviewTest {@Testpublic void test1() {Object o1 = true ? new Integer(1) : new Double(2.0);System.out.println(o1); //1.0}@Testpublic void test2() {Object o2;if (true)o2 = new Integer(1);elseo2 = new Double(2.0);System.out.println(o2);//1}
}
9.2 使用说明:
- 如果执行结果没有任何异常:绿条
- 如果执行结果出现异常:红条
六、面向对象(下)
1.关键字:static(静态的)
1.1 static关键字使用:
(1)static修饰的结构:
属性、方法、代码块、内部类
1.2 static修饰属性:静态变量(类变量)
(1) 静态属性与非静态属性(实例变量):
- 按是否使用static修饰:
- ①实例变量:我们创建了类的多个对象,每个对象都有一套类中的非静态属性。当修改其中一个对象的非静态变量时,不会导致其他对象中同样的属性值的修改;
- ②静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,修改过了的;
- 举例说明:
class Chinese{static String nation=“中国”;String name;int age;public static void main(String []args){//c1和c2各有一套name和age属性,互不影响;而共用nation属性;Chinese c1 = new Chinese(); Chinese c2 = new Chinese();}
}
(2) static修饰属性的其他说明:
- 🔥静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用;
- 静态变量加载要早于对象的创建;
- 由于类只会加载一次,则静态变量在内存中也只存在一份,存在方法区的静态域中;
(3) 表格说明静态变量和非静态变量调用:
| 变量类型 | 类变量 | 实例变量 |
|---|---|---|
| 类名 | yes | no |
| 对象名 | yes | yes |
- ② 举例说明:
class Chinese{static String nation=“中国”;String name;int age;public static void main(String []args){Chinese c1 = new Chinese(); Chinese c2 = new Chinese();//c1和c2各有一套name和age属性,互不影响;而共用nation属性;c1.nation;//对象调用静态变量c1.name; //对象调用实例变量system.out.println(Chinese.nation); //类名调用静态变量}
}
(4)静态属性举例:
System.out , Math.PI
(5) 类变量和实例变量的内存解析:
- ①代码演示:
public class Test(){public static void main(String [] args){Chinese.nation = "中国";Chinese c1 = new Chinese();c1.name = "姚明";c1.age = 40;Chinese c2 = new Chinese();c2.name = "马龙";c2.age = 30;c1.nation = "CHN";c2.nation = "CHINA";}
} class Chinese{String name;int age;static String nation;
}
- ② 内存解析:

1.3:使用static修饰方法:静态方法(类方法)
(1)类方法的说明:
- ①没有对象的实例时,可以用“类名.方法名()”的形式访问由static修饰的类方法。
- ②在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
- ③static修饰的方法不能被重写。
④表格说明:
| 调用结构 | 静态方法 | 非静态方法 |
|---|---|---|
| 类名 | yes | no |
| 对象名 | yes | yes |
④代码说明:
class Person {private int id;private static int total = 0;public static int getTotalPerson() { //static修饰方法:静态方法//id++; //非法return total;}public Person() { //无参构造器total++;id = total;}
}
//测试类
public class PersonTest {
public static void main(String[] args) {System.out.println("Number of total is " + Person.getTotalPerson()); //类名直接调用静态方法//没有创建对象也可以访问静态方法Person p1 = new Person();System.out.println( "Number of total is "+ p1.getTotalPerson()); //创建对象,对象名调用静态方法}
}
1.4:关于static关键字的注意点:
- (1)在静态方法中,不能使用this和super关键字;
- (2)关于静态属性和静态方法的使用,从生命周期的角度去理解(静态的结构随着类的加载而加载);
1.5 :如何确定属性和方法要声明为static:
(1)确定一个属性声明为static:
- ①属性可以被多个对象所共享的,不会随着对象的不同而不同;
- ② 类中的常量也常常声明为static;
(2)确定一个方法声明为static的:
- ① 操作静态属性的方法,通常设置为static的;
- ② 工具类中的方法,习惯上声明为static的。比如:Math、Arrays、Collections
1.6 单例(Singleton)设计模式:
(1)设计模式理解:
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。
(2)单例设计模式解释:
① 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
②创建单例模式的类步骤:
- ①私有化(private)类的构造器;
- ②类内部创建对象:要求此对象必须是static的
- ③提供公共的静态的方法,返回类的对象;
(3)单例模式的两种方式:饿汉式和懒汉式
- ①饿汉式:
public class Bank{//1.私有化构造器private Bank(){}//2.创建私有静态的类对象private static Bank instance = new Bank(); //3.创建静态方法获取该静态对象public static Bank getInstance(){return instance;}
}
饿汉式的优缺点:
- 坏处:对象加载的时间过长;
- 好处:饿汉式是线程安全;
- ②懒汉式(存在线程安全问题):
public class Bank{//1.私有化构造器private Bank(){}//2.创建私有静态的Bank,并不实例化private static Bank instance = null; //3.创建静态方法获取该静态对象public static Bank getInstance(){if(instance==null){instance = new Bank();}return instance;}
}
懒汉式的优缺点:
- 好处:延迟对象的创建;
- 目前坏处:线程不安全;------->到多线程内容时,再修改;
(4)单例模式的优点:
- ① 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可 以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
(5)单例模式应用场景:
- ① 网站的计数器,一般也是单例模式实现,否则难以同步。
- ② 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- ③ 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- ④ 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
- ⑤ Application 也是单例的典型应用。
- ⑥ Windows的**Task Manager (任务管理器)**就是很典型的单例模式 。
- ⑦ Windows的**Recycle Bin (回收站)**也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
2 理解main方法的语法:
第二部分:Java高级编程
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

