java笔记

java语言特性

简单性:相对于其他编程语言而言,java较为简单,例如:java不再支持多继承,C++是支持多继承的,多继承比较复杂,C++中有指针,java中屏蔽了指针的概念,避免了绝大部分的指针越界和内存泄露的问题,这里说明一下,java语言低层是用C++实现的,并不是C语言。
面向对象:java是纯面向对象的,更符合人的思维模式,易于理解。
健壮性:java的健壮性与自动垃圾回收机制有关,自动垃圾回收机制简称GC机制,java语言运行过程中产生的垃圾是自动回收的,不需要程序员关心。
可移植性:java程序可以做到一次编译,到处运行。在Windows操作系统上运行的java程序,不做任何修改,可以直接放到Linux操作系统上运行,这个被称为java程序的可移植性(跨平台)。java的跨平台性是通过JVM(java虚拟机)实现的,java代码不直接与底层操作系统打交道,而是通过JVM这个中间介质间接与底层操作系统交互,JVM屏蔽了各操作系统之间的差异,不同版本的操作系统就有不同版本的JVM,只有在JVM这个环境下的java程序才能运行。
多线程

java的编译与运行

在这里插入图片描述

JDK、JRE、JVM

JDK:java开发工具包
JRE:java运行时环境
JVM:java虚拟机 说明: 1、JDK和JRE都可以单独安装,若不需要开发Java程序,只需要运行的话,那么,只用安装JRE即可。 2、JVM不能单独安装。 以下为三者关系图: 在这里插入图片描述

字符编码

为了让计算机可以表示现实世界中的文字,需要人提前制定好“文字”和“二进制”之间的对照关系,这种对照转换关系被称为“字符编码”。

ASCII码:采用一个字节编码,主要针对英文编码
GBK:主要是简体汉字编码
Unicode:统一了全世界的所有文字编码,采用这三种方式实现(UTF-8、UTF-16、UTF-32) 说明: 1、Java采用Unicode编码方式 2、在实际开发中,一般使用UTF-8编码方式实现

数据类型

Java程序中最基本的单位是类
Java中变量赋值时必须类型对应,否则不兼容,编译不过,可用强转
Java中局部变量不赋值不能使用
在这里插入图片描述
说明:整数默认为int,浮点数默认为double,布尔类型默认为false

数据类型取值范围

在这里插入图片描述

数据类型默认转换

byte、short、char–>int–>long–>float–>double
注:为什么long比float所占字节长度更大却是long转换为float呢? 因为它们底层的存储结构不同,float表示的数据范围要比long的范围更大。

标识符命名方法

小驼峰命名法:标识符是由一个单词组成时首字母小写,多个单词组成时第一个单词首字母小写,其余单词首字母大写。
大驼峰命名法:标识符是由一个单词组成时首字母大写,多个单词组成时每一个单词首字母大写。 说明:小驼峰命名法用于方法、变量的命名,大驼峰命名法用于类的命名。

数组

一维数组

格式如下:

/*动态初始化*/
数组类型[] 数组名 = new 数组类型[元素个数(数组长度)];

/*静态初始化*/
数组类型[] 数组名 = {元素1, 元素2, 元素3, .....} = new 数组类型[]{元素1, 元素2, 元素3, .....};
举例:

String[] array1 = {"abc", "a", "ad"};//字符串数组中不能有字符('')
char[] array2 = {'A', 'b'};//字符数组中不能有字符串("")
int[] array3 = {23, 12, 90}; 

说明:以上例子中可以把“[ ]”放在数组名后面(array1[]),但是Java中不建议这样,放在数组名前面更易于理解。

用“ 数组名.length ”的方式来获得数组长度
字符数组默认初始值为一个空字符(“\u000”)
boolean数组默认初始值为false,其余数组默认值为0
Java中的数组必须先初始化(赋值后)才能使用

二维数组

格式如下:

/*动态初始化*/
写法一:数据类型[][] 数组名 = new 数据类型[m][n];//m表示这个二维数组有m个一维数组, n表示每一个一维数组有n个元素
写法二:数据类型 数组名[][] = new 数据类型[m][n];
写法三:数据类型[] 数组名[] = new 数据类型[m][n];
数据类型[][] 数组名 = new 数据类型[m][];//如果没有给出每个一维数组的元素个数,就表示这个二维数组是变化的,但不能没有一维数组的个数

/*静态初始化*/
数据类型[][] 数组名 = {{元素...}, {元素...}, {元素...}......} = new 数据类型[][] {{元素...}, {元素...}, {元素...}......};

/*注明*/
int[] x, y[];//x是一维数组,y是二维数组

数组排序算法

选择排序:把0索引的元素和1索引后的元素进行比较
在这里插入图片描述

冒泡排序:相邻元素比较
加粗样式

数组查找算法

二分查找(折半查找):针对数组有序的情况,切记不要将已给出的数组先排序后再查找
数组工具类(Arrays)
数组转字符串功能
public static String toString(int[] array);
快速排序功能
public static void sort(int[] array);
二分查找功能
public static int binarySearch(int[] array, int key);
逻辑运算符
在这里插入图片描述

短路运算符:“&&” 、 “||”
非短路运算符:“&” 、 " | "
短路运算符与非短路运算符的区别:短路运算符两边表达式有可能只执行一边的表达式,效率较高,最常用;非短路运算符两边表达式无论什么情况都会执行。
最最常用的逻辑运算符:“&&” 、 “||” 、 " ! "
输入操作
步骤如下:

导包(必须放在类class与包名package的中间)
import java.util.Scanner;
创建Scanner对象
Scanner input = new Scanner(System.in);
接收数据
int n = input.nextInt();
注:

整型:input.nextInt();
单精度浮点型:input.nextFloat();
双精度浮点型:input.nextDouble();
字符串类型:input.nextLine();//这种写法可以得到带空格的字符串
input.next();//此写法在读取内容时会过滤掉有效字符前面的无效字符,所以这种写法不能得到带空格的字符串
短整型:input.nextShort();
字节型:input.nextByte();
字符型:input.next().charAt(i);//i是指从用户输入的字符串中选取第i个单个字符输入到内存中,i从0开始
Java中的命名规则
包:实质上是文件夹,用于保证类名的唯一性(区分相同类名)。为了保证包名的唯一性,一般采用公司域名以逆序的形式作为包名,然后对于不同的工程使用不同的子包(域名.项目名.模块名)例:com.horstmann.corejava,且包名全部小写
类或接口:大驼峰命名法
方法或变量:小驼峰命名法
常量:一个单词组成时全部大写,多个单词组成时每个单词大写且单词间用_隔开
二进制数:以0b开头
八进制数:以0开头
十六进制数:以0x开头
有符号数据表示法
在计算机内,有符号数据有三种表示法:原码、反码、补码,所有数据的运算都是采用补码进行的

原码:二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位数表示数值大小
反码:正数的反码与其原码相同,负数的反码是对其原码逐位取反,但符号位除外
补码:正数的补码与其原码相同,负数的补码是在其反码的末位加1
面向对象与面向过程
面向过程:主要关注实现的具体过程,语句之间是因果关系

优点:对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低
缺点:采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间的耦合度非常高,只要其中一环出现问题整个系统都将受到影响,导致最终的软件扩展力差,由于没有对象的概念,所以无法达到组件复用
面向过程程序设计也称作结构化程序设计或结构化编程,它是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用时一个一个依次调用即可。

面向对象:它是基于面向过程的编程思想,也是一种思考问题的方式,主要关注对象能完成哪些功能

面向对象是把构成问题的事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为

优点:一种更符合我们思想习惯的思想,将我们从执行者变成指挥者,从而达到将复杂问题简单化的目的。耦合度低,扩展力强,更容易解决现实世界中复杂的业务逻辑,组件复用性强
缺点:前期投入成本高,需要进行对象的抽取,大量的系统分析与设计
如何建立面向对象思维呢?1、首先分析有哪些类;2、分析每个类应该有什么;3、最后分析类与类之间的关系
面向对象开发就是不断的创建对象、使用对象、指挥对象做事情
面向对象设计就是在管理和维护对象之间的关系
所有面向对象的编程语言都具有的三大特征:封装、继承、多态
注:采用面向对象的方式开发一个软件的生命周期

