Java学习笔记----第一部分:java基础编程(尚硅谷宋红康老师)

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文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法。

网址: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)基本数据类型:
  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”

  1. 字符型:
  • 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 的实现方式。
  1. 布尔型:

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)引用数据类型:
  1. 类:class

字符串数据类型:String

  • 使用方式与基本数据类型一致。例如:String str = “abcd”( 或String str = new String(“abcd”); );
  • 一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据;其连接得到的结果都为字符串类型。
  • 举例:
//其他数据类型变量与字符串类型变量进行连接时,其结果都为字符串;
String s1="str";
String s2="ing";

String s3=s1+s2;  
System.out.println(s3); //string

int i1=33;
String s4=s1+i1;
System.out.println(s4); //str33

注意:String类型变量与其它八种基本数据类型进行“+”操作,表示连接;

  1. 接口:interface(其后会介绍)
//接口的定义方式
public interface Test{ }
//接口需要其他类来实现(略)

  1. 数组:[ ](其后会介绍)
//一维数组的声明方式:
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型的12

byte 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);  //11

int 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<yanghui[i].length;j++){
 		   		if(j==0 || i==j){
 		   			yanghui[i][j] = 1;  //给首末尾元素赋值;
 		   		}else{
 		   			yanghui[i][j]=yanghui[i-1][j-1]+yanghui[i-1][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 ; //声明私有变量 age
    public 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(); //建立对象c1
    Car c2 = new Car(); //建立对象c2
    c1.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) { //声明方法setAge
    age = 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]);  //null
	System.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; // 属性一般定义为private
		private 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关键字理解:
  1. 在Java中,this关键字比较难理解,它的作用和其词义很接近。
  2. 它在方法内部使用,即这个方法所属对象的引用。
  3. 它在构造器内部使用,表示该构造器正在初始化的对象。
  4. this可以用来修饰和调用:属性、方法、构造器。
  5. 理解:
  • 当前对象;
  • 正在创建对象;
(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()方法:
    @Override
	public 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); // 20
		s.display(); // 20
		Base b = s;
		// ==:对于引用数据类型,比较的是两个引用数据类型变量的地址值是否相同;
		System.out.println(b == s); // true
		System.out.println(b.count);// 10
		b.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); //false
	System.out.println(m==i); //true

    boolean b=true;
    //		System.out.println(m==b);
		
	//引用数据类型
	Students s = new Students("Tom",23);
	Students s1 = new Students("Tom",23);
	System.out.println(s==s1); //false

	String 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{
  @override
  public boolean equals(Object obj){
  }
}
Students s = new Students("Tom",23);
Students s1 = new Students("Tom",23);
System.out.println(s.equals(s1)); //false---->true

String str1 = new String("Tom");
String str2 = new String("Tom");
System.out.println(str1.equals(str2)); //true

Date 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 1970

String 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);  //false

Integer m = 1;
Integer n = 1;
System.out.println(m == n);// true
		
Integer 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 {
	@Test
	public void test1() {
      Object o1 = true ? new Integer(1) : new Double(2.0);
      System.out.println(o1); //1.0
	}
	@Test
	public void test2() {
      Object o2;
      if (true)
      o2 = new Integer(1);
      else
      o2 = 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) 表格说明静态变量和非静态变量调用:
变量类型类变量实例变量
类名yesno
对象名yesyes
  • ② 举例说明
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修饰的方法不能被重写。

④表格说明:

调用结构静态方法非静态方法
类名yesno
对象名yesyes

④代码说明:

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方法的语法:

2.1 main()方法的使用说明:
(1)使用说明(开发工具eclipse中说明):
  • main()方法作为程序的入口;
  • main()方法也是一个普通的static(静态的)方法;
  • main()方法可以作为我们与控制台的交互方法(之前,使用Scanner);

在这里插入图片描述

  • 注意:因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
(2)命令行参数用法举例:
	public class CommandPara {
	  public static void main(String[] args) {
		  for (int i = 0; i < args.length; i++) {
		  	System.out.println("args[" + i + "] = " + args[i]);
				}
			}
	}
	
	//运行程序CommandPara.java
	java CommandParaTom" “Jerry"Shkstart"
	//运行结果:
	/*
	args[0] = Tom
	args[1] = Jerry
	args[2] = Shkstart
	*/ 

3 类的成员之四: 代码块(初始化块)

3.1 代码块的解释:
(1)代码块作用:
  • 对Java类或对象进行初始化;
  • 注意:静态的代码块是随着类的加载而加载的,这就是为什么静态方法中不能使用非静态结构(非静态结构随着对象的创建而被加载);
(2)代码块(或初始化块)的分类:
  • 静态代码块和非静态代码块
  • 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块 (static block),没有使用static修饰的,为非静态代码块。
  • 注意:代码块的加载要早于构造器的加载;
3.2 代码块的使用:
(1)静态代码块:用static 修饰的代码块
  • 可以有输出语句。
  • 可以对类的属性、类的声明进行初始化操作。
  • 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  • 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  • 静态代码块的执行要先于非静态代码块。
  • 静态代码块随着类的加载而执行,且只执行一次。
  • 🔥代码说明
public class Test{
	public String name;
		//静态的代码块
		static {
			//name="tom";  //非法:非静态的属性
			//eat();  //非法:非静态的方法
			info();  //合法:info方法为静态
			System.out.println("hello,static block_1");
		}
	public static void info(){}
	public void eat(){}
}
(2)非静态代码块:没有static修饰的代码块
  • 可以有输出语句。
  • 可以对类的属性、类的声明进行初始化操作。
  • 除了调用非静态的结构外,还可以调用静态的变量或方法。
  • 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  • 每次创建对象的时候,都会执行一次。且先于构造器执行。
  • 🔥代码说明
public class Test2{
	public String name;
		//非静态的代码块
		{
			//调用非静态结构
			name="tom";  
			eat(); 
			
			//调用静态结构
			info();  
			System.out.println("hello,static block_1");
		}
	public static void info(){}
	public void eat(){}
}
(3)对属性可以赋值的位置:
  • ①默认初始化;

  • ②显式初始化/⑤在代码块中赋值;(赋值谁先谁后看顺序)

  • ③构造器初始化;

  • ④有了对象以后,可以通过“对象.属性” 或者“对象.方法”的方式,进行赋值;

  • 赋值(结构)顺序:①——②| ⑤——③——④

  • 代码说明
public class OrderTest {
	public static void main(String[] args) {
		int age=2;
		{
			age=3;
		}
		System.out.println(age);  //3
    
	    {
	      num=3;
	    }
	    int num=4;
	    System.out.println(num);  //4
	}
}
  • 🔥类中结构执行顺序的总结
  • 由父及子,静态先行;

4 关键字:final(最终的)

4.1:final关键字解释:
  • (1)在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”

① final标记的类不能被继承。提高安全性,提高程序的可读性

  • String类、System类、StringBuffer类
  • final标记的方法不能被子类重写。
  • 比如:Object类中的getClass()。

final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次

final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用

  • final double MY_PI = 3.14;
4.2 final关键字修饰结构:
(1)final修饰类:
  • 表示最终的类,不可以被继承;
	final class Person{
	  
	}
	//class Student extends Person{  //{Person类不能被继承
	  
	//}
(2) final修饰方法:
  • 表示最终的方法,不能被重写;
	class A {
	public final void print() {
		System.out.println("A");
		}
	}
	class B extends A {
	 // public void print() { // 错误,不能被重写。
	  //System.out.println("尚硅谷");
	//	}
	}
(3)final修饰变量(属性和局部变量)——常量
  • 表示该变量为常量,只能被赋值一次;
class A {
	  private final String INFO = "atguigu"; //声明常量
	  public void print() {
		  //The final field A.INFO cannot be assigned
		  //INFO = "尚硅谷";
	  }
  
  	  public void show(final int num) { //局部变量:形参
	  		//		num=23;  //编译不通过
			System.out.println(num);
		}
}
  • 注意点常量名要大写,内容不可修改。

🔥final修饰属性

  • 可以考虑赋值的位置有:显式初始化、代码块初始化、构造器中初始化;

🔥fianl 修饰局部变量

  • 尤其是使用final修饰形参时,表明形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值;
(4)关键字final应用举例:
public final class Test {
 	public static int totalNumber = 5;
    public final int ID;
    public Test() {
    	ID = ++totalNumber; // 可在构造器中给final修饰的“变量”赋值
     }
    public static void main(String[] args) {
	    Test t = new Test();
	    System.out.println(t.ID);
	    final int I = 10;
	    final int J;
	    J = 20;
	    J = 30; // 非法
   }
}
(5)static final :用来修饰属性:全局常量

5 抽象类与抽象方法:

5.1 抽象(abstract)概念引入:
  • 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一 般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
5.2 抽象类和方法说明:
(1)abstract修饰类:抽象类
  • 用abstract关键字来修饰一个类,这个类叫做抽象类。
  • ②抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
  • ③抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程);
(2)abstract修饰方法:抽象方法

①抽象方法只有方法的声明,没有方法体;

用abstract来修饰一个方法,该方法叫做抽象方法。

  • 抽象方法:只有方法的声明,没有方法的实现。(没有方法体)
  • 以分号结束: 比如:public abstract void talk();

③含有抽象方法的类必须被声明为抽象类。

(4)抽象类和方法的举例:
public abstract  class Person{  //抽象类
  public abstract void eat();  //抽象方法
}