面向对象的分析(OOA)
面向对象的设计(OOD)
面向对象的编程(OOP)

包的定义:包其实就是文件夹
包的作用:1、把相同的类名放到不同包中;2、对类进行分类管理(分类方案:1、按照功能分,如增删改查;2、按照模块分,如老师、学生,每个模块中有对应的功能)
package语句必须是程序的第一条可执行的代码,注释除外
package语句在一个java文件中只能有一个
如果没有package,默认无包名(default)
拥有包访问权限的类才能访问某个包中的类
同一个包中不允许有相同名字的类,不同包中类的名字可以相同,当同时调用两个不同包中的相同类名的类时,应当加上包名加以区分

类的描述
对一个事物的描述:

属性:该事物的描述信息
行为:该事物能够做什么 在这里插入图片描述
类是一组相关的属性和行为的集合,是构造对象的模板和蓝图,是一个抽象的概念
由类构造对象的过程称为类的实例化
类的导入
一个类可以使用所属包中的所有类,以及其他包中的公共类,有两种方式访问另一个包中的公共类:

使用完全限定名(包名后面跟着类名)如:java.time.LocalDate
使用import语句
自定义类的使用
创建对象的格式:
类名 对象名 = new 类名();
使用成员变量的格式:
对象名.变量名
使用成员方法的格式:
对象名.方法名(参数…)
类的初始化过程
以下代码在内存中做了哪些事情?

Student s = new Student();
分五步:

加载Student.class文件进内存
在栈内存中为引用变量s开辟空间
在堆内存中为学生对象开辟空间
对学生对象的成员变量进行默认初始化
对学生对象的成员变量进行显示初始化
对上述语句的解析:

右边的“new Student()”是以Student类为模板,在堆空间里创建一个Student对象
末尾的“()”意味着在对象创建后立即用Student类的构造方法对刚生成的对象进行初始化
左边的“Student s ”创建了一个Student类引用变量,它存在于栈空间中,也就是用来指向Student对象的对象引用
“=”操作符使对象引用指向刚创建的那个Student对象
类的设计技巧
一定要保证数据私有化
一定要对数据进行初始化
不要在类中使用过多的基本类型
不是所有的字段都需要单独的字段访问器和字段更改器
分解有过多职责的类
类名和方法名的命名要起到见名知意的效果(类名应当是一个名词或动名词,访问器方法用小写get开头,更改器方法用小写set开头)
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行的加载

加载:将.class文件读入内存,并为之创建一个class对象,任何类被使用时系统都会建立一个class对象

连接:

验证:是否有正确的内部结构,并和其他类协调一致
准备:负责为类的静态成员分配内存,并设置默认初始值
解析:将类的二进制数据中的符号引用替换为直接引用
类的加载时机
什么时候类会加载?

创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载器
根类加载器(Bootstrap ClassLoader):也叫引导类加载器,负责Java核心类的加载,比如System、String等,在JDK中JRE的lib目录下的rt.jar文件中
扩展类加载器(Extension ClassLoader):负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下的ext目录中
系统类加载器(System ClassLoader):负责在JVM启动时加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径(我们自定义的类就是通过它加载的)
对象
对象是该类事物的具体表现形式,具体存在的个体,如:学生是一个类,那么,班长就是一个对象
匿名对象
匿名对象是指没有名字的对象,只创建对象但是不用变量来接收

//匿名对象举例
new student();
new student().show();
匿名对象的应用场景:

该方法仅调用一次的时候
匿名对象可以作为实际参数传递
匿名对象的优点:匿名对象创建的方式能够减少栈帧的分配和指向,且在调用完毕后能够被GC机制(垃圾回收机制)快速的回收

方法
方法概述
方法是完成特定功能的代码块,Java中的方法就是C语言中的函数
在Java中,所有的方法都必须在类的内部定义
如下代码中,像这里的 e 出现在方法名前面的叫隐式参数,可以用this指示;像这里的 5 出现在方法名后面括号中的数值叫显式参数
Employee e = new Employee(“xl”…);
e.raiseSalary(5);
如果方法需要返回一个可变对象的引用,首先应该对它进行克隆(clone,指存放在另一个新位置上的对象副本)再返回,而不是直接返回可变对象本身
class Employee
{

public Date getHireDay()
{
return (Date)hireDay.clone();
}

}
方法可以访问所属类任何对象的私有特性,而不仅限于隐式参数
按值调用:方法接收的是调用者提供的值。按引用调用:方法接收的是调用者提供的变量地址
方法可以修改按引用传递的变量值,但是不能修改按值传递的变量值
方法不能修改基本数据类型的参数(即数值型或布尔型)
方法可以改变对象参数的状态
方法不能让一个对象参数引用一个新的对象
方法签名只包括方法名和参数类型,不包括返回值类型,通过方法名来区分不同的方法
不要使用finalize方法来进行清理,这个方法原本要在垃圾回收器清理对象之前调用,但是如果使用这个方法就不能确定到底什么时候调用了,而且该方法已经被废弃
只访问对象而不修改对象的方法称为访问器方法,相反的有更改器方法
Java中值传递和引用传递
按值传递:值传递是指在调用方法时将实际参数复制一份传递到方法中,这样在方法中如果对参数进行修改,将不会影响到实际参数

按引用传递:引用传递就是直接把内存地址传过去,也就是说引用传递时,操作的其实都是源数据,有可能影响原数据,除了基本类型的参数以外,其它的都是引用传递,比如:Object,二维数组,List,Map等

java中所有的参数传递都是传递变量所代表的值的副本,因此,Java中的对象引用还是按值传递的,并不是按引用传递

方法的内存分配与变化
方法只定义不调用是不会执行的,并且在JVM中也不会给方法分配“运行所属”的内存空间,只有在调用方法时才会动态的给这个方法分配所属的内存空间
JVM内存划分上有这三块主要的内存空间:方法区内存、栈内存、堆内存
方法代码片段属于.class字节码文件的一部分,字节码文件在类加载的时候被放到了方法区当中,所以JVM中的三块主要的内存空间中方法区内存最先有数据——方法代码片段
栈内存中分配方法运行的所属内存空间
方法在调用的瞬间,给该方法分配内存空间,在栈中发生压栈动作,方法调用结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作
局部变量运行阶段内存在栈中分配 在这里插入图片描述
堆内存中的数据使用完毕后就变成了垃圾,但是并没有立即回收,会在垃圾回收器空闲的时候回收
方法重载
方法重载(overload):在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
方法重载的特点:1、与方法返回值无关,只看方法名与参数列表;2、在调用时,Java虚拟机通过参数列表的不同自动区分同名方法,这一过程被称为重载解析
方法重写
方法重写(override):在子类中出现和父类中一模一样的方法声明,其也被称为方法覆盖或方法复写
方法重写的作用:当子类需要父类的功能,而功能主体子类有自己特有的内容时,这就可以重写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容(在子类中重写方法时如果想用父类中的方法则需使用super关键字调用,不然只有子类中重写的功能)
父类中的私有方法不能被重写,因为父类的私有方法子类根本无法继承
子类重写父类方法时,访问权限不能更低,即大于或等于,最好权限保持一致
main方法的格式讲解
public static void main(String[] args)
{

}
public:公共的,访问权限最大的修饰符,由于main方法是被JVM调用,所以权限要足够大
static:静态的,不需要创建对象,方便JVM调用
void:被JVM调用,不需要给JVM返回值
main:被JVM识别,程序的执行入口
String[] args:早期是为了接收键盘录入的数据所设置的参数,现在用Scanner了
构造器
构造器是一种特殊的方法(构造方法),用来构造并初始化对象
构造器与类同名,每个类可以有一个以上的构造器,构造器分无参构造和带参构造
构造器没有返回值
构造器总是伴随着new操作符一起调用
对于一个类来说,当你没有写无参构造方法时,系统会默认给出(因为所有类都继承Object类),写了无参构造方法后系统就不会给了,但两者效果一样,唯独当你只写了带参构造方法时,那么,这个类就没有无参构造方法了
在构造器中防止初始化字段为空,有两种方法:

“宽容性”,这种方法是当检测到传入进来的值为空(null)时,便会用一个不为空的值赋值,如下代码:
//构造方法
public Employee(String name)
{
this.name = Objects.requireNonNullElse(name, “空名字”);
}
“严格型”,这种方法是当检测到传入进来的值为空时将自动终止程序,并抛出异常,这种方法的优点:1、异常报告会提供这个问题的描述;2、异常报告会准确地指出代码出错所在的位置,方便改错,如下代码抛出异常并提示“名字不能为空”
public Employee(String name)
{
Objects.requireNonNUll(name, “名字不能为空”);
this.name = name;
}
变量
Java中的变量类型
在这里插入图片描述

成员变量与局部变量
两者区别:

在类中位置不同:成员变量在方法外。局部变量在方法内或者方法声明上
在内存中的位置不同:成员变量在堆内存中。局部变量在栈内存中
生命周期不同:成员变量随着对象的创建而存在,随着对象的消失而消失。局部变量随着方法的调用而存在,随着方法调用完毕而消失
初始化值不同:成员变量有默认的初始值(被final修饰的实例变量必须手动赋值,不能采用系统默认值)。局部变量没有默认的初始值,必须在定义赋值后才能使用
注:局部变量名可以与实例变量名相同,在方法被调用时采用就近原则

访问控制权限修饰符
访问控制权限修饰符是用来控制元素访问范围的
访问控制权限修饰符包括:
访问权限修饰符 功能
public 对外部完全可见
protected 对本包和所有子类可见
缺省(不写修饰符) 对本包可见
private 仅对本类可见
访问控制权限修饰符可以修饰类、实例变量、方法…
访问控制权限修饰符不能修饰局部变量
当希望某个数据只给子类使用时,就可以用protected修饰
访问控制权限修饰符的范围: private < 缺省 < protected < public
封装
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方法
封装是一种信息隐藏技术,在java中通过关键字private实现封装
封装的好处:1、提高了代码的复用性和安全性;2、保证内部结构的安全
封装的原则:屏蔽复杂,暴露简单
this关键字
this关键字指向的是当前类的对象的引用,用来区分同名称的成员变量和局部变量
this只能在类中的非静态方法中使用,静态方法和静态代码块中绝对不能出现this,并且this只和特定的对象关联,而不和类关联,同一类的不同对象有不同的this
this关键字有两个含义:1、指示隐式参数的引用;2、调用该类的其它构造器
static关键字
static(静态关键字),可以修饰成员变量和成员方法
static关键字的特点:1、随着类的加载而加载;2、优先于对象存在;3、被类的所有对象共享(判断是否使用静态关键字static的条件);4、可以通过类名调用(推荐使用)
static方法:static方法也叫作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说是没有this关键字的使用的,因为this关键字要依附于对象,在静态方法中不能访问类的非静态成员,但是,在非静态成员方法中是可以访问静态成员方法或静态变量的
static变量:static变量也叫作静态变量或类变量,静态变量与非静态变量的区别在于:静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化;非静态变量是对象所拥有的,在创建对象的时候被初始化,在内存中存在多个副本,且各对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行 注:
Java中的static关键字不会影响到变量或者方法的作用域,在Java中能够影响访问权限的只有访问权限修饰符
静态成员变量虽然独立于对象,但是仍然可以通过对象进行访问(只要访问权限足够)
static是不允许用来修饰局部变量的
可以理解为静态元素与对象没有关系,它属于类
静态元素中不可以使用this、super关键字
静态变量存储在方法区中的静态区
final关键字
final关键字可以用来修饰类、方法和变量
修饰类:当用final修饰一个类时表明这个类不能被继承,final类中的成员变量并不是默认final修饰的,可以根据需要设为final,但final类中的所有方法却默认final修饰
修饰方法:对于重写问题而言,当父类中的某个方法被final修饰时,就表明父类中的这个方法不能被子类重写,也就是禁止子类重写此方法(主要目的是防止该方法的内容被修改) 注:重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时又被private修饰,此时不会产生重写与final的矛盾,因为子类根本就没有继承这个方法,这个方法被私有化了,既然没有继承何来的重写,final就无作用了,所以当父类中的某个方法的修饰符上同时有private和final时,在子类中依然可以出现同样声明的方法,因为这被视为在子类中重新定义了新的方法
修饰变量:被final修饰的变量就变成了常量,常量不能被重新赋值,只读不可写,Java中定义常量时一般会加上static修饰,因为常量是不变的,任何对象拥有的都一样,如果一直new一样的东西就会浪费内存 注:被定义为final的成员变量必须在构造对象时就被初始化,并且以后不能再修改
final修饰基本数据类型时是值不能被改变,而final修饰引用类型数据时是地址值不能被改变,但是该对象的内容是可以变的
final修饰的实例变量必须手动赋值不能采用系统默认值
父类中的final方法可被子类继承,但是不能被子类重写
final修饰的引用指向的对象无法被垃圾回收器回收
注:当变量被final修饰后,这个变量就变成了常量,既然是常量,那么它在内存中存储的就只是数值了,与之前的变量内存就无关系了,即当变量消失时,常量不会消失,依旧是那个数值在运算,所以,若想某个数据不会因变量消失而消失,就将它修饰为常量

代码块
在Java中使用{ }括起来的代码被称为代码块,根据其位置和声明的不同可以分为:局部代码块、构造代码块、静态代码块、同步代码块

局部代码块:其又叫普通代码块,在方法中出现,限定变量生命周期,主要用于解决当前方法中变量名重复的问题。若想要在一个方法中多次使用同一个变量名,并且互不影响,这时就可以将该变量放入不同局部代码块当中,因局部代码块中的变量生命周期只限于该代码块中
构造代码块:在类中方法外出现,多个构造方法中相同的代码存放到一起,每次调用构造都执行,只要创建对象就会执行构造代码块,主要作用是对对象进行初始化
静态代码块:在类中方法外出现,加了static修饰符,最先被执行,且对于一个类的多个对象只执行一次,其主要作用是对类进行初始化,随着类的加载而执行,与创不创建对象无关
同步代码块:在方法中出现,使用synchronized关键字修饰,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性
以上代码块执行顺序:

静态代码块–>构造代码块–>构造方法–>局部代码块
继承
继承:多个类中存在相同属性(成员变量)和行为(成员方法)时,将这些内容抽取到单独一个类中,那么多个类就无需再定义这些属性和行为了,只要继承那个类即可

通过extends关键字可以实现类与类的继承

class 子类名 extends 父类名{}
单独的类称为父类、基类或超类,子类也叫派生类,父类中的内容是多个子类重复的内容
继承的好处:1、提高了代码的复用性,多个类相同的成员可以放到同一个类中;2、提高了代码的维护性,如果功能的代码需要修改,只需要修改父类这一处即可;3、让类与类之间产生了关系,这是多态的前提(这也是继承的缺点),使得类的耦合性增强
开发的原则:低耦合、高内聚
继承的缺点:1、破坏了封装,子类与父类之间紧密耦合,子类依赖父类的实现,造成子类缺乏独立性;2、支持扩展,但是往往以增强系统结构的复杂度为代价;3、不支持动态继承,在运行时子类无法选择不同的父类;4、子类不能改变父类的接口
继承的特点:1、Java只支持单继承,不支持类的多继承,一个类只能有一个父类,不可以有多个父类;2、Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
子类只能继承父类中所有非私有的成员方法和成员变量
子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法
不要为了部分功能而去继承
在Java中,所有的继承都是公共继承
继承绝对不会删除任何字段或方法
Object类是Java中所有类的父类,如果某个类没有明确地指出父类,那么Object类就被认为是这个类的父类,这个类就默认访问Object类的无参构造
在子类中访问一个变量的执行顺序:首先在子类的局部范围中找,然后在子类成员范围中找,最后在父类成员范围中找(不能访问到父类的局部范围),如果还是没有就报错