class Student extends Person{
  public void eat(){ //子类实现父类的抽象方法
    sysout();
  }
}
(5)abstract使用注意:
  • ①不能用abstract修饰变量、代码块、构造器;
  • ②不能用abstract修饰私有方法、静态方法、final的方法、final的类。
  • ③ 代码说明:
public abstract class AbstractTest1 {
  //以下都不合法:
//  public abstract String name;
//	public abstract final void eat() {}
	
//	public abstract AbstractTest1() {
//		
//	}
}
5.3 设计模式:模板方法(TemplateMethod)
(1)解释:
  • 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
(2)解决的问题:
  • 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
(3)具体实现:
public class TemplateTest {
	public static void main(String[] args) {
		Template t= new SubTemplate();
		SubTemplate t1= new SubTemplate();
		t.spendTime();
		
		System.out.println("*****************************");
		t1.spendTime();
	}

}
abstract class Template{
	//计算某段代码执行所花费的时间
	public void spendTime() {
		long start =System.currentTimeMillis();
		code(); //不确定的部分、易变的部分
		
		long end = System.currentTimeMillis();
		System.out.println("花费的时间:"+(end-start));
	}
	public abstract void code(); //抽象
}

class SubTemplate extends Template{
	@Override
	public void code() {  //实现父类中抽象方法
		//1000以内的质数输出:
		boolean isFlag=true;
		for(int i=2;i<=1000;i++) {
			for(int j=2;j<=Math.sqrt(i);j++) {
				if(i%j==0) {
					isFlag=false;
					break;
				}
			}
			if(isFlag) {
				System.out.println(i+" ");
			}
			isFlag=true;
		}
	}
}
5.4 抽象类的匿名子类:
  • 理解:匿名子类就是不直接定义类去继承抽象类并实现其抽象方法,而是直接使用new 抽象类名{ }的形式并在大括号中实现抽象方法的类,称为匿名子类。
abstract class Person{
	String name;
	int age;	
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public abstract void eat ();  //抽象方法	
	public void walk() {
		System.out.println("人走路");
	}
}
 class  Student extends Person{
	public Student() {
		super();
	}
	public void eat() {
		System.out.println("学生多吃营养的食物");
	}
 }

class PersonTest{
  public static void mian(String []args){
    //1.非匿名子类的非匿名对象
    Student s = new Student();
    method(s);
    //2.非匿名子类的匿名对象
    method(new Student());
    //3.匿名子类的非匿名对象
    Person p = new Person(){
      public void eat() { //实现abstract方法
	    	System.out.println("工人多吃营养的食物");
     	   }
      };
     method(p);
    
     //4创建匿名子类的匿名对象
    method(new Person(){
      public void eat() { //实现abstract方法
	    	System.out.println("农民多吃营养的食物");
     	   }
    });
   }
  public static void method(Person p){
    p.eat();
  }
}

6 接口(interface)

6.1 接口的引出:
  • (1)一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
  • (2)另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
  • (3)接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则 必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 “能不能” 的关系。
  • (4)接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
6.2 接口(interface)说明:
  • (1)在java语言中,接口和类是并列的结构;
  • (2)如何定义接口:定义接口中的成员

① JDK7及以前: 只能定义全局常量和抽象方法

  • 全局常量:public static final的,但是书写时可以省略;
  • 抽象方法:public abstract的
  • (3)接口中不能定义构造器,说明不能实例化;
  • (4)java开发中,接口通过让类去实现(implements)的方式来使用:
  • ① 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
  • ② 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
  • ③图片展示:

在这里插入图片描述

  • ④代码演示:
interface Attackble{
  //抽象方法
	void attack();
}
interface Flyable{	
	//全局常量
	public static final int MAX_SPEED=7900;//第一宇宙速度
//	public static final int MIN_SPEED=1;
	int MIN_SPEED=1; //省略public static final	
	//抽象方法
	public abstract void fly();
	void stop(); //省略了public abstract 	
}

class Plane implements Flyable{//类实现接口

	@Override
	public void fly() {
		System.out.println("通过引擎飞行");
	}

	@Override
	public void stop() {
		System.out.println("驾驶员减速停止");
	}
}
  • (5)java类可以实现多个接口---->弥补了java的单继承性的局限性;
  • ① 格式:class AA extends BB implements CC,DD,EE{}
  • ② 代码演示:
//接口以上代码;
class Bullet implements Flyable,Attackble{
  @Override
	public void attack() {
		// TODO Auto-generated method stub		
	}
	@Override
	public void fly() {
		// TODO Auto-generated method stub	
	}
	@Override
	public void stop() {
		// TODO Auto-generated method stub	
	}
}
  • (6)接口和接口之间也是继承,并且是多继承:
  • ① 代码演示:
interface AA{  }
interface BB{}

interface CC extends AA,BB{ }  //接口之间的多继承;
  • (7)接口的具体使用,体现了多态性
  • ① 代码演示:
interface AA{
	void info();   //省略了public abstract
}
class BB implements AA{
	public void info(){
		System.out.println("这是一个类实现接口......");
	}
}

public class Test3{
	public static void main(String [] args){
		AA aa = new BB();   //多态使用,接口引用指向实现类对象
	}
}
  • (8)接口(interface)是抽象方法和常量值定义的集合。
6.3 接口(interface)使用:
(1)接口的特点:
  • 用interface来定义
  • 接口中的所有成员变量都默认是由public static final修饰的。
  • 接口中的所有抽象方法都默认是由public abstract修饰的。
  • 接口中没有构造器。
  • 接口采用多继承机制
(2)接口定义举例:
public interface Runner {
	public static final int ID = 1;  //------> int ID=1;
	public abstract void start();  //------> void start();
	public abstract void run();  //------->void run();
	public abstract void stop();  //------>void stop();
}
(3)接口再次说明:
  • ① 定义Java类的语法格式:先写extends,后写implements ;
    • 格式:class SubClass extends SuperClass implements InterfaceA{ }
  • ② 一个类可以实现多个接口,接口也可以继承其它接口(多继承)。
  • ③ 接口的主要用途就是被实现类实现。(面向接口编程)
  • ④ 与继承关系类似,接口与实现类之间存在多态性。
  • ⑤ 接口和类是并列关系,或者可以理解为一种特殊的类。
6.4 接口应用:
(1)举例一:
interface Runner {
    public void start();
    public void run();
    public void stop();
}
class Person implements Runner {
    public void start() {
    // 准备工作:弯腰、蹬腿、咬牙、瞪眼
    // 开跑
}
    public void run() {
    // 摆动手臂
    // 维持直线方向
    }
    public void stop() {
    // 减速直至停止、喝水。
    }
}
(2)举例二:
interface MyInterface{
  	String s=MyInterface; //--->public static final String s=“MyInterface”;
  	public void absM1();
}
interface SubInterface extends MyInterface{
		public void absM2();
}
public class SubAdapter implements SubInterface{
    public void absM1(){
    	System.out.println(“absM1”);
     }
    public void absM2(){
    	System.out.println(“absM2”);
     }
}
  • 注意:实现类SubAdapter必须给出接口SubInterface以及父接口MyInterface中 所有抽象方法的实现。否则,SubAdapter仍需声明为abstract的。
(3)举例三:代理模式(proxy)
  • ①概述:
  • 代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
  • ②代码:
interface Network {
public void browse();
}
// 被代理类
class RealServer implements Network {
@Override
  public void browse() {
  System.out.println("真实服务器上网浏览信息");
  }
}
// 代理类
class ProxyServer implements Network {
    private Network network;
    public ProxyServer(Network network) {
      this.network = network;
    }
public void check() {
		System.out.println("检查网络连接等操作");
}
public void browse() {
    check();
    network.browse();
    }
}
public class ProxyDemo {
public static void main(String[] args) {
    Network net = new ProxyServer(new
   RealServer());
    net.browse();
    }
}
(4)举例四:工厂模式
  • 🔥核心本质: 实例化对象,用工厂方法代替 new 操作。 将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
① 无工厂模式:
interface Car{
	void run();
}
class Audi implements Car{
  public void run() {
    System.out.println("奥迪在跑");
  }
}
class BYD implements Car{
  public void run() {
  	System.out.println("比亚迪在跑");
  }
}
public class Client01 {
  public static void main(String[] args) {
    Car a = new Audi();
    Car b = new BYD();
    a.run();
    b.run();
  }
}
② 简单工厂模式:
//工厂类
class CarFactory {
//方式一
  public static Car getCar(String type) {
    if ("奥迪".equals(type)) {
     	 return new Audi();
      } else if ("比亚迪".equals(type)) {
     	 return new BYD();
      } else {
  		  return null;
    }
}
//方式二
// public static Car getAudi() {
// return new Audi();
// }
//
// public static Car getByd() {
// return new BYD();
// }
}
public class Client02 {
	public static void main(String[] args) {
		Car a = CarFactory.getCar("奥迪");
		a.run();
		Car b = CarFactory.getCar("比亚迪");
		b.run();
	  }
}

在这里插入图片描述

  • 缺点:对于增加新产品,不修改代码的话,是无法扩展的。违反了开闭原则(对 扩展开放;对修改封闭)。
③ 工厂方法模式:
  • 用来生产同一等级结构中的固定产品。(支持增加任意产品)
  • 简单工厂模式只有一个(对于一 个项目或者一个独立的模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。
  • 代码说明
//工厂接口
interface Factory{
	Car getCar();  //----->public abstract Car gerCar();
}
//两个工厂类
class AudiFactory implements Factory{
  public Audi getCar(){
  	return new Audi();
  }
}
class BydFactory implements Factory{
  public BYD getCar(){
  	return new BYD();
  }
}
public class Client {
  public static void main(String[] args) {
    Car a = new AudiFactory().getCar();
    Car b = new BydFactory().getCar();
    a.run();
    b.run();
  }
}

在这里插入图片描述

⑤ 抽象工厂模式:
  • 用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
  • 抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。
6.5 接口和抽象类之间的对比:

在这里插入图片描述

6.6 Java 8中关于接口的改进:
(1)说明:
  • ① Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

  • 静态方法:使用static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。

  • 你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
  • 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
  • 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
public interface AA {
  double PI = 3.14; //全局常量
  public default void method() { //默认方法
  		System.out.println("北京");
  } 
  default String method1() {//默认方法
 		 return "上海";
  }
  public static void method2() { //静态方法
 		 System.out.println("hello lambda!");
  }
}
(2)接口中的默认方法:
  • 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。

解决方式:

  • 实现类必须覆盖接口中同名同参数的方法,来解决冲突。
  • 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
interface Filial {// 孝顺的
	default void help() {
    	System.out.println("老妈,我来救你了");
    }
  }
interface Spoony {// 痴情的
	default void help() {
    	System.out.println("媳妇,别怕,我来了");
    }
	}
}
class Man implements Filial, Spoony {
@Override
	public void help() {
      System.out.println("我该怎么办呢?");
      Filial.super.help();
      Spoony.super.help();
    }
}
  • 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法:
class BB{
  public void method3(){
    
  }
}
interface CC{
  public default void method3(){ //默认方法
    
  }
}
class AA extends BB implements CC{
  public void method3(){} 
	public void myMethod() {
		method3(); //自己定义的重写方法
		super.method3(); //调用的是父类中声明的方法
		
		//调用接口中声明的默认方法
		CC.super.method3(); 
	}
}

7 类的内部成员之五: 内部类

7.1 内部类概念引出:

(1)当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

7.2 内部类使用说明:
  • ①在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类

  • ②Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称(Inner class的名字不能与包含它的外部类类名相同;)。

  • ③内部类分类:

    • 成员内部类(static成员内部类和非static成员内部类);
    • 局部内部类(不谈修饰符)、匿名内部类;
7.3 内部类的分类:成员内部类与局部内部类
(1)成员内部类:
  • ① 作为外部类的成员
  • 调用外部类的结构;
  • 可以被static修饰;
  • 可以被四种不同的权限修饰;
  • ② 作为一个类:
  • 类内可以定义属性、方法和构造器等;
  • 可以被final修饰,表示此类不能被继承。言外之意,不使用final就可以继承;
  • 可以被abstract修饰;
  • ③关注如下的三个问题:
  • 如何实例化成员内部类的对象,代码如下:
class Person{
 static  class Dog{ //静态成员内部类
    
  }
  class bird{ //非静态成员内部类
    
  }
}
class innerClass{
 public static void main(String [] args){
    //静态而言
    Person.Dag dog = new Person.Dog();
    
    //非静态而言
    Person p = new Person(); 
	Person.Bird bird =p.new Bird();
  }
}
  • 如何在成员内部类中区分调用外部类的结构:
class Person{
  String name;
  int age;
  public void eat(){}
  static class Dog{ //静态成员内部类
    Person.this.eat(); //调用外部类的非静态的方法
  }
  class bird{ //非静态成员内部类
    String name;
    public void display(String name){
			System.out.println(name); //方法的形参
			System.out.println(this.name); //内部类的属性
			System.out.println(Person.this.name);//外部类的属性
    }
  }
}
(2)局部内部类(方法内、代码块内):
  • ① 如何声明局部内部类:
class 外部类{
  方法(){  //方法内
	   class 局部内部类{
	     }
   }
 {//代码块内
	  class 局部内部类{
	   }
 }
}
  • ② 如何使用局部内部类:
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类;
  • 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
  • ③ 局部内部类的特点:
  • 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但前面冠以外部类的类名和$符号,以及数字编号。
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
  • 局部内部类可以使用外部类的成员,包括私有的。
  • 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
  • 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private。
  • 局部内部类不能使用static修饰,因此也不能包含静态成员。
  • ④ 匿名内部类:
  • 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  • 格式如下
 new 父类构造器(实参列表)| 实现接口(){ 
		 //匿名内部类的类体部分 
 }
  • ⑤ 代码演示:
public class InnerClassTest1 {
	//开发中很少见
	public void method() {
		//局部内部类
		class AA{			
		}
	}
	//返回一个实现了Comparable接口的对象
	public Comparable getCompareble() {	
		//创建了实现了Comparable接口的类:局部内部类
		//方式一:
//		class MyComparable implements Comparable{
//
//			@Override
//			public int compareTo(Object o) {
//				return 0;
//			}
//			
//		}
//		return new MyComparable();		
		//方式二:
		return new Comparable() {
			@Override
			public int compareTo(Object o) {   //匿名内部类(comparable接口的实现类)
				// TODO Auto-generated method stub
				return 0;
			}		
		};
	}	
}
7.4 内部类的总结与注意:
(1)内部类总结:
  • ① 成员内部类和局部内部类,在编译以后,都会产生字节码文件。
  • 格式
  • 成员内部类:外部类$内部类名.class
  • 局部内部类:外部类$数字 内部类名.class