super关键字
super有两个含义:1、调用父类的方法;2、调用父类的构造器
调用父类成员变量
super.成员变量
调用父类的构造方法(使用super调用构造器的语句必须是子类构造器的第一条语句)
super(参数)
调用父类的成员方法
super.成员方法
注:

super不是一个对象的引用,不能将值super赋给另一个对象变量,它只是一个指示编译器调用父类方法的特殊关键字
this代表本类对应的引用,通过其操作本类的成员,super代表父类存储空间的标识
继承中构造方法的关系
子类中所有的构造方法默认都会访问父类中的无参构造方法,因为子类会继承父类中的数据,可能还会使用父类的数据,所以子类初始化之前一定要先完成父类数据的初始化(先进行父类的初始化再进行子类的初始化,这叫分层初始化)
子类每一个构造方法的第一条语句都是super(),这是系统默认的,写不写都有,但如果父类没有无参构造方法,这时在子类中系统就不会自动调用父类的无参构造了,需手动调用父类的构造方法,不然会报错
继承使用场景
采用假设法:如果有两个类A、B,只要它们符合A是B的一种或者B是A的一种(A is B)或(B is A)这样的关系,就可以考虑使用继承

继承的设计技巧
通过父类定义子类时,只需要在子类中指出子类与父类的不同之处即可,将通用的字段和方法(不管是否是抽象的)都放在父类(不管是不是抽象类)中,而更特殊的方法就放在子类中
多态
多态:一个对象变量可以指示多种实际类型的现象(某一个对象在不同时刻表现出来的多种状态)
多态的使用前提:1、要有继承关系;2、要有方法重写;3、要有父类引用指向子类对象
父类名 f = new 子类名();
多态的好处:1、提高了代码的维护性;2、提高了代码的扩展性
向上转型
fu f = new zi();
向下转型
zi z = (zi)f;//要求该f必须是能够转换为zi的
注:在进行以上两种转型之前应当对两对象之间进行检查,这是编程的好习惯,instanceof操作符:检查对象之间是否能成功地进行转换 例:

/*
Manager类继承了Employee类
*/
Employee[] staff = new Employee[3];
Manager boss = new Manager();

if(staff[1] instanceof Manager)
{
boss = (Manager)staff[1];
}

多态中的成员访问特点
成员变量:编译看父类,运行看父类
成员方法:编译看父类,运行看子类(由于只有成员方法存在方法重写,所以它运行看子类)
静态方法:编译看父类,运行看父类(静态和类相关,算不上重写,所以访问的还是父类的)
动态绑定(后期绑定):在运行时能够自动的选择适当的方法,java中的动态绑定是默认行为,动态绑定是多态得以实现的重要因素
静态绑定(前期绑定):在程序执行前已经被绑定,即在编译过程中就已经知道这个方法是哪个类的方法,此时由编译器获取其它连接程序实现。在Java中,final、private、static修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。
抽象类
抽象:所谓抽象就是不是一种具体的事物而是一类事物的总和,如人分男人和女人,那么男人和女人相对于人来说就是具体的,人就是抽象的
抽象类:被abstract修饰的类叫抽象类,抽象类没有实体的东西,其无法直接创建对象(不能被实例化),因为调用抽象方法无意义,如果父类为抽象类,那么子类只有重写了父类中的所有方法抽象方法后才可以创建对象,否则该子类还是一个抽象类,因为假如子类没有对抽象类的所有方法重写,那么子类也就会继承了抽象类中的抽象方法,只要有抽象方法的类就一定是抽象类,但抽象类不一定有抽象方法
抽象类的思想:强制子类重写抽象方法
抽象类的作用:降低接口实现类对接口的实现难度,将接口中不需要使用的方法交给抽象类实现,这样接口实现类只需要对要使用的方法进行重写
抽象方法:被abstract修饰的方法叫抽象方法,其没有方法体,且抽象方法必须存在于抽象类中 注:抽象方法是没有方法体,没有方法体不代表空方法体
public abstract void eat(){…}//这叫空方法体,会报错
public abstract void eat();//这才叫无方法体
抽象类不可被实例化
抽象类是有构造器的
抽象类可以没有抽象方法
抽象类的使用场景一般在运用多态时比较适用
abstract不能与这几个关键字共用:private(私有的方法子类是无法继承的,但是abstract又要求子类需要实现抽象方法,这是矛盾的)、final(类、方法被final修饰后不能被继承和重写,矛盾)、static(通过类名访问抽象方法是没有意义的,因为抽象方法没有方法体)
抽象类中的抽象方法是强制要求子类做的事情,非抽象方法是子类继承的事情,提高代码的复用性
接口
接口:接口是Java 语言中的一种引用类型,它是抽象方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法
接口用关键字interface修饰
public interface 接口名{}
接口不是类,它是另外一种引用数据类型
接口不能创建对象,但是可以被实现,一个实现接口的类需要实现接口中所有的抽象方法,否则它就是抽象类
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类或接口的子类,实现用implements关键字修饰
class 类名 implements 接口名{}
接口不能定义构造方法(因为接口的作用主要是为了扩展功能,并不具体存在,没必要初始化)且接口中只有常量(隐式修饰,public static final)
接口不能创建对象,只能通过其实现类来使用
一个接口可以有多个方法且接口中所有的方法必须是抽象方法,默认修饰符public abstract(隐式修饰),如果需要定义具体方法实现,则此时方法需要使用default修饰
接口不是被类继承而是被类实现
一个接口能继承另一个接口且接口支持多继承
public interface 接口1 extends 接口2, 接口3{}
接口中不能含有静态代码块以及静态方法(这里编译器不会报错,只是在实际开发中这样做是没有意义的)
一个类可以实现多个接口
接口中的方法都是公有的
接口是隐式抽象的,当声明一个接口的时候不必使用abstract修饰
接口中每一个方法是隐式抽象的,声明同样不需要abstract关键字
接口的作用是降低耦合度和扩展功能
注:开发中实现接口的类的类名命名格式:接口名+Impi

接口与抽象类的区别:抽象类中定义的是该继承体系的共性功能,而接口中定义的是该继承体系中的扩展功能(特性功能)
当引用类型作形式参数或返回值时: 类:需要的是该类的对象 抽象类:需要的是该抽象类的子类对象(多态) 接口:需要的是该接口的实现类对象(多态)
简单修饰符归纳
类的修饰符可有:public、缺省、final、abstract 注:成员内部类可有private和static修饰符,其余的普通类不行,因为成员内部类可以看作是外部类的成员

成员变量的修饰符可有:private、缺省、protected、public、static、final

构造方法的修饰符可有:private、缺省、protected、public

成员方法的修饰符可有:private、缺省、protected、public、static、final、abstract

内部类
内部类:一个类定义在另一个类里面或一个方法里面,这样的类称为内部类,内部类一般包括:成员内部类、局部内部类、匿名内部类、静态内部类
内部类和外部类之间没有继承关系
直接访问内部类成员的语法格式:

外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
成员内部类
成员内部类是最普通的内部类,位于另一个类的内部
成员内部类可以无条件的访问外部类的所有成员变量和成员方法(包括private成员和静态成员)
当成员内部类拥有和外部类同名的成员变量或方法时,默认情况下访问的是内部类的成员(就近原则),若要访问外部类的同名成员,语法格式为:
外部类名.this.成员变量
外部类名.this.成员方法
在外部类如果要访问成员内部类的成员就必须先创建一个内部类的对象,再通过指向这个对象的引用来访问
内部类是依附于外部类存在的,如果要创建内部类的对象,前提是必须存在一个外部类的对象
局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的作用范围只限于方法内或该作用域内
局部内部类可以无条件的访问外部类的所有成员变量和成员方法(包括private成员和静态成员)
局部内部类就像是方法里面的一个局部变量一样是不能有public、protected、private以及static修饰的
匿名内部类
匿名内部类是没有名字的内部类且它只能使用一次(和匿名对象相似),通常用来简化代码
匿名内部类的实质是对象(可将匿名内部类当作对象使用),并不是类,它是一个类或子类或实现接口类的一个对象,是一个继承了该类或实现了该接口的子类匿名对象
匿名内部类不能有访问修饰符和static
匿名内部类使用条件:必须继承一个父类或实现一个接口
匿名内部类是唯一一种没有构造方法的类,因此它的使用范围非常有限,大部分匿名内部类用于接口回调
注:一般来说,匿名内部类用于继承其他类或实现接口,并不需要增加额外的方法,只是对继承方法的实现或重写,语法格式:

new 类名或者接口名(){重写方法}//可以理解为一个对象
静态内部类
静态内部类是被static修饰的内部类
当外部类被加载的时候,静态内部类是不会被加载的
当使用静态内部类中的成员时,静态内部类才会被加载且仅仅加载一次,不会有线程安全的问题
与静态成员变量不一样的是静态内部类是不能通过外部类对象访问的
静态内部类不能使用外部类的非静态成员变量和方法,只能访问外部类的静态成员
静态内部类的唯一访问语法格式:

外部类名.内部类名 对象名 = new 外部类名.内部类名();
JVM的类加载规则
static类型的变量和方法在类加载的时候就会存在于内存中
要想使用某个类的static变量或方法,那么这个类就必须要加载到Java虚拟机中
非静态内部类并不随外部类一起加载,只有实例化外部类之后才会加载
注:现在考虑这个情况:外部类并没有实例化,内部类还没有加载,这时候如果调用内部类的静态成员或方法,内部类还没有加载却试图在内存中创建该内部类的静态成员,这是明显矛盾的,所以非静态内部类不能有静态成员变量或静态方法(这里就和非静态方法不一样了)

Java中的String
在 Java 中字符串(String)属于对象,并不是基础的数据类型
字符串直接赋值的方式是先到字符串常量池(方法区中)里面去找,如果有就返回,没有就创建对象并返回
//以下两者的区别
String s1 = new String(“hello”);//创建2个或1个对象,new操作符是一定要创建对象的
String s2 = “hello”;//创建1个或0个对象
String作参数传递时,虽然它是引用类型,但是效果和基本类型作参数传递是一样的,要将它看作基本类型
Java中的StringBuffer和StringBuilder
它们与String不同的是StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象(除截取功能外)。
StringBuffer和StringBuilder相同点:1、两个类都是用来解决大量字符串拼接时而产生的中间对象问题;2、都需要实例化创建对象;3、都可以看作可变长度字符串,初始化容量都为16
StringBuffer和StringBuilder不同点:StringBuffer是线程安全的(数据安全),运行速度较慢,StringBuilder是非同步的(不安全),效率高,运行速度较快
三者使用场景

String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况(多数情况下使用)
StringBuffer:适用于多线程下在字符缓冲区进行大量操作的情况
以下说明用StringBuilder类举例,对于StringBuffer类同等适用 在这里插入图片描述

StringBuilder的构造方法:

StringBuilder();
StringBuilder(int size);//指定初始容量的字符串缓冲区(不一定是实际长度)
StringBuilder(String str);//指定字符串内容的字符串缓冲区
StringBuilder类的常见功能(除截取功能外,其余功能返回StringBuilder本身):

1、添加功能
public StringBuilder append(String str);//可以把任意类型添加到字符串缓冲区里
public StringBuilder insert(int offset, String str);//在指定位置把任意类型的数据插入到字符串缓冲区里

2、删除功能
public StringBuilder deleteCharAt(int index);//删除指定位置的字符
public StringBuilder delete(int start, int end);//删除从指定位置开始到指定位置结束的这一范围的内容

3、替换功能
public StringBuilder replace(int start, int end, String str);

4、翻转功能
public StringBuilder reverse();

5、截取功能
public String subString(int start);//从start开始到末尾
public String subString(int start, int end);//从start到end
注:StingBuilder类中的截取方法不再是返回本身了,而是重新开辟空间

String与StringBuilder相互转换

String转StringBuilder
1、通过构造方法
StringBuilder s1 = new StringBuilder(“字符串”);

2、通过append()
StringBuilder s3 = new StringBuilder();
s3.append(“字符串”);
StringBuilder转String
StringBuilder builder = new StringBuilder(“字符串”);

1、通过构造方法
String str = new String(builder);

2、通过toString()
String str = builder.toString();
String和int的相互转换

String转int
Integer.parseInt(“数字字符串”);
int转String
String.valueof(int型数据);
增强for
Java中的增强for(for each循环)是一种很强的循环结构,可以用来依次处理数组中的每一个元素,不必考虑下标的问题

for(int element : a)//这里的a是一个数组名或集合
{
System.out.println(element);//输出每一个元素
}
增强for的循环变量将会遍历数组中的每一个元素而不需要使用下标值
for each循环的局限性:该循环只能遍历整个数组,每个元素都会用到且循环内部没有下标处理
增强for的目标不能为null,所以要在使用前考虑做判断
增强for其实是用来替代迭代器的
包装类
包装类类型:为了对基本数据类型进行更多更方便的操作,Java就针对每一种基本类型数据提供了对应的类类型,即将基本类型转化为对象操作
基本类型 引用类型(类)
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
Integer类中常用的方法:

1、String转int
public static int parseInt(String s);//字符串s必须为由数字组成的字符串,字母的不行

2、进制转换(radix的范围:2<= radix <= 36)
public static String toString(int i, int radix);//i表示被转换的数(十进制数),radix表示进制数

注:

包装类都是被final修饰的,因此不能派生它们的子类
自动装箱规范要求boolean、byte、char<= 127
介于-128和127之间的short和int被包装到固定的对象中,不会创建新的对象 在这里插入图片描述
如果在一个条件表达式中混合使用Integer和Double类型,Integer值会拆箱,提升为double再装箱为Double
装箱和拆箱是编译器要做的工作而不是虚拟机的,编译器在生成类的字节码时会插入必要的方法调用,虚拟机只是执行这些字节码
Character类中常用的方法:

1、判断给定的字符大小写
public static boolean isUpperCase(char ch);//大写返回true,小写返回false
public static boolean isLowerCase(char ch);//大写返回false,小写返回true

2、判断给定的字符是否是数字
public static boolean isDigit(char ch);

3、把给定的字符转成大小写
public static char toUpperCase(char ch);
public static char toLowerCase(char ch);
Math类
Math类常用方法:

1、取绝对值
public static int abs(int a);

2、向上取整
public static double ceil(double a);

3、向下取整
public static double floor(double a);

4、取最大值
public static int max(int a, int b);

5、a的b次幂
public static double pow(double a, double b);

6、取随机值
public static double random();//返回值范围[0.0, 1.0)

7、四舍五入
public static int round(float a);

8、正平方根(开方)
public static double sqrt(double a);

Random类
Random类作用:产生随机数的类
Random类的构造方法:

Random();//默认种子,每次产生的随机数不同
Random(long seed);//指定种子,每次种子相同,随机数就相同
Random类常用方法:

int nextInt();//返回int范围内的随机数
int nextInt(int n);//返回[0, n)范围内的随机数
int nextDouble();//随机获得一个0-1的double类型数据,括号里不填
int nextFloat();//随机获得一个0-1的float的类型数据
int nextBoolean();//随机获得true或false
System类(系统类)
System类常用方法:

1、运行垃圾回收器
public static void gc();

2、退出JVM
public static void exit(int status);//status - 退出状态。

3、获取当前时间的毫秒值
public static long currentTimeMillis();