(2)内部类注意点:
  • 在局部内部类的方法(show())中,如果调用局部内部类所在声明的方法(例如:method())中的局部变量(例如:num);要求此局部变量声明为final的。
  • Jdk 7及之前版本,要求此局部变量显式的声明为final的;
  • jdk 8及之后版本,可以省略final的声明;

8 面向对象内容总结:

在这里插入图片描述

七、异常处理:

1 异常概述与异常体系结构:

1.1 异常概述:

(1)使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美, 在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

(2)异常:在Java语言中,将程序执行中发生的不正常情况称为“异常” 。 (注意:开发过程中的语法错误和逻辑错误不是异常)

(3)异常事件分类:
  • ① Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM(堆溢出)。一般不编写针对性的代码进行处理。
//1.栈溢出: java.lang.StackOverflowError
	//	main(args);
		
//2.堆溢出:java.lang.OutOfMemoryError:
	//	Integer[] in = new Integer[1024*1024*1024];
  • ② Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。

举例如下

  • 空指针访问 ;
  • 试图读取不存在的文件;
  • 网络连接中断;
  • 数组角标越界;
(4)异常出现的说明:
  • ① 对于这些错误,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
  • 捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。 比如:除数为0,数组下标越界等;
(5)Exception:编译时异常 与 运行时异常
① 运行时异常:
  • 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。
  • 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
  • 代码演示:

//**********************运行时异常***************************
//ArithmeticException
@Test
	public void test6() {
//		int i=4;
//		int j=0;
//		System.out.println(i/j);
	}
	//InputMismatchException
	@Test 
	public void test5() {
//		Scanner  scanner = new Scanner(System.in);
//		System.out.println(scanner.nextInt());
	}
	//NumberFormatException
	@Test
	public void test4() {
		String str ="123";
//		str ="abc";
//		Integer in =Integer.parseInt(str);
		
	}
② 编译时异常:
  • 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一 般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
  • 对于这类异常,如果程序不处理,可能会带来意想不到的结果。
  • 代码演示:
//***************************编译时异常*****************************
	@Test
	public void test7() {
//		File file = new File("hello.txt");
//		FileInputStream fis = new FileInputStream(file);
//		
//		int data =  fis.read();
//		while(data!=-1) {
//			System.out.println((char)data);
//			data=fis.read();
//		}
//		fis.close();
	}
1.2 异常体系结构:

Java.lang.Throwable
-----java.lang.Error:一般不编写针对性的代码进行处理。
-----java.lang.Exception:可以进行异常的处理
-------------编译时异常(checked)
-------------------------IOException
-------------------------FileNotFoundException
-------------------------ClassNotFoundException
-------------运行时异常(unchecked)
--------------------------NullPointerException
--------------------------ArrayIndexOutOfBoundException
--------------------------ClassCastException
--------------------------NumberFormatException
--------------------------InputMismatchException
--------------------------ArithmeticException

在这里插入图片描述

2 常见异常(如下代码举例):

(1)java.lang.RuntimeException:
  • ① ClassCastException :
public class Order {
  public static void main(String[] args) {
    Object obj = new Date();
    Order order;
    order = (Order) obj;
    System.out.println(order);
  }
}
  • ② ArrayIndexOutOfBoundsException
public class IndexOutExp {
  public static void main(String[] args) {
    String friends[] = { "lisa", "bily", "kessy" };
    for (int i = 0; i < 5; i++) {
      System.out.println(friends[i]); // friends[4]?
    }
    System.out.println("\nthis is the end");
  }
}
  • ③ NullPointerException
public class NullRef {
		int i = 1;
	public static void main(String[] args) {
    NullRef t = new NullRef();
    t = null;
    System.out.println(t.i);
    }
}
  • ④ ArithmeticException
public class DivideZero {
  	int x;
  public static void main(String[] args) {
  	int y;
  	DivideZero c=new DivideZero();
 		 y=3/c.x; 
 	 System.out.println("program ends ok!");
  }
}
  • ⑤ NumberFormatException
  • ⑥ InputMismatchException
(2)java.io.IOExeption
  • FileNotFoundException
  • EOFException
(3) java.lang.ClassNotFoundException
(4)java.lang.InterruptedException
(5)java.io.FileNotFoundException
(6) java.sql.SQLException

3 异常的总结:

体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不报错,但是运行时仍可能报错。

  • 相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。

体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。

  • 针对编译时异常,我们说一定需要考虑异常处理。

4 异常处理机制一: try-catch-finally

4.1 Java异常处理
  • Java采用的异常处理机制,是将异常处理的程序代码集中在一起, 与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
  • Java提供的是异常处理的抓抛模型
  • Java程序的执行过程中如出现异常,会生成一个异常类对象, 该异常对象将被提交给Java运行时系统,这个过程称为抛出 (throw)异常
  • (1)异常对象的生成:
  • ① **由虚拟机自动生成:**程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例 对象并抛出——自动抛出
  • 由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样。
4.2 异常的抛出机制:

在这里插入图片描述

  • (1)如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。 这一过程称为捕获(catch)异常
  • (2)如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。
  • (3)程序员通常只能处理Exception,而对Error无能为力。

(4)异常处理是通过try-catch-finally语句实现的。

try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
[ finally{  //finally结构可选择
...... //无论是否发生异常,都无条件执行的语句
} ]

① try:

  • 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。

② catch(Exception e):

  • 在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随 一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
  • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。 比如 : 可以用 ArithmeticException 类作为参数的地方 , 就可以用 RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。 但不能是与ArithmeticException类无关的异常,如NullPointerException(catch 中的语句将不会执行)。

捕获异常的有关信息:

  • 与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
  • 🔥getMessage() 获取异常信息,返回字符串
  • 🔥**printStackTrace() **获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

在这里插入图片描述

③finally:

  • 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
  • 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return, finally块中的语句都会被执行。
  • finally语句和catch语句是任选的。

在这里插入图片描述

(1)(RuntimeException)异常处理的举例:
  • RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。
  • ① 举例一:ArrayIndexOutOfException
public class IndexOutExp {
  public static void main(String[] args) {
  String friends[] = { "lisa", "bily", "kessy" };
  try {
    for (int i = 0; i < 5; i++) {
   		 System.out.println(friends[i]);
    }
  } catch (ArrayIndexOutOfBoundsException e) {
 			 System.out.println("index err");
 		 }
 			 System.out.println("\nthis is the end");
  }
}
  • ② 举例二:ArithmeticException
public class DivideZero1 {
 		 int x;
  public static void main(String[] args) {
  	 int y;
 		 DivideZero1 c = new DivideZero1();
  try {
  y = 3 / c.x;
  } catch (ArithmeticException e) {
 		 System.out.println("divide by zero error!");
  }
 		 System.out.println("program ends ok!");
  }
}
(2)(IOException)编译时异常处理:
  • 如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为 运行时异常
  • ①举例一:(异常出现)
//FileNotFoundException
import java.io.*;
public class IOExp {
  public static void main(String[] args) {
    FileInputStream in = new FileInputStream("atguigushk.txt");
    int b;
    b = in.read();
  while (b != -1) {
    System.out.print((char) b);
    b = in.read();
  }
 		 in.close();
  }
}
  • ② 举例二:(异常处理)
import java.io.*;
public class IOExp {
    public static void main(String[] args) {
    try {
      FileInputStream in = new FileInputStream("atguigushk.txt");
      int b;
      b = in.read();
      while (b != -1) {
        System.out.print((char) b);
        b = in.read();
    }
   		 in.close();
    } catch (IOException e) {
   		 System.out.println(e);
    } finally {
   		 System.out.println(" It’s ok!");
   	 }
    }
}

5 异常处理机制二:throws + 异常类型

5.1 声明抛出异常(throws):
(1)throws的说明:
  • ①"throws + 异常类型"写在方法的声明处。指明此方法执行时。可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
  • ②如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应**显示地声明抛出异常,**表明该方法将不对这些异常进行处理, 而由该方法的调用者负责处理。
  • ③在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
(2)throws抛出异常举例:
public static void method2() throws IOException{
		method1();
}
public static void method1() throws FileNotFoundException,IOException{
		File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file);
		int data =  fis.read();
		while(data!=-1) {
			System.out.println((char)data);
			data=fis.read();
		}
		fis.close();
	}

在这里插入图片描述

5.2 重写方法声明抛出异常的原则:
(1)原则说明:
  • 重写方法不能抛出比被重写方法范围更大的异常类型。在多态的情况下, 对methodA()方法的调用-异常的捕获按父类声明的异常处理。
public class A {
  public void methodA() throws IOException {
  		//……
	} 
}
public class B1 extends A {
  public void methodA() throws FileNotFoundException {
 		// ……
	} 
}
public class B2 extends A {
  public void methodA() throws Exception { //报错
		//  ……
	}
 }
5.3 开发中,如何选择try-catch-finally 还是 使用throws?
  • ① 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws。意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
  • ② 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们创建这几个方法使用throws的方式进行处理。而执行的方法a可以考虑try-catch-finally方式进行处理。

6 手动抛出异常:

6.1 说明:

(1)Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。

  • ① 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。
IOException e = new IOException();
throw e;
  • 注意:可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将 会产生语法错误:
throw new String("want to throw");

代码演示:

public void regist(int id){		
	if(id>0) {			
		this.id=id;		
	}else {
		//System.out.println("输入的数据非法!");			
		//手动抛出异常
			// throw new RuntimeException("您输入的数据非法!");			
			throw new MyException("您输入的数据非法!");  //MyException为自定义运行时异常(如下说明)						
			//错误的
			// throws new String("不能输入负数!");		
		}	
	}

7 用户自定义异常类 :

7.1 自定义异常的说明:
  • ①用户自定义异常类都是RuntimeException的子类;
  • ② 自定义异常类通常需要编写几个重载的构造器。
  • ③自定义的异常通过throw抛出。
  • ④继承于现有的异常结构:RuntimeException、Exception;
  • ⑤自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
7.2 自定义异常类举例:
class MyException extends Exception {
  static final long serialVersionUID = 13465653435L;
  private int idnumber;
  public MyException(String message, int id) {
    super(message);
    this.idnumber = id;
  }
  public int getId() {
    return idnumber;
  }
}

public class MyExpTest {
  public void regist(int num) throws MyException {
  if (num < 0)
    throw new MyException("人数为负值,不合理", 3);  //手动生成异常对象
    else
    System.out.println("登记人数" + num);
  }
  public void manager() {
    try {
      regist(100);
    } catch (MyException e) {
   		 System.out.print("登记失败,出错种类" + e.getId());
    }
   		 System.out.print("本次登记操作结束");
  }
  public static void main(String args[]) {
      MyExpTest t = new MyExpTest();
      t.manager();
  }
}
(1)异常简记忆图:

在这里插入图片描述

  • 22
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 宋红康先生的Java笔记Typora是一份非常有价值的Java学习资料。Typora是一种Markdown编写工具,非常适合于码字和笔记整理。是一款非常支持运用Java的软件。宋红康先生的Java笔记,是以Typora为载体,对Java技术进行深度解析和介绍的学习资料。 宋红康先生的Java笔记Typora,内容丰富,涵盖了JavaSE、JavaEE、Java框架等多方面的知识点。除了传统的类和对象、继承和多态等Java基础语法外,Typora笔记还涉及了Java I/O、异常处理、集合框架、多线程、JDBC等重要的Java应用方面的知识,能够让读者更好地掌握Java编程的技巧。 此外,宋红康先生不仅将Java的技术点进行了详细讲解,还给出了很多实际的代码例子和实战项目,让读者能够更好地理解Java的应用场景和解决问题的方法。同时,宋红康先生的Java笔记Typora还提供了非常全面的Java学习资源,包括经典的Java书籍、Java论坛、Java常用工具等,可供读者深度学习和研究Java技术。 总之,宋红康Java笔记Typora是一份非常有价值的Java学习资料,无论是初学者还是有一定编程经验的程序员都可以从中获得很多启发和帮助。如果你想要快速入门Java编程,那么Typora笔记会是一个非常不错的选择。 ### 回答2: 宋红康java笔记typora是指宋红康学习Java编程语言的过程中,记录笔记的工具为Typora的事情。Typora是一款轻量级的Markdown编辑器,可以帮助用户快速高效地编辑和排版文档。 宋红康是一位资深的Java程序员,对Java编程语言非常熟悉,他利用Typora这一工具来记录自己的学习笔记。Typora简单易用,支持常用的Markdown语法,让宋红康可以专注于记录自己对Java的理解和加深对Java编程语言的认识。 宋红康Java笔记包括对Java语法、面向对象编程Java开发工具、Java Web开发、Java框架、数据库编程等方面的知识点的记录和总结。这些知识点通过Typora的排版功能,呈现出来非常清晰,易于查看和理解。 总之,宋红康java笔记typora是一种高效的学习方式,它不仅可以帮助学习者快速记录和总结知识点,还可以提高学习效率,让学习更加轻松愉快。 ### 回答3: 宋红康Java笔记Typora是一本非常优秀的Java编程入门教材。Typora是一款兼容多种操作系统的软件,它极大地方便了笔记的编辑和共享传递。宋红康Java笔记中的内容相当全面,从Java程序开发的基础语法入手,依次阐述了Java高级程序开发中的各项难点,同时引导读者通过自主练习来深入理解与掌握Java编程技能。 宋红康Java笔记在内容上结构合理、连贯易懂,将Java程序设计的复杂知识点分解为逐步深入的章节内容,每个章节都有相关的练习题来帮助读者巩固所学的知识点。此外,该笔记还提供了详细的代码实例,让读者可以更加清晰的了解Java编程的执行过程,同时加深对程序设计思路的理解。 总之,宋红康Java笔记Typora是一本很好的Java编程入门教材,如果你对Java编程有困惑,又想全面系统地了解Java编程,那么这本笔记一定会给你带来很大的收获。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值