4、复制数组
public static void arraycopy(Object src, int srcpos, Object dest, int destpos, int length);
参数:
src:源数组
srcpos:源数组中的起始位置
dest:目标数组
destpos:目标数组中的起始位置
length:要复制的数组元素的数量
BigInteger类
BigInteger类是针对大整数的运算
BigInteger类的构造方法(以下只举一种)
public BigInteger(String val);//字符串val必须是数字字符串
BigInteger类中常用的成员方法
1、加法
public BigInteger add(BigInteger val);

2、减法
public BigInteger subtract(BigInteger val);

3、乘法
public BigInteger multiply(BigInteger val);

4、除法
public BigInteger divide(BigInteger val);

5、返回商和余数的数组,该数组中只有两个元素:商array[0]、余数array[1]
public BigInteger[] divideAndRemainder(BigInteger val);
BigDecimal类
用于浮点数运算,避免丢失精度
BigDecimal类的构造方法(以下只举一种)
public BigDecimal(String val);//字符串val必须是数字字符串
BigDecimal类中常用的方法
1、加法
public BigDecimal add(BigDecimal augend);

2、减法
public BigDecimal subtract(BigDecimal subtrahend);

3、乘法
public BigDecimal multiply(BigDecimal multiplicand);

4、除法
public BigDecimal divide(BigDecimal divisor);

5、对除法运算结果的操作
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode);
参数:
divisor:被除数
scale:小数点后位数
roundingMode:舍取模式,一般用四舍五入模式(BigDecimal.ROUND_HALF_UP)
Date类
Date类是日期类,可以精确到毫秒
Date类的构造方法
public Date();//根据当前的默认毫秒值创建对象
public Date(long date);//根据给定的毫秒值创建对象,data是毫秒值,日期是从1970年1月1日0:0:0开始计算,data加上这个时间即可
Date类中常用的方法
1、获取时间,以毫秒为单位返回
public long getTime();

2、设置时间
public void setTime(long time);
DateFormat类
DateFormat类是针对日期进行格式化和针对字符串进行解析的类,但是该类是抽象类,所以使用其子类SimpleDateFormat
SimpleDateFormat类构造方法
public SimpleDateFormat(String pattern);//指定日期的格式(如:yyyy-MM-dd HH:mm:ss)
public SimpleDateFormat();//默认格式表示日期
SimpleDateFormat类的常用方法
1、Date转String(格式化)
public final String format(Date date);

2、String转Date(解析)
public Date parse(String source);
Calendar类
Calendar类是日历类,它是一个抽象类,其封装了所有的日历字段值,通过统一的方法根据传入不同的日历字段可以获取值
Calendar类中常用的方法
1、获取一个日历对象
Calendar rightNow = Calendar.getIntstance();

2、返回给定日历字段的值,日历字段中的每个日历字段都是静态的成员变量,并且是int型
public int get(int field);

3、根据给定的日历字段和对应的时间来对当前的日历进行操作
public void add(int field, int amount);

4、设置当前日历的年月日
public final void set(int year, int month, int date);
集合
数组和集合类同是容器,区别在于:1、数组虽然也可以存储对象(对象数组),但长度是固定的,集合长度是可变的;2、数组可存储基本数据类型,但集合只能存储对象的内存地址(引用),且集合可存储不同类型的对象

集合继承体系图: 在这里插入图片描述

ArrayList:底层数据结构是动态数组,默认初始化容量为10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为10),自动扩容(扩容到原容量的1.5倍),支持随机访问,查询快,增删慢,非同步,线程不安全,效率高
Vector:底层数据结构是动态数组,查询快,增删慢,同步,线程安全,效率低
LinkedList:底层数据结构是双向链表,不支持随机访问,查询慢,增删快,非同步,线程不安全,效率高
Collection
Collection中的常用功能(以下功能的结果遵循谁调用这个方法谁就改变)
1、添加功能
boolean add(Object obj);//添加一个元素
boolean addAll(Collection c);//添加一个集合的元素

2、删除功能
void clear();//移除所有元素
boolean remove(Object o);//移除一个元素
boolean removeAll(Collection c);//移除一个集合的元素,只要有一个元素被移除就返回true

3、判断功能
boolean contains(Object o);//判断集合中是否包含指定元素
boolean containsAll(Collection c);//判断集合中是否包含指定的集合元素,只有包含所有元素才叫包含,才返回true
boolean isEmpty();//判断集合是否为空

4、长度功能
int size();//这里获取的是元素的实际个数,不是集合的容量

5、交集功能
/*
设有两个集合A、B
A.retainAll(B);
若A、B之间有交集就把交集保存到集合A中,若无交集,那么集合A就为空,
至于返回结果则取决于保存交集的A集与保存之前的集合内容是否有变化,
有变化就返回true,没有变化就返回false
*/
boolean retainAll(Collection c);

6、集合转数组
Object[] toArray();//转型后的数组中每一个元素都是Object类型的
迭代器
集合的迭代也叫集合的遍历,集合的迭代是通过迭代器完成的,迭代器是依赖于集合而存在的,也就是有集合才有迭代器

迭代器(集合的专用遍历方式)
Iterator iterator();
Iterator接口中的方法:
Object next();//获取元素之后自动后移
boolean hasNext();//判断是否还有元素,如果仍有元素可以迭代就返回true
迭代举例
Collection c = new ArrayList();
方式一:
Iterator it = c.iterator();//通过集合对象获取迭代器对象
while(it.hasNext())
{
System.out.println(it.next());
}

方式二:
for(Iterator it = c.iterator(); it.hasNext()😉
{
System.out.println(it.next());
}

总:方式一结构清晰;方式二效率更高,运行更快,因为it循环之后就变成垃圾

问:迭代器为什么不定义成一个类,而定义成一个接口? 答:Java中提供了很多的集合类,而这些集合类的数据结构是不同的,意味着它们的存储方式和遍历方式也各不相同,遍历一个集合应当具备判断功能和获取功能,因每种集合的方式不太一样,所以把这两个功能提取出来,这种方式就是接口,这个接口的实现类是以内部类的方式体现的

List
List集合:有序的Collection(也称为序列),此接口的用户可以对列表中每个元素的插入位置进行精确控制,用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素,与Set不同的是List允许重复的元素
List集合的特点:1、有序(存储和取出的元素顺序一致);2、可重复
List集合特有的遍历
List c = new ArrayList();
for(int i = 0; i < c.size(); i++)
{
System.out.println(c.get(i));
}
List集合特有的迭代器(列表迭代器)
列表迭代器:ListIterator listIterator();
该迭代器继承了Iterator迭代器,所以也可直接使用hasNext()和next()

列表迭代器特有的功能:
逆向遍历:
Object previous();//获取上一个元素
boolean hasprevious();//判断是否有元素
正向遍历:
Object next();
boolean hasNext();

注:ListIterator可以实现逆向遍历,但是必须同一迭代器正向遍历之后才能进行逆向遍历,无实际意义,一般不使用
LinkedList
LinkedList集合特有的功能
1、添加功能
public void addFirst(Object o);//开头添加
public void addLast(Object o);//结尾添加

2、获取功能
public Object getFirst();//获取开头元素
public Object getLast();//获取结尾元素

3、删除功能
public Object removeFirst();//删除开头元素并返回被删除元素
public Object removeLast();//删除结尾元素并返回被删除元素
Map
Map集合:Map集合中键和值的关系——映射,可以通过键来获取值
Map集合的特点:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值(无序不重复)
Map集合与Collection集合的区别:1、Map集合存储元素是成对出现的(键值对),Map集合的键是唯一的,值是重复的,Collection集合存储元素是单独出现的,Collection的Set是唯一的,List是可重复的;2、Map集合的数据结构针对键有效,跟值无关,Collecton集合的数据结构针对元素有效
给定一个键和一个值就可以将该值存储在一个Map对象中,之后可通过键来访问对应的值
当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常
当对象的类型和Map里元素类型不兼容时,方法就会抛出一个ClassCastException异常
当在不允许使用Null对象的Map中使用Null对象时,方法就会抛出一个NullPointerException异常
Map集合中常用的功能:
1、添加功能
/*
(key表示键,value表示值)如果键是第一次存储就直接存储元素并返回null,
如果键不是第一次存储就用现在的值把以前的值替换掉并返回以前的值
*/
v put(k key, v value);

2、删除功能
void clear();//移除所有的键值对元素
v remove(Object key);//根据键删除键值对元素并把值返回

3、判断功能
boolean containsKey(Object key);//判断集合中是否包含指定的键
boolean containValue(Object value);//判断集合是否包含指定的值
boolean isEmpty();//判断集合是否为空

4、长度功能
int size();//返回集合中键值对的对数

5、获取功能
v get(Object key);//根据键来获取值
Set keySet();//获取集合中所有键的集合
Collection values();//获取集合中所有值的集合
Set<Map.Entry<K,V>> entrySet();//返回的是键值对对象的集合
Map集合的遍历 在这里插入图片描述
Hashtable与HashMap的区别:

Hashtable:线程安全,效率低,不允许null键和null值,初始容量为11,扩容是:原容量*2+1
HashMap:线程不安全,效率高,允许null键和null值
哈希表的数据结构
在这里插入图片描述 注:

如果一个类的equals()方法重写了,那么它的hashCode()方法必须重写
放在HashMap集合Key(键)部分的和放在HashSet集合中的元素需要同时重写hashCode()方法和equals()方法
为了提高检索效率,在JDK8之后,如果哈希表的单向链表中元素大于等于8个 并且 数组长度大于等于64时,单向链表这种数据结构才会变成红黑树数据结构,如果数组长度小于64则会先将数组进行一次扩容(2倍),如果扩容之后还没有达到长度64则继续比较下一个节点,链表不会转成红黑树,直到table数组长度大于等于64时单向链表才会变成红黑树,当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表
HashMap的默认初始化容量是16(建议是2的倍数),加载因子是0.75(当哈希表的元素超过总容量的75%时,哈希表将自动开始扩容(原容量*2)
TreeSet
TreeSet集合底层实际上是一个TreeMap,也就是一个二叉树,放到TreeSet集合中的元素等同于放到TreeMap集合的Key(键)部分了,TreeSet集合中的元素无序不重复,可以按照元素的大小顺序自动排序

重点: TreeSet集合自动排序的特点是通过实现java.lang包下Comparable接口或java.util包下的Comparator接口来完成的,即要指定比较规则。如String、Integer等已经默认实现了Comparable接口,所以TreeSet中的String、Integer就不用再指定比较规则了,而放到TreeSet或TreeMap集合key部分的自定义元素要想做到排序就必须指定比较规则,有如下两种方式:

自定义元素实现Comparable接口中的compareTo()方法,在compareTo()方法中指定比较规则,这时TreeSet或TreeMap集合的构造方法是无参构造 在这里插入图片描述 在这里插入图片描述

在构造TreeSet或TreeMap集合时,用匿名内部类的方式实现比较器Comparator接口中的compare()方法(也可以单独建一个比较器类实现Comparator接口),在compare()方法中指定规则,这时TreeSet或TreeMap集合的构造方法是带参构造 在这里插入图片描述

说明:compareTo()方法的返回值:

返回值0表示相同,value会覆盖
返回值大于0,会继续在右子树上找(左小右大)
返回追小于0,会继续在左子树上找(左小右大)
问:Comparable和Comparator怎么选择 答:当比较规则不会发生改变的时候,或者说当比较规则只有一个的时候建议使用Comparable接口,如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口

Collections
Collections是针对集合进行操作的工具类,其中的方法都是静态的

Collection与Collections的区别:

Collection:是单列集合的顶层接口,有子接口List和Set
Collections:是针对集合操作的工具类
Collections类中的常用方法:

1、排序,默认情况下是自然排序
public static void sort(List list);

2、二分查找
public static int binarySearch(List<?> list,T key);

3、最大值(根据元素的自然顺序)
public static T max(Collection<?> coll);

4、最小值
public static T min(Collection<?> coll);

5、随机转换
public static void shuffle(List<?> list);

6、反转
public static void reverse(List<?> list);

7、将线程不安全的List转换成线程安全的List(Set和Map也有类似的方法)
public static List synchronizedList(List list);

泛型
泛型是一种把数据类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型,也叫参数化类型,把类型当作参数一样传递

泛型的格式:
<数据类型> 注:这里的数据类型只能是引用类型

例:ArrayList array = new ArrayList();//array这个集合中所有元素都是String类型的,且只能是String类型的
泛型的好处:1、把运行时期的问题提前到了编译期间;2、避免了强制类型转换;3、优化了程序设计;4、增强了程序的安全性
看API,如果类、接口、抽象类后面跟有< E >就表示要使用泛型,一般来说在集合中使用的情况居多
泛型通配符
< ? >:任意类型,如果没有明确,那么就是Object以及任意的Java类了
泛型如果明确时前后必须一致
?表示任意的类型都可以(后面new的类型是任意引用类型)
Collection<?> c1 = new ArrayList(); Collection<?> c2 = new ArrayList();
Collection<?> c3 = new ArrayList();
:向下限定,后面new的类型必须是E及其子类
Collection<? extends Animal> c = new ArrayList();//Dog继承类Animal
:向上限定,后面new的类型必须是E及其父类
静态导入
可以直接导入到静态方法的级别

静态导入格式
import static 包名.类名.方法名;

import static java.lang.System.out;
out.println(“java”);
方法必须是静态的
当有多个同名静态方法时,就必须加前缀使用了,即用静态导入的方法是唯一的
可变参数
当定义方法的时候不确定该定义多少个参数时就使用可变参数
public int sum(int…a);//这里必须是三个点
可变参数本质上是一个数组
如果一个方法有可变参数,并且有多个参数时,可变参数必须写在最后面
public int sum(int b, int c, int…a);
一个方法只能指定一个可变参数
枚举
枚举(enum)是一种引用数据类型,编译之后生成class文件,枚举中的每一个值可以看作常量

枚举语法格式;

enum 枚举类型名
{
枚举值1, 枚举值2, 枚举值3…
}
说明:结果只有两种情况的建议使用布尔类型,结果超过两种并且可以一枚一枚列举出来的建议使用枚举类型,如颜色、四季、星期等都可以使用枚举类型

下列程序只是为了演示枚举语法,并无实际开发意义: 在这里插入图片描述

异常
异常继承体系图
在这里插入图片描述

错误:错误不是异常而是脱离程序员控制的问题,Java程序通常不捕获错误,错误一般发生在严重故障时,它们在Java程序处理的范畴外
编译时异常:也叫受检异常或受控异常,编译时异常不是在编译阶段发生的(所有的异常都发生在运行阶段),它表示必须在编译(编写)程序的时候预先对这种异常进行处理,如果不处理编译器就报错(直接父类为Exception的异常称为编译时异常)
运行时异常:也叫未受检异常或未受控异常,Runtime Exception的子类称为运行时异常,运行时异常在编译阶段可处理也可不处理
异常在Java中以类的形式存在,每一个异常类都可以创建异常对象
不管是错误还是异常都是可抛出的,所有的错误只要发生,Java程序只有一个结果,那就是终止程序的执行,退出JVM,因为错误是不可处理的
处理异常的两种方式
在方法内部使用try…catch…语句进行异常捕捉,可理解为异常自行处理
try…catch…语法格式:

try{
//可能出现异常的代码
}catch(异常类名 变量名){
//针对异常的提示处理
}finally{
//不管try内有没有异常出现,finally语句块中的语句一定执行
//这里一般是资源的释放和关闭
}

注:
1、虽然说所有的异常类名都可写Exception,但异常类名能明确就明确,方便调错
2、try语句块中的某一行出现异常该行后面的代码不会执行,只有在try…catch…捕捉异常后,后续代码才可执行
3、当try语句块中有return时,finally中的语句也会执行,且return语句在finally之后执行,也就是最后执行
4、如果在执行finally之前JVM退出了(System.exit(0)),那么就不会再执行finally里的语句了
出现多个异常时:

方式一:针对每一个异常写一个try...catch...

方式二:将异常集中在一个try中,多个catch
try{
	...
}catch(异常类名1 变量名){
	...
}catch(异常类名2 变量名){
	...
}

注:平级关系的异常谁前谁后无所谓,如果出现父子关系,那么父必须出现在子的后面,否则会报错

JDK8的一个新异常处理方式

try{

}catch(异常类名1 | 异常类名2 | … 变量名){

}

注:这个方法很简洁,特点是处理方式一致,且多个异常间必须是平级关系
异常对象有两个重要的方法

1、获取异常简单的描述信息
public String getMessage();

2、打印异常追踪的堆栈信息
public void printStackTrace();
例: 在这里插入图片描述

在方法声明的位置上使用throws关键字,将异常抛给调用者处理(上抛),调用者也面临这两种处理方式
语法格式:

throws 异常类名(可以跟多个异常类名)
在这里插入图片描述

throws:用于方法声明后面,其后跟的是异常类名,将异常上抛给调用者处理,抛出的异常可能会发生
throw:手动抛异常,用在方法体内,其后跟的是异常对象名,且只能抛出一个异常对象,抛出的异常一定会发生。注意:1、当抛出的是编译时异常就必须与throws连用(因为编译时异常必须处理),而抛出的是运行时异常就不必与throws连用(因为运行时异常可处理也可不处理)。2、只要异常没有被捕捉,采用上抛的方式,此方法的后续代码不会执行
public static void myException() throws ClassNotFoundException
{
Class.forName(“文件路径”);
//因为异常只是被上抛,没有被处理,所以以下代码不会执行
System.out.println(“Hello”);
}
如果方法内部可以将异常处理就用try,如果内部处理不了就用throws上抛

自定义异常
分两步:

编写一个类继承Exception或Runtime Exception,要编写编译时异常就继承Exception,如果编写运行时异常就继承Runtime Exception
提供两个构造方法,一个无参构造,一个带有String参数的构造方法
public class MyException extends Exception //编译时异常
{
public MyException()
{

}
public MyException(String s)
{
	super(s);
} 

}
final、finally、finalize
final:关键字,表示最终,修饰类时类不能被继承,修饰方法时方法不能被重写,修饰变量时变量变常量
finally:关键字,是异常处理的一部分,用于释放资源,一般来说其中的代码肯定会执行,特殊情况:在执行到finally之前JVM退出了
finalize:标识符,Object类的一个方法,用于垃圾回收
异常注意事项
子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类
如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内部有异常产生,那么子类只能try…catch…
IO流
IO流的释义
IO流是数据传输的通道/管道,是实现数据输入和输出的基础,通过IO流可以完成硬盘文件的读和写

IO流的分类
在这里插入图片描述

按照流的方向进行分类(以内存作为参照物):

输入流:数据从硬盘到内存里去叫作输入或读,可理解为读进去
输出流:数据从内存中出来到硬盘叫作输出或写,可理解为写出来
按照数据的读取方式进行分类:

字节流:按照字节的方式读取数据,一次读取一个字节byte,等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件、图片、声音文件、视频文件
字符流:按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,但这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,也读不了word文件
总:在Java中只要类名以“Stream”结尾的都是字节流,以“Reader/Writer”结尾的都是字符流

字节输入流:InputStream
字节输出流:OutputStream
字符输入流:Reader
字符输出流:Writer
注:1、以上所有的流都实现了Closeable接口,表明所有的流都可以关闭,都有close()方法,切记用完流之后一定要关闭流。2、所有的输出流都实现了Flushable接口,表明所有的输出流都是可以刷新的,都有flush()方法,输出流在最终输出完后一定要记得flush()方法刷新一下,这个刷新表示将通道当中剩余未输出的数据强行输出完(清空通道),如果没有flush()方法可能为导致数据丢失

文件专属流
字节输入流(FileInputStream)
常用方法

1、返回流当中剩余的没有读到的字节数量
public int available();

2、跳过n个字节不读
public long skip(long n);

3、返回读到的字节值,每次读一个字节,读完自动后移,文件读完返回-1
public int read();

4、将读到的内容存入字节数组中,且最多读入b.length个字节,返回读到的字节总数,文件读完返回-1
public int read(byte[] b);

字节输出流(FileOutputStream)
常用方法

1、将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流
public void write(byte[] b, int off, int len);

字符输出流与字符输入流的常用方法与以上的区别在于把byte数组换成了char数组
文本复制: 在这里插入图片描述

用字节流完成文本复制
FileInputStream fis = null;
FileOutputStream fos = null;
try{
//创建字节输入流对象
fis = new FileInputStream(“src/lianxi_io/myio_1”);
//创建字节输出流对象,true表示追加
fos = new FileOutputStream(“src/lianxi_io/myio_2”, true);

		byte[] bytes = new byte[1024];
		int rcount = 0;
		while((rcount = fis.read(bytes)) != -1)
		{
			fos.write(bytes, 0, rcount);
		}
		//输出流刷新(刷新缓冲区)
		fos.flush();
	} catch (FileNotFoundException e){
		
		e.printStackTrace();
	} catch (IOException e){
		e.printStackTrace();
	} finally {
		//关闭流
		if(fis != null)
		{
			try{
				fis.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
		
		if(fos != null)
		{
			try{
				fos.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
	}

转换流
InputStreamReader
注:当一个流的构造方法中需要一个流的时候,这个被传进来的流叫作节点流,外部负责包装的这个流叫包装流或处理流,只需关闭包装流就行,当包装流关闭后节点流会自动关闭

缓冲流
缓冲流是为了提高数据的读写速度而存在的,因为缓冲流自带缓冲区,不需要再定义数组

BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
注:其中BufferedReader的特殊方法

一次读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达文本末尾,则返回 null
public String readLine();

1
2
3
数据流
数据流中DateOutputStream可以将数据连同数据的类型一并写入文件中,这个文件不是普通的文本文件,用记事本打不开,只能使用DateInputStream去读,并且读的时候需要与写的顺序一致才可以正常取出数据

标准输出流
printStream
printWriter
标准的输出流默认输出到控制台

System.out.println(“Hello”);
1
改变输出方向,输入到指定文本中

PrintStream ps = new PrintStream(new FileOutputStream(“src/lianxi_io/myio_1”, true));
//改变输出方向
System.setOut(ps);

多线程

程序、进程、线程的理解

  1. 程序(programm)

    概念:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码

  2. 进程(process)

    1. 概念:程序的一次执行过程,或是正在运行的一个程序
    2. 说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
  3. 线程(thread)

    1. 概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
    2. 说明:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。

    在这里插入图片描述

  4. 补充:内存结构

在这里插入图片描述

  1. 进程可以细化为多个线程。
  2. 每个线程,拥有自己独立的:栈、程序计数器
  3. 多个线程,共享同一个进程中的结构:方法区、堆。

并行与并发

  1. 单核CPU与多核CPU的理解
    1. 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费。)但是因为CPU时间单元特别短,因此感觉不出来。
    2. 如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
    3. 一个Java应用程序java.exe,其实至少三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
  2. 并行与并发的理解
    1. 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
    2. 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

创建多线程的两种方式

  1. 方式一:继承Thread类的方式:

    1. 创建一个继承于Thread类的子类
    2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中
    3. 创建Thread类的子类的对象
    4. 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()

    说明两个问题:

    • 问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。
    • 问题二:如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start().
  2. 方式二:实现Runnable接口的方式:

    1. 创建一个实现了Runnable接口的类
    2. 实现类去实现Runnable中的抽象方法:run()
    3. 创建实现类的对象
    4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
    5. 通过Thread类的对象调用start()
  3. 两种方式的对比:

    1. 开发中:优先选择:实现Runnable接口的方式。原因:

      1. 实现的方式没有类的单继承性的局限性
      2. 实现的方式更适合来处理多个线程有共享数据的情况。
    2. 联系:public class Thread implements Runnable

    3. 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

      目前两种方式,要想启动线程,都是调用的Thread类中的start()。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值