Java简介
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1] 。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3]
数据类型
数据类型包括:基本数据类型和引用数据类型,基本数据类型主要为八种,byte、short、int、long、float、double、boolean、char、String ;引用类型包括对象、数组,其引用类型的默认值为null。
基本类型
八种基本数据类型,四种整数类型、两种浮点型、一种布尔型、一种字符型。
数据类型 | 说明 | 默认值 |
---|---|---|
byte | 8位二进制,其取值范围为(-128 ~ 127)2^7 | 0 |
short | 16位二进制,其取值范围为(-32768 ~ 32767)2^15 | 0 |
int | 32位二进制,其取值范围为 (-263)(263 -1) | 0 |
long | 64位二进制,其取值范围为**(-2^63)**到 (2^63 -1) | 0L |
float | 单精度、32位,不能表示精确的数据,如货币金额等 | 0.0f |
double | 双精度、64 位、浮点数的默认类型为 double 类型 | 0.0d |
boolean | 表示一位信息,也就是是否的关系,false、true | false |
char | 一个16 位Unicode 字符,**\u0000 **到 \uffff | ‘’ |
拆箱与装箱
包装类位于java.lang包下。
-
特点
- 都带有自己的对应的类型的参数的构造方法。
- 除Character其他的还有构造方法重载带String。
- 有六个数字相关类都继承Number。
-
常用方法
- XXX value()://将一个包装类类型转化为对应的基本类型。
- int value = new Integer(10)://在堆内存重新创建一个变量。
- Integer类加载时有一个静态空间,空间立即加载Integer数组(-128~127)。
若使用的对象范围在此范围之内,则直接取静态区的对象
若超出则帮我们重新创建一个新的Integer对象
引用类型
数组内的元素是储存在堆内存中;一组数据类型相同的数据组合,且自身为一个引用数据类型,也可储存基本类型。初始化必须指定长度,数组长度一旦被创建就不可以更改,数组中储存的的类型可以是基本也可以是引用类型。
多维数组,也就是数组中嵌套数组。
- 静态数组 有长度有元素 初始化:int [] array = [ new int ]{value};
- 动态数组 有长度无元素 初始化:int [] array = new int {index};
- 通过index(元素在数组的位置来访问),获取数组的总长度,即所储存元素的个数——>”变量名.length“
- index的取值范围[0~lengh-1]
注:如果index超出范围,在运行时会出现 ArrayIndexOutOfBoundsException 数组索引越界
- 数组遍历-循环
遍历数组时,常用For循环或者ForEach循环
//数组声明
int [] array = new int [5]{1,2,3,4,5};
//for循环遍历
for(int i = 0 ; i < array.length ; i++){
//打印数组值
System.out.println(array[i]);
}
-
数组遍历-迭代器:
遍历集合,每个集合都有 iterator 方法,该方法返回一个迭代器对象
List<Integer> b = Arrays.asList (a) ;
Interor ib = b.iterator () ;
//常用方法:
E next ();//返回集合的下一个元素
boolean hasNext (); //判断下一个是否有元素
void remove(); //从集合删除当前元素
//实例:
Integer [] a = {10,20,30,40,50};
Object x;
List<Integer> b = Arrays.asList (a) ;
Interor ib = b.iterator () ;
while (ib.hasNext()){
x=ib.next;
System.out.println(x+" ");
}
基础语法
运算符
在程序中大致分为四种类型:算数运算符,赋值运算符,逻辑运算符,关系运算符。
-
进制转换
利用二进制判断奇偶数:判断该数的二进制数末尾为0还是1;如果为零,这表明该数为偶数,若为一,则表明该数为奇数。
进制单位 | 进制数值 |
---|---|
二进制 | 0 ,1 |
八进制 | 0,1,2,3,4,5,6,7 |
十进制 | 0,1,2,3,4,5,6,7,8,9 |
十六进制 | 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F |
- 赋值运算
加等于+= | 减等于-= | 成等于*= | 除等于 /= | 取余等于 += |
---|
- 关系运算符
大于> | 大于等于>= | 小于< | 小于等于<= | 非等于!= |
---|
-
” =“ 与“== ”的区别
= 赋值符号,将结果存入变量之中。
== 可以理解为判断两值或对象是否相等,返回的结果为Flase或true。 -
”==“和“equals()”的区别
对于基本类型,==和equals方法比较的都为两值是否相等;
对于字符串,==比较的为地址,equals方法比较的为值;
对于对象,==比较的为内存地址,equals方法比较的为值;
- 逻辑运算符
逻辑运算符 | 解释 |
---|---|
& 逻辑与 | 理解为“和,并且”的意思,用该符号连接是只有&前后的条件一致,才返回ture。 |
| 逻辑或 | 理解为“或者”的意思,只要两条件满足一个,就返回true。 |
^ 逻辑异或 | 前后结果不一致,返回true。 |
! 逻辑非 | 为否定的意思,即将原来的结果取反。 |
&& 短路与 | 即与&的用法相同。 |
|| 短路或 | 与|的用法理解相同。 |
- 位(bit)运算符
位运算符 | 说明 |
---|---|
& | 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0。 |
| | 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1。 |
^ | 按位异或 若参加运算的两个二进制位值相同则为0,否则为1。 |
<< | 左位移 用来将一个数的各二进制位全部左移N位,右补0。 |
>> | 右位移 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0。 |
标识符
- 项目名
- 全部小写。如:jobserver,studentmanagesystem;
- 包名
- 全部小写,名词。如:java.awt.event;
- 类名、接口名
- 首字母大写,多个单词组成时,每个单词的首字母大写。如MyClass,String;
- 方法名
- 首字母小写,多个单词组成时,从第二个单词开始,每个单词的首字母大写。第一个单词一般为动词,如:toString();
- 变量名
- 普通变量名:同方法的命名,一般为名词。如:int index=0;
- 常量名(静态变量名):全部大写。如: public static final String GAME_COLOR=”RED”;
- 属性文件properties
- 以下划线隔开。如:errors_zh_CN.properties,hibernate_test.properties;
7.数据库命名:
- 表、字段命名全部大写,多个单词以_隔开。
修饰符
权限修饰符
权限修饰符 | 说明 |
---|---|
public | |
(公共的) | public为最大的权限修饰符,可修饰类,成员变量,成员方法,构造方法,在一个类中可任意使用。 |
protected | |
(受保护的) | protected可以修饰成员变量,成员方法,构造方法,但不能修饰内部类,且被protected修饰后,只能被同包下的其他类访问。如果不同包下的类想访问被protected修饰的成员,这个类必须是个子类,即就是不同包的子类访问。 |
default | |
默认 | 默认不写的,它可以修饰类,成员变量,成员方法,构造方法。被默认权限修饰后其只能被本类同包下的类访问。 |
private | |
(私有的) | private修饰的成员变量,方法,构造方法 ,但不能外部类,除程序块。 |
private修饰的成员只能在其修饰的类中访问,在其他类中不能被调用,但能被private修饰的成员可以通过set和get方法向外界提供访问。 |
权限修饰符 | 同类 | 通包 | 不同包(子类) | 不同包(无关类) |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
注意:并不是每个权限修饰符都可以修饰类(外部类),只有public,默认的可以。所有权限修饰符都可以修饰数据成员,方法成员,构造方法。
特征修饰符
特征修饰符有:final(最终的)、static(静态的)、abstract(抽象的)、native(本地的)、transient(瞬时的)、synchronized(同步的)、volatile(不稳定的) 等。
- final 修饰符
final 不变的修饰变量的时候若无初始值,则给一次赋值机会,若一旦被final修饰,则不让其改变其值。
- 修饰变量的区别
若final修饰基本变量,则变量的值不能改变;若final修饰引用变量,则变量内的地址引用不变。
若属性使用final修饰,必须给其赋初始值,否则编译报错,属性如果没有赋值,但其是有默认值的,它储存在堆内存中的。
-
修饰方法
final修饰方法,不能被子类重写,即只能被子类继承。 -
修饰类
final修饰的类不能被继承,通常final修饰的是一些定义好的工具类。
- static 修饰符
static修饰符,也就是静态的,static修饰符可以修饰 属性、方法、块、内部类。
- 静态元素在类加载的时候,就已经初始化完毕,并在静态元素区储存,且每一个类都有自己的储存区域。
- 类加载一次,全部类及对象共享可以理解静态元素不属于任何的一个对象,属于类。
- 非静态元素成员(堆内存对象)中可以访问静态成员(静态元素区)且静态成员可以访问静态成员,但是静态成员不可访问非静态成员。
- 静态元素中不可出现this或super关键字。
注意:
如果类已经被加载,则静态代码块和静态变量,就不会重复执行,再创建类对象时只执行相关的变量的初始和构造方法。
可以理解:如果相同的静态成员已加载,则不再会再次加载,静态变量只加载一次。
- abstract 修饰符
abstract修饰符,抽象的,用于修饰类(接口和抽象类)、抽象方法。
- 同一个类不能被 abstract 与 **final **同时修饰。
- 同一个方法不能被 abstract 与 **static **同时修饰。
- synchronized 关键字声明的方法同一时间只能被一个线程访问
分支结构
单分支
if条件语句,也就是当条件符合时,执行语句里面的内容。
//当执行的条件符合,也就是条件为true时,执行if判断语句的内容。
if ( 条件 ) {
//执行内容
}
//双分支
if ( 条件 ) {
//执行体1
} else {
//执行体2
}
多分支
if条件语句多分支结构
if多分支结构就是在if单分支条件上增加多个条件判断。
if( 判断条件1 ){
//执行体1
}else if( 判断条件2 ){
//执行体2
}else if( 判断条件3 ){
//执行体3
}...
Switch多分支条件语句
switch分支中的语句中,所支持的数据类型有:byte、short、int、char、String、eaum等。
Switch( values ){
case 1:
//代码执行程序1;
beark;
case 2:
//代码执行程序2;
beark;
case 3:
//代码执行程序3;
beark;
//...
}
//switch语句具有穿透性,可以用break去除穿透效果;
循环结构
循环就是在不停的往复重复一件事情,可以理解不断执行的代码。循环的条件:初始值、终点判定条件 、变化量。在循环中,一直达不到终点判断条件,则该循环会陷入“死循环”,会抛出**OutOfMemoryError** 内存溢出的错误,也可以理解内存使用完。
for 循环结构
循环中断:使用break关键字,即可中断本次循环。
循环继续:使用continue关键字,就是中断本次循环继续下一次循环,且只能存在循环中。
循环嵌套:顾名思义就是在循环中嵌套循环,循环的次数就是两循环次数的乘积。
for (初始量;重点判定条件;变化量){
//执行代码;
}
//循环嵌套
for (初始量;重点判定条件;变化量){
for (初始量;重点判定条件;变化量){
//执行代码;
}
}
while循环结构
while循环是_先判断后执行,条件不符不执行_,并且变换量放在执行代码上下顺序可能对执行有影响;
//初始值
int i = 0;
//while循环:先判断后执行,,倘若条件为真true,则跳出循环
while( 终点判断条件 ){
//执行体;
//变化量;
}
//do...while循环:先执行后判断,倘若条件为真true,则跳出循环
do{
//执行体
}while( 终点判断条件 )
面向对象
类和对象
对象(Object):一个具体的事物具有一定属性和行为,含有“物体的概念”,一切物体皆对象。
类的定义
类 (Class):类是java程序中组成的基本单位,是一组具有相同属性和行为的对象;类是由属性和行为组成,属性主要通过成员变量来实现,行为主要通过定义成员方法来实现。
- 属性结构:权限修饰符 特诊修饰符 数据类型 属性名称 [=所赋予的值];
- 方法结构:权限修饰符 特征修饰符 返回值类型 方法名称 (参数列表) [抛出异常] {方法体;}
public class Person{
//属性
public String name;
//方法
public String getName (){
return this.name;
}
}
方法相关
- 方法重载(overload)
对于一个同一个类的同一组方法,具有相同的方法名,不同的参数列表(个数、顺序、类型),这样一组方法构成方法重载。
方法重载的原则:
- 方法名称必须相同。
- 参数列表必须不同。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以称为方法的重载。
注意:final修饰的方法不能被重载。static修饰的方法不能被重载,但是能够被在此声明。
- **实参与形参 **
形参:方法执行时的临时变量空间。
实参:方法调用时传递的出去的参数。
方法调用时会将实参的内容传递给形参
如果内容是基本数据类型,则传递的为值,形参改变,实参不改变。
如果内容是引用数据类型,则传递的为地址,形参改变,实参改变。
- 构造方法
构造方法,就是构造当前类的对象,对一个类进行实例初始化,其没有返回类型,甚至连void关键字修饰都无。
构造方法可以进行方法重载。
构造方法分为有参构造和无参构造,其类默认为无参构造方法,当类无构造方法,其系统会默认添加无参构造方法,若想让类不可被实例化,可以用private修饰构造方法。
语法结构:权限修饰符 方法名(与类名一致)(参数列表) [抛出异常] {方法体; }
可以通过this(),调用当前类的构造方法。
- this和super关键字
this代表当前对象,用于代替某个对象或当前属性和方法。并且this关键字可以调用属性和方 法,也调用构造方法,但必须通过**this()。**在另一个构造方法内调用构造方法,必须放在程序的第一行。
super来实现对父类成员引用,用来引用当前对象的父类。
- 代码块
代码块就是一个只有方法的方法体,没有方法名、参数列表、返回值并且不能通过对象调用,只能在类加载时自己执行。
- 创建形式 “ {} ” 就是没有参数,没有修饰符(static除外),没有返回值的特殊方法。
- 没有重载,但可以定义多个程序块。
- 在创建对象之前进行,每一次在调用构造方法之前都会默认执行。
- 非静态代码块
非静态代码块也就是,不使用static修饰的代码块,其包括:局部代码块、构造代码块。
//构造代码块:也就是处于方法外
{
System.out.println("这是构造代码块");
}
//局部代码块
public void test () {
System.out.prinln("这是局部代码块");
}
- 静态代码块
也就是通过static修饰的代码块,一般用于数据初始化,并且静态块伴随类的初始化而加载,且只执行一次。
static{
//执行体
}
注意:如果方法来回的调用,但执行时可能会产生 StackOverFlowError 栈溢出错误,也就是内存使用完。
public class block {
{
System.out.println("构造代码块");
}
static{
System.out.println("静态代码块");
}
public static void main(String[] args) {
{
//对类进行实例化
new block();
System.out.println("局部代码块");
}
}
}
内存划分
- 声明的对象是先在栈内存中为其分配地址空间,在对其进行实例化后则在堆内存中为其分配地址。
- 堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。
- 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,
- 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。
面向对象
面向对象的编程的主要思想是把构成问题的各个事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为。对象作为程序的基本单位,将程序和数据封装其中,以提高程序的重用性,灵活性和可扩展性。类是创建对象的模板,一个类可以创建多个对象。对象是类的实例化。类是 抽象的,不占用存储空间;而对象具体的,占用存储空间。
- 面向对象的最重要的特性是支持继承、封装和多态。
- 系统设计应该遵循开闭原则,系统应该稳定不不可修改,但应支持通过继承、组合等方式进行扩展
- 面向对象设计时,每个类的职责应该单一,不要再一个类中引入过多的接口
- 过程式语言和面向对象的语言各有其优势,过程式语言更加灵活,面向对象语言更加强调抽象和封装
- Java和C++都是静态类型的面向对象编程语言
继承
继承实现提高程序的复用,提高耦合性,但响应的降低了独立性,子类可以直接获得父类提供的属性和方法,子类还可通过拓展,定义自己的属性和行为。
继承通过 extends关键字实现,实现的类为子类,被继承的为父类,一个父类有多个实现类,但一个子类只有一个父类,子类可以获取父类中的所有非私有的属性和行为。
继承类型包含单继承和多继承,但Java中只支持单继承,但可以通过传递的方式实现多继承效果,一个类可以被多个子类直接或间接继承。
public class animal {
public Eat eat;
//......
}
public calss person extends animal {
//继承父类的属性或行为
public Eat eat;
//......
}
子类可以调用父类的属性和方法且可以添加自己独有的属性和方法,也就是方法重载(override),重写父类的方法体,也就是覆盖父类的方法内容,保持子类的**方法名、参数列表、返回值相同**,后修改方法体。
ouverride 与ouverlode 的区别
-
类的区别
方法重写是产生两个继承的类,子类重写父类的方法。方法重载是同一类的同一组方法。 -
权限权限符
方法重写子类的权限修饰符可以大于父类(public>protected>默认的>private)。方法重载无要求。 -
特征修饰符
若父类的方法被final修饰,则子类不能重写父类的方法;若父类被static修饰,则子类不存在重写;若父类被abstract修饰,则子类必须重写(或者该子类为抽象类)。
-
返回值
重写中子类的返回值必须小于父类,重载没有要求。 -
参数列表
方法重写,子类必须与父类一致;方法重载,每个方法的参数必须不一致。 -
异常区别
如果父类方法执行时异常,子类不予理会。如果父类抛出编译时异常,子类抛出的异常个数少于等于父类。
子类抛出的异常类型小于等于父类。
三目运算符
三目运算符就类似于if-else语句,但是语法更加简洁,语法结构:判断条件 ? 执行体1 : 执行体2
类关系
类之间的关系包括:继承(泛化 Generalization)、 实现(Realization)、依赖(Dependenc)、关联(Correlation)、聚合(Paradigmatic)、组合(Syntagmatic)。
- 继承关系
继承关系也叫泛化关系是,通过关键字extends实现,继承的类为子类,被继承的类为父类,但在java中只支持单继承,若想要实现多继承可以通过叠加的方式实现,通过一个空心箭头的实线表示。
- 实现关系
实现关系,其实现形式为 **interface **接口 ,并通过关键字 Implements 实现,其通过空心箭头的虚线表示。在java中单继承多实现。
- **依赖关系 use-a **
是一种使用关系,临时性关系。在代码中由局部变量,函数参数,返回值建立对于其他对象调用。类比“屠夫杀猪、人使用手机”之间的关系。通过虚线箭头表示。
- **关联关系 **
体现的是两个类之间的语义级别具有强烈依赖关系,且关系一般为平等、双向的关系,如:人有车、学生与学校的关系等。通过实线箭头表示。
** 一个类成员变量是另个一个类的实例对象。**
- 组合关系(Compostion)
表示 contains-a 的关系,是一种强烈的包含关系,含有相同的生命周期。整体和部分的关系不可分割,,同时出现同时消失,类比“人与大脑”的关系,也就是整体和部分不可分离,通过实心菱形箭头实线表示。
- 聚合关系
它体现整体与内部的关系也就是 has - a 关系,此时整体和部分可以分割,他们具有各自的生命周期,部分可以属于多个整体对象,也可为多个整体对象所共享,类比“汽车和轮胎,电脑和主板”的关系,通过空心菱形箭头实线表示-。
- 各种关系强弱:泛化=实现>组合>聚合>关联>依赖
Object方法
- hashCOde();将对象在内存中的地址经过计算得到一个 int 整数。
- **equlas();**用于比较对象的内容,且Object的默认效果为“==”
- toString(); 打印输出的对象为String字符串类型。
- **getClass();**获取对象的映反射。
- **wait();**让线程进入挂起状态,且存在方法的重载。
- **notity();**线程唤醒。
- **notifyAll();**唤醒所有线程。
- tinalize();
- **clone();**为克隆对象。
封装
对象的全部属性和行为都在类的内部实现,不可分割,“信息隐蔽”,就是将类内部的数据进行私有化或者对执行过程进行封装,使类的内部实现细节不可见的,从而保护数据和执行过程的安全,使用者只能通过类提供的公共方法,来获取类的内部数据。
- 类加载顺序
** 先父类后子类,先静态后动态,先变量后块,最后构造器。**
-
类加载做了哪些事?
- 类加载时,同时初始化类中静态的属性(赋默认值)。
- 执行静态代码块。
- 分配内存空间,同时初始化非静态的属性(赋默认值)。
- 如果声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖。
- 执行匿名代码块。
- 执行构造器。
- 返回内存地址。
抽象
- 抽象类
在普通类的基础上扩充了抽象方法的类,使用 **abstract **关键字定义。
使用原则
- 所有抽象类都必须还有子类,且abstract 和 final不能同时修饰,否这个会编译报错。
- 若子类不是抽象类,子类必须重写父类(抽象类)的所有抽象方法,若子类为抽象类则可以不要求重写,即以abstract修饰子类。
- 抽象类可以修饰子类向上转型,对其实现实例化,抽象类不一定能直接实例化对象。
- 由于抽象类强制要求子类复写父类的方法,故 private 和 abstract 不能同时使用,(private私有的,被private修饰的属性方法不能被外部使用)。
- 抽象类可以没有抽象方法,但抽象类也可全为抽象方法,若全为抽象方法可以转化为另一种使用形式-接口。
- 抽象类的属性和普通方法(非抽象方法)都能用final和static修饰也可含有块和构造方法(static修饰块),构造方法也可重载。
- 子类重写父类的方法,在子类内部在子类方法外进行重写。后在子类主方法内那个调用重写之后的方法。
//抽象类
public abstract class Animal {
//抽象方法-抽象方法无方法体
public abstract void eat();
//普通方法
public void sleep (){
//方法体
}
}
- 抽象方法
使用 abstract 关键字修饰没有方法体的方法,且子类继承后必须重写父类的抽象方法。
使用原则
- 先调用抽象类构造方法,再调用子类构造方法。
- 抽象方法必须放在抽象类中或接口中。
- 抽象方法一定不能用final和static修声明,因为final不允许有子类,而抽象类必须含有子类,即子类通过重写抽象类的方法。
- 直接继承外部的抽象类,则只需重写外部类的抽象类的所有抽象方法。
接口
接口是一种特殊的类,简单理解就是抽象类的极致化,使用interface关键字声明,大多用于定义规则。通过关键字 **implements **表示被子类实现。
public interface Student{
//静态熟悉/常量
public static final Integer ID = 1;
//抽象方法
public abstract void study();
}
-
使用原则
- 在接口中所有的属性值都是通过 public static final 修饰,即静态常量。
- 在接口中公有的抽象方法都是通过 public abstract 修饰。
- 不允许使用 **private **和 **protected **声明接口成员和结构,包括在任何地方使用。
- 通过“implement”(实现)表示被子类继承,同时需要在子类中实现抽象方法。如:
public class Person implement Animal{}
。 - 使用接口常量,直接通过"对象名."的形式进行调用。
- 使用接口的方法,通过创建对象,后进行调用子类实现的方法。
-
类与接口的关系
- 接口不能继承其他类,包括抽象类和具体方法,但可以直接继承其他接口。
- 抽象类可以直接实现接口,但具体类不可以直接实现接口,必须将接口的方法具体化或将该子类变为抽象类。
- 在java中是单继承多实现机制,但是可以通过阶梯式实现多继承。
多态
多态也就是类的多种形态,不同对象使用同一接口,所产生的不同状态。
类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。
实现条件
- 继承关系:必须含有继承关系的子类和父类。
- 重写方法:子类对父类的某些方法进行重新定义,在调用这些方法就会调用子类的方法。
- 向上转型:在多态中需要将子类的引用赋给父类的对象( 父类的引用指向子类的对象),才可调用子类和父类的方法。
- 如果父类和子类有同名的属性,则执行的为父类的属性。
- 如果子类将父类的方法重写,则调用子类重写的结果。
**上下转型 **
- 向上转型
当父类引用指向一个子类对象时,便是向上转型。当使用多态方式调用方法时,首先检查父类中是否有该方法,若有,则执行的是子类重写后的方法,若没有,则编译出错。
Person person = new Teacher;
//该引用只方能调用父类的属性和方法(Person为父类,Teacher为子类)
- 向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
Teacher teacher = new Person();
//向下转型
注意:在造型时(强制向上或向下转型)可能会出现一个运行时异常 ClassCastException。
枚举
对于对象固定的类,一组常量,使用枚举类。
-
自定义枚举
- 构造方法有
**private**
,防止构造新的对象,枚举值要调用对应的构造器来创建。 - 枚举成员声明
**private final**
。 - 枚举类对象声明
**private static final**
。
- 构造方法有
public class FruitEnum {
//枚举成员
private final String name;
//构造方法私有化
private FruitEnum(String name) {
this.name = name;
}
//枚举成员
public static final FruitEnum APPLE = new FruitEnum("苹果");
public static final FruitEnum BANANA = new FruitEnum("香蕉");
public static final FruitEnum MELON = new FruitEnum("西瓜");
//获取方法
public String getName() {
return name;
}
public static void main(String[] args) {
System.out.println(FruitEnum.APPLE);
String fuirtName = FruitEnum.MELON.getName();
System.out.println(fuirtName);
}
}
-
Enum枚举类
枚举类通过,关键字 **enum **定义的类,即继承于class java.lang.Enum类。主要定义成员和成员包含的属性值。
常用方法- **value():**返回枚举类型的对象数组。
- **valueOf(String str)**通过name获取对应的枚举。
- **toString():**返回当前枚举对象的名称。
- **compareTo():**可以比较两个枚举对象。
/**枚举类通过enum修饰*/
public enum AnimalEnum {
/**声明成员,多个成员变量通过“ ,”分隔 */
PIG("小猪"),QUAIL("鹌鹑"),PANDA("熊猫");
/**声明属性,通过 private final修饰*/
private final String name;
/**在enum枚举类中对构造方法默认是private修饰*/
AnimalEnum(String name) {
this.name = name;
}
/**获取属性值-通过get方法*/
public String getName() {
return name;
}
}
注意:switch的控制表达式可以是任何枚举类型,case可以用枚举对象的对象名。
内部类
定义:将类的定义放置在另一个类的内部。
- 成员内部类
将一个类直接定义在类里面,作为成员,与属于的方法一致,正常类一致,可以用不同修饰符
成员内部类可以访问外部类的所有的成员(含私有)。
- 内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。
- 内部类是另外一种封装,对外部的其他类隐藏,也就是其他类访问不了。
- 内部类可以实现java的单继承局限。
public class MemberInnerClass {
private String outsideText = "private修饰的外部类私有属性";
/**
* 成员内部类的定义
* -当前类可以访问成员内部类的所有私有域,成员内部类也可访问外部类的私有域
* -以外的类不可访问成员内部类,只能通过外部类访问到内部类
* */
class InnerClass{
private String innerText = "private修饰的内部类私有属性";
public void getInnerText () {
System.out.println("这是成员内部类,调用外部类的私有属性: "+outsideText);
System.out.println("这是成员内部类,调用内部类的私有属性: "+innerText);
}
}
public void test (){
new InnerClass().getInnerText();
}
public static void main(String[] args) {
MemberInnerClass memberInnerClass = new MemberInnerClass();
//只能通过外部类调用内部类的相关属性和行为
memberInnerClass.test();
}
}
- 局部内部类
方法内部类,不允许使用访问权限服public、private、protected
修饰。将一个类定义在方法或块中,作为成员的内部类结构,与临时的局部变量一个局次。局部内部类类像局部变量,只能用abstract 或 final 修饰局部内部的变量使用,只用final修饰。
- 局部内部类只能在,其所在的方法中使用。
public class LocalInnerClass {
private String outsideText = "private修饰的外部类私有属性";
public void method () {
/**
* 定义局部内部类-方法内部类
* -只能在方法中调用该内部类的内容
*/
class LocalInner {
String localInnerText = "局部内部类的属性";
public void localTest(){
System.out.println(outsideText);
System.out.println(localInnerText);
}
}
LocalInner localInner = new LocalInner();
localInner.localTest();
}
public static void main(String[] args) {
LocalInnerClass localInnerClass = new LocalInnerClass();
localInnerClass.method();
}
}
- 匿名内部类
名内部类就是一个没有名字的方法内部类,通常实现父类和接口的匿名子类。匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或者实现一个接口。
- 通常为接口或抽象类,可省略一个源文件
- 匿名类内部类,只有类体的所有结构(修饰,名字,继承,实现)
- 匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以初始化块,可以通过初始化块来完成构造器需要完成的工作。
public interface InnerClass {
void getName();
}
public interface InnerInterfaceClass {
void getName();
}
public class AnonymousInnerClass {
public void getName (){
//对接口或者抽象类,进行重写实现方法体。
InnerInterfaceClass innerInterfaceClass = new InnerInterfaceClass() {
@Override
public void getName() {
System.out.println("该为匿名内部类-实现接口");
}
};
InnerAbstractClass innerAbstractClass = new InnerAbstractClass() {
@Override
void getName() {
System.out.println("该为匿名内部类-实现抽象类");
}
};
//调用方法
innerInterfaceClass.getName();
innerAbstractClass.getName();
}
public static void main(String[] args) {
AnonymousInnerClass anonymousInnerClass = new AnonymousInnerClass();
anonymousInnerClass.getName();
}
}
- 静态内部类
成员静态内部类,使用 **static **修饰,不需要外部类对象,通过正常的方式直接创建内部类,静态元素不能访问非静态成员(自己类和外部类)。
可以通过 类名.静态成员 的形式调用。
public class StaticInnerClass {
private static String outsideText = "外部类私有属性";
/**
* 静态内部类-staticInnerClass
* -不用依赖外部类,可以直接创建
* */
static class innerClass{
public innerClass() {
System.out.println("静态内部类的构造方法");
System.out.println(innerClass.class.getName());
//静态内部类调用不了,其外部类的属性,但若其外部类的属性使用static修饰,则可调用。
System.out.println("调用外部类static修饰的属性:"+outsideText);
}
}
public static void main(String[] args) {
//不需要外部类,直接访问内部类
new StaticInnerClass.innerClass();
}
}
-
内部类与外部类的关系
- 内部类可以直接访问外部类的元素(包括私有域),外部类在内部类之前创建,创建内部类时会将外部类的对象传入。
- 外部类可以通过内部类的引用间接访问内部类元素,要想访问内部类属性,必须先创建内部类对象。
常用工具类
数学相关
Math类
Math类所属的包Java.lang,封装了常用的数学运算相关,Math的构造方法是私有的,不能直接创建对象,且Math中提供的方法及属性,用static修饰。
常用方法
方法名 | 说明 |
---|---|
abs() | 返回给定数字的绝对值。 |
ceil() | 向上取整。 |
flour() | 向下取整。 |
rint() | 临近整数,如果两边都一样,则返回偶数。 |
round() | 四舍五入取整数。 |
max(a,b)/min(a,b) | 去两数最大值与最小值。 |
pow(a,b) | a的b次方,且参数和返回值全为double。 |
sqrt(double a) | 获取给定数的平方根。 |
random() | 随机产生一个[0.0~1.0)的数 |
Random类
Random类所属Java.util包类,其没有任何继承关系,默认继承Object类。
方法名 | 说明 |
---|---|
nextInt() | 随机产生一个范围为 int 的随机数,有正有负。 |
nextInt(int bound) | 随机产生 [ 0 , boundon )的整数。 |
nextFloat(); | 随机产生一个[ 0.0~1.0 )数。 |
Boolean( ); | 随机产生一个Boolean类型的类 |
四则运算
添加——> add(); | 减——> substract(); |
---|---|
乘 ——>multily(); | 除——> divide(); |
Decimal对象,setScale(位数,设计模式);//设置小数点之后的位数
DecimalFormat类
Date类
Date类所属java.util包,其作用用于创建表示日期时间的对象,并可以比较日期的大小,Date类创建本地计算机当前日期的对象用毫秒值表示。
Date date = new Date();
System.out.println(date); //Tue May 17 16:35:41 CST 2022
System.out.println(date.getTime()); //1652776541865
DateFormat类
DateFromat类所属java.text包,且为抽象类其SimpDateFormat为其子类,主要作用就是对于日期的解析和格式化。
yyyy-年 | MM-月 |
---|---|
dd-日 | HH-时(24制) |
hh-时(12制) | ss-秒 |
mm-分 | E-星期 |
a-上午 | |
//创建Date对象
Date date = new Date();
//设置日期格式
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//解析Date对象
System.out.println(format.format(date));
Calendar类
Calendar是一个抽象类,被abstract修饰,即不能通过new来获取实例对象。
//Calender类-通过子类对象实现
Calendar calendar = Calendar.getInstance();
//将日历转化为Date对象
System.out.println(calendar.getTime());
//返回当前系统的毫秒值
System.out.println(calendar.getTimeInMillis());
System.out.print(calendar.get(Calendar.YEAR)+"年");
System.out.print(calendar.get(Calendar.MONTH+1)+"月");
System.out.print(calendar.get(Calendar.DATE)+"日");
注意:在calendar类中的月份是从0开始,故在得在其基础上加一。
TimeZone类
TimeZone类表示时区偏移量,也可以显示夏令时,所属java.util包下。
//Timezoo
TimeZone timeZone = TimeZone.getDefault();
//获取时区
System.out.println(timeZone.getID()); //Asia/Shanghai
//获取时间名称
System.out.println(timeZone.getDisplayName()); //中国标准时间
字符串相关
String类
所属java.lang包下,为引用类型所属,实现 Serializable、CharSequence、Comparable接口。String具有不可变特性,具体表现在**长度及内容不可变,即String是以final修饰char数组,数组的长度本身不改变,且所在的地址不改变和以private 修饰的属性**所以不能在类的外部访问。
String str = "";//str(字符串常量池)
String str = new String ("");//带String参数的构造方法转化为字符串对象,分配在堆内存的中。
String str = new String (char [] value);//将字符串数组转化为字符串对象
String (char [] value,int index,int count);//把数组中的一部分封装成字符串对象
String.valueOf(char [] );//将char[]转化为String类型
但可以以当前的变量名指向String的引用地址,用另一个相同的变量名去接受其引用地址。
常用方法
方法名 | 作用 |
---|---|
equals(); | 继承Object重写,比较两个字符串的字面是否相同。 |
hashCode(); | 继承Object类且重写,将字符串的每一个char类型的元素拆开乘以31并求和。 |
compareTo(str); | 实现自compareTo接口,且以Unicode编码顺序进行比较。 |
charAt(int index); | 返回index索引的位置的char值。 |
codePointAt(int index); | 返回给定index索引位置对应的Unicode编码。 |
lengh(); | 返回字符串的长度。 |
concat(String); | 将给定的字符串拼接在当前字符串之后。 |
contains(charSequence s); | 判断给定的s是否正在字符串中。 |
startsWith(String prefix); | |
endsWith(String suffix); | 用于检测字符串是否指定的前/后缀开始。 |
toCharArray(); | 将当前的字符串转化为数组。 |
indexOf(); | |
lastIndexOf(); | 找寻给定元素在字符串中第一次或最后一次出现的索引位置,若不存在则返回-1。 |
isEmpty(); | 判断当前字符串是否为空字符串。 |
replace(char oldChar,char newhar); | 将oldchar替换为newchar,返回一个新的字符串。 |
replaceFist(); | 替换第一次的字符串为新的字符串。 |
split(String regex[,int limit限度界限]); | 按照表达方式将原来的字符串拆分,String的regex为正则表达式。 |
substring(int beginIdex[,int endIndex]); | 将当前字符串截取一部份[beginIndex,endIndex)。 |
toUpperCase(); | |
toLowerCase(); | 将全部字符串转化为大小写。 |
trim(); | 去掉字符串前后的空格。 |
mathches(String regex); | 返回是否匹配字符串。 |
StringBuffer类和StringBuilder类
对于字符串操作较多,时使用Stringbuffer或者StringBuilder,其中StringBuffer线程安全,StringBuilder非线程安全。
StringBuffer类
Stringbuffer所属java.util包,考虑线程安全,默认继承Object类,实现Serializable,CharSequence,Appendble且两类不含compareTo()方法,含有独特的方法**append();**字符拼接,含有多个重载。
StringBuilder() | 构造一个没有字符的字符串构建器,初始容量为16个字符。 |
---|---|
StringBuilder(CharSequence seq) | 构造一个包含与指定的相同字符的字符串构建器 CharSequence 。 |
StringBuilder(int capacity) | 构造一个没有字符的字符串构建器,由 capacity参数指定的初始容量。 |
new StringBuilder(); | 自定义长度空间的对象,char[]. | |
StringBuilder(String str) | 构造一个初始化为指定字符串内容的字符串构建器。 |
StringBuilder类
StringBuilder类与StringBuffer基本类似,其构造方法与方法类似,StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。
方法名 | 作用 |
---|---|
append(); | 频繁的拼接字符串的时候,使用此方法,提高性能。 |
insert( int index,value ); | 可以控制插入起始的地方。 |
ensureCapacity(int minimumCapacity); | 确保容量的足够,两倍的旧容量,加上2。 |
capacity(); | 取字符量底层char类型的容量。 |
lengh(); | 返回字符串的有效长度。 |
setLengh(); | 设置字符串的有效个数。 |
delete(int start [,int end]); | 独有方法,将start的end之间的字符串删除。 |
deleteCharAt(int index); | 将给定index位置的字符删去。 |
replace(int start,int end,String str); | 将start到end之间的字符串替换为str。 |
setCharAt(int index , char value); | 将index的字符改成value |
UUID类
UUID类所属Java.util包,其默认继承Object类,是为一种无重复字符串的一种程序类。获取一个十六进制的随机数的对象.
//创建实例化
UUID uuid = UUID.randomUUID();
//fcec530e-fabf-4b1b-89fe-f14def4b5cc9
System.out.println(uuid.toString());
Scanner类
Sacnner类所属Java.util包,常用于用户的输入。
常用方法
方法名 | 作用 |
---|---|
nextInt(): | 限制输入的类型为int。 |
nextFloat(); | 限制输入的类型为float。 |
next(); | 只读取输入直到空格。它不能读两个由空格或符号隔开的单词。且next()在读取输入后将光标放在同一行中。 |
nextLine(); | 读取所有输入,直接读到行尾,且光标移动到下一行。 |
//Scanner类
Scanner input = new Scanner(System.in);
System.out.print("请进行文本输入:");
System.out.println(input.next());
集合相关
常用集合分为三大类:List、Set、Map。
(https://cdn.jsdelivr.net/gh/SILENTI/Imgs/1609135000527-631d7e54-c08a-4bcf-b845-50a0e67f917a.png#crop=0&crop=0&crop=1&crop=1&id=rQC9D&originHeight=450&originWidth=1166&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
List
List集合继承自Collection接口,List集合元素有序且可重复。
特有方法 | 作用 |
---|---|
add(int index,E element); | 添加元素element到指定的索引位置。 |
remove(int index,); | 移除索引index处的元素。 |
get(int index); | 获取索引index处的元素。 |
set(int index,E element); | 修改索引index处的的元素。 |
ArrayList
ArrayList集合是有序可重复的,其底层是基于数组结构数组实现动态扩容,允许null存在,其特点查询快、增删慢,线程不安但效率高。
Vector
Vector集合是有序可重复的,其底层是基于数组实现的,其特点就是查询快、增删慢,线程安全,效率低。
LinkedList
LinkedList集合是有序可重复的,其底层是基于链表链形式实现的,其特点是查询慢、增删快,线程不安全,效率低。
Set
Set集合其特点是无需不重复的,不允许含有相同的元素。
HashSet
无序 唯一 底层数据结构是链表和哈希表,通过hashcode()和equals()来确定唯一性,当hashcode()的值相同时,才会去调用equals()方法来确定唯一性性,当想自己定义唯一性时可以在对象类中重写hashcode()和equals()方法。
TreeSet
底层数据结构是红叉树(红黑树是一种自平衡的二叉树),存储容器是TreeMap,我们可以从源码上看出来。
LinkedHashSet
底层数据结构是链表和哈希表,链表保证元素有序,哈希表保证元素唯一
Map
TreeMap
是排序有序的,HashMap和HashTable是无序的,HashTable是线程安全的,效率低,不允许null值,HashMap是线程不安全的,效率高,允许null值(key和value都允许)
HashMap
以数组加链表的xing
异常与线程
异常
异常指的是程序流程不能处理的或者没有处理的异常事件或结果,这是由人规定的结果。由异常类型、提示信息、报错行号组成。
- Error:通常为一些物理量,Java虚拟机自身出现错误,程序指令处理不了。
- Exception:通常表现为一种人为规定的不正常的一种现象。
运行时异常与编译时异常
- 运行时异常:在java编译时不会报错。
- 编译时异常:在编译时会出现报错,必须强制对异常进行处理。
异常处理
异常处理,是通过 try/catch 关键字对异常的处理,让其后续代码不会因为异常的抛出而中断程序运行。
两种方式(catch throw)
- try/catch 异常捕获
**try/catch处理异常,**try关键字不能单独处理,且后面必须添加catch,finally不是必须存在的,若存在该结构,则必须执行。
try{
//捕获的异常
}catch(异常类型 异常名){
//处理方案
}catch(异常类型 异常名)...
//catch可以存在多个,捕获多个异常
finally{
//最后必须执行
}
注意:**final finally finalize区别 **
- final特征修饰符,用于修饰变量、属性、方法、类等。
- finally处理异常手段的一部分,若含该关键字,不管是否出现异常,都会执行。
- finalize是Object类中的一个protected方法,对象没有任何引用指向的时候,会被GC回收,当对象回收的时候 默认调用finalize方法。
-
throws 异常抛出
通过关键字throws将异常抛出,对于那种强制处理的可以抛出活用try/catch处理。使用throws抛出的异常最终还是要处理的。
若方法抛出的异常,由调用异常的类最终处理。
自定义异常
根据项目,可定义自定义异常,方便后端人员快速排出BUG。
- 创建一个异常类并实现 **Exception **或者 **RunTimeException **类。
- 继承Exception,编译时时异常,必须进行处理。
- 继承RunTimeException,运行时异常,可不处理。
/**
* 1.创建自定义异常类,并继承RunTimeException
* 2.重写RunTimeException中的方法
* */
public class TestException extends RuntimeException{
public TestException() {
super();
}
public TestException(String message) {
super(message);
}
public TestException(String message, Throwable cause) {
super(message, cause);
}
public TestException(Throwable cause) {
super(cause);
}
protected TestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- 使用异常,也就是手动抛出异常。
public class Test {
public static void main(String[] args) {
//在实际项目,根据实际情况,抛出异常。
throw new TestException("手动抛出异常!!!");
}
}
常见异常
- InputMisMatchException——输入类型不匹配,如在使用Scanner类时可能会抛出该异常。
- NumberFormatException——数字格式化异常,如在对于基本类型的拆装箱时可能会抛出该异常。
- NegativeArraySizeException——数组长度负数,如在数组中使用负值的索引值。
- ArrayIndexOutOfBoundsException——数组索引越界。
- NullPointerException——空指针异常。
- ArithmeticException——数字异常。
- ClassCastException——造型异常,在强转时可能出现该异常。
- StringIndexOutOfBoundsException——字符串索引越界。
- IndexOutOfBoundsException——集合越界,若在集合操作是,索引长度越界。
- IllegalArgumentException——非法参数异常。
- ArithmeticException——算数异常。
- StackOverFlowError——栈内存溢出。
- OutOfMemoryError——内存溢出。
线程
线程是独立的线程,其代表是独立的执行空间。 Thread是用来表示线程的类,其所属java.lang包,其主要方法为启动线程start()、连接线程join()、线程睡眠sleep()等。当朝超过一个执行空间,代表着有着多线程执行,实际上是多个处理器处理多个事情。
进程是执行的程序,一个进程至少含有一个或者多个线程。
线程常用方法
- sleep(long millis)
属于静态方法,sleep方法会使线程休眠 millis (毫秒值),当过后线程会自动复活,但是不会释放锁资源,使用sleep可能存在 **InterruptedException **中断异常。
- join()
连接线程,当将当前线程中连接到另一线程,则该线程会进入等待状态,直到另一线程执行完毕后,才会继续执行当前线程。
- wait(long timeout)
让线程进入等待状态,在使用该方法需要加锁synchronized,否则可能出现 IllegalMonitorStateException 异常。
在多线程共享的对象使用wait方法,故需要进行加锁处理保证线程同步。
- notify和notifyAll
唤醒线程,notify与notifyAll的区别就是notify是唤醒一个线程而且还是随机的,而notifyAll是唤醒所有的线程,在实际使用中,更加倾向notifyAll的使用。
- yield()
暂停当前执行的线程,并让其他线程执行。
- getPriority()
设置线程的优先级,也就是线程的执行调用程度。其含有1~10个优先级,最小优先级为1,最大优先级为10,默认优先级为5。
创建线程
- 创建线程类,并继承Thread类或者实现Runnable接口。对于Thread和Runnable的区别,一个是类一个是接口,其实Thread是在Runnable的基础上进行拓展,在实际使用中用Thread类。
- 重写run方法。
- 启动线程。
public class Thread extends java.lang.Thread {
//重写Run方法,添加自定义方法体。
@Override
public void run() {
System.out.println("run方法中线程的执行体");
}
public static void main(String[] args) {
Thread thread = new Thread();
//执行线程。
thread.start();
}
}
线程状态
线程的状态:创建状态、就绪状态、执行状态、阻塞状态、死亡状态。
- 创建状态,也称新建状态,也就是Tread的实例已被创建但还未启动,即有Tread对象但未执行。
- 就绪状态,也称可执行状态,也就是已经启动线程,但还未轮到该线程执行,此时该线程已经布置好执行的空间。
- 执行状态,也就是当前线程已经开始执行。
- 阻塞状态,也就是处于一种等待的状态暂时不执行,该种状态有线程调度器来决定,但若脱离该种等待状态,线程又会重新进入就绪状态。阻塞线程,通常是调用sleep、locked或者wait后。在线程的实际执行中,同常是在可执行状态和执行状态来回转换。
- 死亡状态,也就是线程中断或者已执行完毕,进入该种状态。
线程同步
线程的三大特性:可见性、原子性、有序性。
- 可见性(Visibility)
可见性,简单理解就是当线程A修改共享变量value后,另一线程B可以得知修改后的共享变量value,即将共享变量重新刷新到共享内存,且synchronized具有可见性。
- 原子性(Atomicity)
原子性,就是一个事务或者多个事务,要么全部执行并且不能被中断,要么就全部都不执行。synchronized是具有原子性的,故获取synchronized的线程,在执行的过程中会阻塞其他线程,直到当前线程执行完毕。
- 有序性(Ordering)
有序性,就是在线程中代码的执行顺序是有序的,由上至下,在一个线程中观察另一个线程,其中的所有操作是无序的,也就是指令重排和内存延迟造成的。
锁的类型
通常锁的功能越强大,其性能会越低。
- 可重入锁(递归锁)
线程获取该锁之后,再次获取该锁之后,能成功获取,不会变成阻塞状态,也就是线程已经获取过的锁重新获取锁。
- 可/不中断锁
中断锁顾名思义就是,锁在执行的过程中可以被中断,Lock锁可被中断。
而不可中断锁,也就是以synchronized为代表,当该锁执行时,不可被中断,会让其它的线程一直处于阻塞状态,但是其最大的问题就是当出现异常时,也一直会让其它线程处于阻塞状态。
- 公平/非公平锁
公平锁,也就是公平的,根据线程发送请求的执行顺序,进行加锁,该种类似于顺序队列中的形式,就是先进先出原则,该种类型不会产生线程饥饿。
线程饥饿,也就是线程长期未得到执行,形象理解,就是穷人和富人两级分化严重,穷人越穷富人越富的局面。
非公平锁,线程执行不是公平的,不是按线程请求的执行顺序,但这样做的好处就是其执行效率高于公平锁的执行效率,但是其可能产生线程饥饿。
- 读写锁
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。
也就是讲线程化为读线程与写线程,在读写锁中,可以同时读取,但不能同时写入和同时读写。
常用线程锁
线程锁含有synchronized锁和lock锁,synchronized是java内置的关键字,得到锁的线程执行完释放锁,若线程被中断也会释放锁,适合少量的线程同步;Lock是所属java.util包下的ReentrantLock类中的一个方法,其必须通过finall关键字释放锁,否则会造成死锁,在大量线程同步,还是Lock类实现。
synchronized 锁
synchronized关键字,即“同步锁” ,synchronized关键字的作用就是保证通过synchronized修饰的方法或者块只有一个线程执行,从而达到同步的效果。
synchronized的三种使用场景:修饰方法、修饰静态方法、修饰代码块。
synchronized对静态方法或者类块加锁时,实际上是对类进行加锁,而将synchronized对普通方法加锁,也就是对普通方法加锁/实例对象。
对象锁,只锁住当前对象,不会对产生其它影响,不同对象访问对象锁时不会产生阻塞或者等待;类锁,类只有一个,当一个线程获取来类锁,其它线程会处于阻塞或者等待状态,直到该线程指向完毕。
- 修饰方法
对于当前实例对象进行加锁,也就是当前方法进行加锁,在执行实例对象也就是方法前会先获取锁,当另一线程调用非当前实例对象时,不会发生互斥。
public synchronized void method (){
System.out.println("synchronized修饰普通方法");
}
//两种方式是等价的
public void method (){
synchronized(this){
System.out.println("synchronized修饰普通方法");
}
}
/**
* @author 居無何
* date: 2022/5/22 9:40
* Description:
*/
public class customMethod extends java.lang.Thread {
static customMethod thread1 = new customMethod();
static customMethod thread2 = new customMethod();
@Override
public void run() {
method1();
}
public synchronized void method1(){
System.out.println(java.lang.Thread.currentThread().getName()+" 加锁的普通方法");
if(java.lang.Thread.currentThread().getName().equals("Thread-A")){
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(java.lang.Thread.currentThread().getName()+" 运行结束");
}
public void method2(){
System.out.println(java.lang.Thread.currentThread().getName()+" 普通方法");
}
public static void main(String[] args) {
java.lang.Thread A = new java.lang.Thread(thread1);
java.lang.Thread B = new java.lang.Thread(thread2);
A.setName("线程-A");
B.setName("线程-B");
A.start();
B.start();
}
}
- 修饰静态方法
synchronized对静态方法进行修饰,是对整个类进行加锁,即对所有类中的实例对象加锁。
public synchronized void method{
//方法执行体
}
public class staticMethod extends java.lang.Thread {
static staticMethod thread = new staticMethod();
@Override
public void run() {
method1();
if(java.lang.Thread.currentThread().getName().equals("Thread-1")){
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
method3();
method2();
}
public synchronized void method1 (){
System.out.println(java.lang.Thread.currentThread().getName()+" 加锁静态方法");
}
public void method2 (){
System.out.println(java.lang.Thread.currentThread().getName()+" 普通静态方法");
}
public synchronized void method3(){
System.out.println(java.lang.Thread.currentThread().getName()+" 加锁的普通方法");
}
public static void main(String[] args) {
java.lang.Thread thread1 = new java.lang.Thread(thread);
java.lang.Thread thread2 = new java.lang.Thread(thread);
thread1.start();
thread2.start();
}
}
- 修饰代码块
synchronized修饰块,当线程A获得锁,在执行时,会阻塞线程B直到线程A执行完毕。
package customThread;
/**
* @author 居無何
* date: 2022/5/22 14:36
* Description:
*/
public class chunk extends Thread {
public chunk() {
}
@Override
public void run() {
synchronized (this){
System.out.println(java.lang.Thread.currentThread().getName()+" 块内容");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
java.lang.Thread A = new java.lang.Thread(new chunk());
java.lang.Thread B = new java.lang.Thread(new chunk());
A.setName("线程-A");
B.setName("线程-B");
A.start();
B.start();
}
}
lock 锁
lock锁其常用的实现类为ReentrantLock,ReentrantLock是可重入的锁,其大致功能与synchronized一致,但是比synchronized锁更加灵活,通过lock进行加锁,在末尾finally中通过unlock进行解锁。
ReentrantLock的默认构造方法中,实现公平锁,可通过传递Boolean值,确认公平锁与非公平锁。
ReentrantLock可以打断线程,当线程休眠时间长,阻塞其它线程时,ReentrantLock可打断当前线程。
方法名 | 返回值 | 作用 |
---|---|---|
lock() | void | 获取锁 |
unlock() | void | 释放锁 |
isLocked() | boolean | 查询当前锁,是否被线程持有 |
lockInterruptibly() | void | 获取锁,但是优先考虑响应式中断 |
tryLock() | boolean | 获取锁,在其未被其它线程占用时,才可获得到锁 |
tryLock(long,TimeUnit) | boolean | 获取锁,设置等待时间,当锁在等待时间内未被占用,且当前线程仍存在,获得锁 |
public interface Lock {
//加锁
void lock();
//加锁-响应式中断
void lockInterruptibly() throws InterruptedException;
//判断加锁是否成功
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//解锁
void unlock();
//条件变量
Condition newCondition();
}
public class test extends Thread{
public ReentrantLock lock = new ReentrantLock();
public static int value = 100;
@Override
public void run() {
method();
}
public void method (){
Thread thread = Thread.currentThread();
try{
System.out.println(thread.getName()+" 获取锁");
while (value>1){
lock.lock();
System.out.println(thread.getName()+":"+value);
value--;
if (value<1){
break;
}
lock.unlock();
}
}catch (Exception e){
System.out.println(e.getCause());
}
}
public static void main(String[] args) {
Thread A = new Thread(new test());
Thread B = new Thread(new test());
A.setName("线程-A");
B.setName("线程-B");
A.start();
B.start();
}
}
线程池
线程池简介
线程池,如字面意思就是存储线程的池子,其实线程池就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。
线程池其作用就是,减少线程的重复创建和线程销毁的资源损耗,提高线程的复用性,加快线程的响应速度。
线程池的实现类为ThreadPoolExecutor。
//构造方法
public ThreadPoolExecutor(
//核心线程数
int corePoolSize,
//线程池的最大容量
int maximumPoolSize,
//闲置线程的过期时间,超过该闲置时间,线程会被回收
long keepAliveTime,
//时间工具-时间单位
TimeUnit unit,
//任务队列
BlockingQueue<Runnable> workQueue,
//线程工厂
ThreadFactory threadFactory,
//拒绝策略,当线程池达到最大值-后线程的处理策略
RejectedExecutionHandler handler
) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
构造参数
工作队列
- ArrayBalockingQueue
ArrayBalockingQueu队列,底层是基于数组的有界阻塞队列,其采取的是FIFPO先进先出原则,且该数组的长度是有限的,当线程数超过该数组的长度,则会执行拒绝策略。
- LinkedBlockingQueue
LinkedBlockingQueue队列,底层是基于链表的无界阻塞队列,同样采取FIFO先进先出原则,且没有长度限制,也就是在底层规定的是长度Integer.MAX_VALUE。
- SynchronousQueue
SynchronousQueue队列,不缓存线程,在入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何元素,则该线程将被阻塞,直到线程将元素插入队列。
- PriorityBlockingQueue
PriorityBlockingQueue队列使用优先级的无界阻塞队列,优先级的设置通过参数Comparator实现。
线程工厂
线程池配置默认的线程工厂DefaultThreadFactory其是对ThreadFactory接口的实现。
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
拒绝策略
拒绝策略,也就是当线程中的线程到达当前线程池的最大数时,执行拒绝策略,拒绝策略实现的为RejectedExecutionHandler接口,并提供rejectedExecution方法。
其含有四种拒绝策略:CallerRunsPolicy、AbortPolicy(默认)、DiscardPolicy、DiscardOldestPolicy。
public interface RejectedExecutionHandler {
/**
* Method that may be invoked by a {@link ThreadPoolExecutor} when
* {@link ThreadPoolExecutor#execute execute} cannot accept a
* task. This may occur when no more threads or queue slots are
* available because their bounds would be exceeded, or upon
* shutdown of the Executor.
*
* <p>In the absence of other alternatives, the method may throw
* an unchecked {@link RejectedExecutionException}, which will be
* propagated to the caller of {@code execute}.
*
* @param r the runnable task requested to be executed
* @param executor the executor attempting to execute this task
* @throws RejectedExecutionException if there is no remedy
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
- AbortPolicy
AbortPolicy (中止策略),直接抛弃任务线程,并抛出异常RejectedExecutionException 拒绝执行异常。
/**
* The default rejected execution handler
*/
//默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
//直接丢弃任务线程,并抛出异常RejectedExecutionException。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- CallerRunsPolicy
CallerRunsPolicy,若线程池未关闭,则直接调用线程处理该任务。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
//调用线程执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//线程池未关闭-前提条件
if (!e.isShutdown()) {
r.run();
}
}
}
- DiscardPolicy
DiscardPolicy,直接丢弃任务,异常都未抛出。
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
//异常都未抛出!!!!
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- DiscardOldestPolicy
DiscardOldestPolicy,也就是将阻塞队列中最早加入的任务删除,并将当前任务作为新任务添加进去。``
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
//直接将任务,添加到阻塞队列中
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//线程池未关闭-前提条件
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
线程池类型
缓存线程池
**缓存线程池(CachedThreadPool) ,**缓存线程池就是创建一个可缓存的线程池,当该线程池超过可以处理的线程长度,可回收闲置线程(60s过期回收闲置线程)。线程池对线程的大小不做限制,其线程的大小取决于当前系统创建的线程大小。
适用于线程执行量大且线程执行耗时少的工作场景。
//newCachedThreadPool缓存线程池构造方法
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//获取缓存线程池 newCachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
//缓存线程的执行任务-Runnable执行的线程
executorService.execute(Runnable command);
public class test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-线程");
}
});executorService.execute(new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-线程");
}
});executorService.execute(new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-线程");
}
});executorService.execute(new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-线程");
}
});
}
}
定长线程池
** 定长线程池(FixedThreadPool)**,在创建线程池的同时规定线程池的最大并法量,也就是线程存储的最大数量,超出对大并发量时,线程进入队列等待。
//定长线程池构造方法
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//获取定长线程池-规定线程池中线程数量
ExecutorService executorService = Executors.newFixedThreadPool(12);
public class test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(60000);
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-线程");
//当线程数超过,定长线程池规定的最大数,则其他线程则会进入队列中等待。
}
});
}
}
}
单线程线程池
单线程线程池(SingleThreadExecutor),单线程线程池,顾名思义也就是一个线程的线程池,其最大线程池的线程数与线程池大小都为 1 ,当单线程线程池中的线程中断或执行结束,则会有新线程代替该线程,其任务队列为LinkedBlockingQueue。
//构造方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//获取单线程线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
定时线程池
定时线程池(任务线程池-ScheduledThreadPool),定时线程池是用于执行周期性的任务,其核心线程数固定,也就是创建定时线程池传递的corePoolSize值,其非核心线程数不作限制,且使用DelayedWorkQueue延迟队列。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
自定义线程池
自定义线程池,也就是对ThreadPoolExecutor对象实例化,传递相关参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
IO流详解
IO流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出 [1] 。
输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以 流的方式传递 [1] 。
File类
- File介绍
File类所属java.io包中,该类提供文件的目录的操作功能,如创作文件、删除文件、目录、查询目录、更改文件、目录属性等操作。
File类实现Serializable和Comparable接口。
public class File implements Serializable, Comparable<File>{}
- File属性
path-String具体路径、separatorChar分隔符 “\”或者“/”、pathSeparatorChar为“;”
/**
* This abstract pathname's normalized pathname string. A normalized
* pathname string uses the default name-separator character and does not
* contain any duplicate or redundant separators.
*
* @serial
*/
//代表文件的路径-String类型
private final String path;
/**
* The system-dependent default name-separator character. This field is
* initialized to contain the first character of the value of the system
* property <code>file.separator</code>. On UNIX systems the value of this
* field is <code>'/'</code>; on Microsoft Windows systems it is <code>'\\'</code>.
*
* @see java.lang.System#getProperty(java.lang.String)
*/
//与系统相关的默认名称分隔符即“ \ ”,Linux中为“ / ”。
public static final char separatorChar = fs.getSeparator();
/**
* The system-dependent default name-separator character, represented as a
* string for convenience. This string contains a single character, namely
* <code>{@link #separatorChar}</code>.
*/
//与系统相关的默认名称分隔符字符,以方便的方式表示为字符串。
public static final String separator = "" + separatorChar;
/**
* The system-dependent path-separator character. This field is
* initialized to contain the first character of the value of the system
* property <code>path.separator</code>. This character is used to
* separate filenames in a sequence of files given as a <em>path list</em>.
* On UNIX systems, this character is <code>':'</code>; on Microsoft Windows systems it
* is <code>';'</code>.
*
* @see java.lang.System#getProperty(java.lang.String)
*/
//与系统相关的路径分隔符,即“ ; ”。
public static final char pathSeparatorChar = fs.getPathSeparator();
/**
* The system-dependent path-separator character, represented as a string
* for convenience. This string contains a single character, namely
* <code>{@link #pathSeparatorChar}</code>.
*/
//拓展字符
public static final String pathSeparator = "" + pathSeparatorChar;
-
构造方法
- File(String pathname):根据文件路径,获取File对象。
- File(String parent, String child):根据父文件/目录路径和子文件/目录路径,获取File对象。
- File(String child, File parent):根据子文件/目录路径和父File对象,获取File对象。
- File(File parent, String child):根据URL路径,获取FIle对象。
//根据文件路径,获取File对象。
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
//根据父文件/目录路径和子文件/目录路径,获取File对象。
public File(String parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.equals("")) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(fs.normalize(parent),
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}
//根据子文件/目录路径和父File对象,获取File对象。
private File(String child, File parent) {
assert parent.path != null;
assert (!parent.path.equals(""));
this.path = fs.resolve(parent.path, child);
this.prefixLength = parent.prefixLength;
}
//根据UIRL获取File对象。
public File(File parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.path.equals("")) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(parent.path,
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}
public File(URI uri) {
// Check our many preconditions
if (!uri.isAbsolute())
throw new IllegalArgumentException("URI is not absolute");
if (uri.isOpaque())
throw new IllegalArgumentException("URI is not hierarchical");
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
if (uri.getAuthority() != null)
throw new IllegalArgumentException("URI has an authority component");
if (uri.getFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
if (uri.getQuery() != null)
throw new IllegalArgumentException("URI has a query component");
String p = uri.getPath();
if (p.equals(""))
throw new IllegalArgumentException("URI path component is empty");
// Okay, now initialize
p = fs.fromURIPath(p);
if (File.separatorChar != '/')
p = p.replace('/', File.separatorChar);
this.path = fs.normalize(p);
this.prefixLength = fs.prefixLength(this.path);
}
-
绝对路径与相对路径
- 绝对路径:可以通过完整的字符串,定位盘符文件及文件夹,如:C:\Users\Administrator\Documents。
- 相对路径:没有盘符的写法,在当前工程所在的位置找寻。
-
常用方法
-
判断相关
public static void main(String[] args) {
File file = new File("G:\\FileTest\\test.txt");
System.out.println("判断当前路径是否为文件:"+file.isFile());
System.out.println("判断当前路径是否为目录:"+file.isDirectory());
System.out.println("判断当前文件或者路径是否存在:"+file.exists());
System.out.println("判断当前文件是否隐藏:"+file.isHidden());
System.out.println("判断当前文件是否可读:"+file.canRead());
System.out.println("判断当前文件是否可写:"+file.canWrite());
}
- 获取相关
public static void main(String[] args) {
File file = new File("G:\\FileTest\\test.txt");
System.out.println("获取文件的长度:"+file.length());
System.out.println("获取文件最后的修改时间(毫秒值):"+file.lastModified());
System.out.println("获取文件绝对路径:"+file.getAbsolutePath());
System.out.println("获取文件文件名:"+file.getName());
System.out.println("获取父文件夹名:"+file.getParent());
System.out.println("获取父File对象:"+file.getParentFile());
//该两种方法,其返回值为数组-通常遍历子文件/夹数量。
System.out.println("获取所有子名:"+file.list());
System.out.println("获取所有子File对象:"+file.listFiles());
}
- 创建相关
public static void main(String[] args) throws IOException {
//使用mkdir创建文件夹,如果外部无文件夹,则创建不了。
System.out.println("创建单文件夹:"+
new File("G:\\FileTest\\mkdirFile").mkdir());
System.out.println("创建多层文件夹:"+
new File("G:\\FileTest\\mkdirFile1\\mkdirFile2\\mkdirFile3").mkdirs());
System.out.println("创建文件:"+
new File("G:\\FileTest\\mkdirFile1\\mkdirFile2\\mkdirFile3\\mkdirFile.txt").createNewFile());
}
-
删除相关
- boolean = delete():删除文件或空的文件夹 不能删除带元素的文件夹。
IO分类
在java中的IO分类,根据流的流向不同分为输入流和输出流,根据流的单位不同分为字节流和字符流,根据流的功能不同分为节点流和处理流。
在数据传输的底层是,以二进制数字,进行传输。
字节流
字节流,在传输时是一个字节一个字节的传输,字节流可以传输任意格式的文件。字节流又分为字节输出流-OutPutSystem和字节输入流为-InPutSystem两抽象类。
输入和输出,实质是比较对象不同,可以简单的理解是电脑向人输入文件中的数据,展示也就是读,输出是人向电脑文件,输入人的见解,也就是写。
字节输入流-InputStream
字节输入流(InputSystem),该类为抽象类,所属java.io包下,其实现Closeable接口,inputSystem是所有字节输入流的父类,FileInputSystem为其子类,也是常用的输入流。
![输入流.png](https://img-blog.csdnimg.cn/img_convert/20ca05bcd2985da1723555ac156c10c7.png#clientId=ub884b8ec-c2e8-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=uc44d0322&margin=[object Object]&name=输入流.png&originHeight=240&originWidth=526&originalType=binary&ratio=1&rotation=0&showTitle=false&size=26610&status=done&style=none&taskId=uae7c09e7-6197-4e2e-95e2-c3f9fb229c8&title=)
//输入流读取数据到下一字节。
public abstract int read() throws IOException;
//输入流读取数据到下一字节,并将其存储到byte数组中。
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//关闭流通道。
public void close() throws IOException {}
FileInputStream
FileInputStream类,所属java.io包下,继承InputSystem类。
- 构造方法
//根据文件路径创建FileInputSystem对象
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
//根据File对象创建FileInputSystem对象
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name);
}
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
* FileDescriptor is being shared by streams.
* Register this stream with FileDescriptor tracker.
*/
fd.attach(this);
}
- 实例使用
在FileInputStream中常用的是read方法,是一个字节一个字节的读取,其返回值为int,若为-1则表示读到末尾。
FileInputStream fileInputStream = new FileInputStream("G:\\FileTest\\test.txt");
//实际上使用while循环多。
for (int i = 0; i!=-1; i=fileInputStream.read()) {
System.out.print((char) i);
}
int cout ;
while ((cout = fileInputStream.read())!=-1){
System.out.print((char) cout);
}
//关闭流通道。
fileInputStream.close();
字节输出流-OutputSystem
字节输出流(OutputSystem),该类为抽象类,所属java.io包下,其实现Closeable接口,OuputStream是所有字节输入流的父类,FileOutputSystem为其子类,也是常用的输出流。
FileOutputStream
FileOutputStream类,所属java.io包下,并继承OutputStream类,追加数据,在FileOutputStream构造方法中**,传递boolean的append为true**。
- 构造方法
//继承OutputStream类
public class FileOutputStream extends OutputStream{}
//使用该构造方法-会直接覆盖原有文件内容
public FileOutputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, false);
}
//会将写入的数据,追加到原有数据的末尾。
public FileOutputStream(String name, boolean append)
throws FileNotFoundException
{
this(name != null ? new File(name) : null, append);
}
- 使用实例
**使用FileOutputStream主要,使用write方法,**write(int b) 写入单个字节、write(byte b[]) 写入多个字节、write(byte b[], int off, int len) 从len写入byte数组中off处开始的字节。
//创建实例
FileOutputStream fileOutputStream = new FileOutputStream("G:\\FileTest\\test.txt",true);
//String writer1 = "这是写入的内容,通过FileOutputStream类";
String writer2 = "这是追加的文件内容";
byte [] count = writer2.getBytes(StandardCharsets.UTF_8);
//会将文件直接覆盖
//fileOutputStream.write(count);
//追加内容
fileOutputStream.write(count);
fileOutputStream.close();
缓存流
缓冲流,即高效流,是对四个基本流得到增强,缓冲流就是把数据从原始流成块读入或把数据积累到一个大数据块后再成批写出,这个过程是在内存中的缓冲区中实现,通过减少系统资源的读写次数来加快程序的执行。
缓冲流,字节流包括InputStream和OutputStream,字符流包括Reader和Writer。
- Reader 输入流
Reder类,所属java.io包,实现Readable、Closeable接口,其主要实现类 FileReader 文件字符输入流 、**BufferedInputStream **字节缓冲输入流、**BufferedReader **字符缓冲输入流、**InputStreamReader **字节字符转换输入流。
- Writer 输出流
Writer类,所属java.io包,实现 Appendable、Closeable、Flushable 接口,主要的实现类为 **FileWriter **文件输出流、**BufferedOutputStream **字节缓冲输出流、BufferedWriter 字符缓冲输出流、OutputStreamWriter 字节字符转换输出流。
字节缓冲流
- 字节缓冲输入流-BufferedInputStream
BufferInputStream类,所属java.io包,继承自FilterInputStream类。BuffererInputStream会创建一个缓冲区的数组,当在流管道读取,进入缓冲区,然后同一刷新到另一端。
//继承FileInputStream类
public class BufferedInputStream extends FilterInputStream {
//默认缓冲区大小
private static int DEFAULT_BUFFER_SIZE = 8192;
//最大缓冲区大小
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
//构造方法-传递InputStream输入流-其使用FileInputStream作为实现类
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
//构造方法-设置缓冲区的大小
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
//实例化
BufferedInputStream input = null;
inout = new BufferedInputStream(new FileInputStream(new File("FilePath")));
input = new BufferedInputStream(new FileInputStream("FilePath"));
- 字节缓冲输出流-BufferedOutputStream
BufferedOutputStream类,所属java.io包下,继承自FilterOutputStream类,将数据写入文件中。
//继承自FileOutputStream类
public class BufferedOutputStream extends FilterOutputStream {
//构造方法-缓冲区大小8192。
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
//构造方法-自定义缓冲区大小
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
BufferedOutputStream bufferedOutputStream =
new BufferedOutputStream(new FileOutputStream("G:\\FileTest\\test.txt"));
String count = "使用BufferedOutputStream,添加的内容。";
byte [] addCount = count.getBytes(StandardCharsets.UTF_8);
//写入后-刷新
bufferedOutputStream.write(addCount);
bufferedOutputStream.flush();
//关闭流通道
bufferedOutputStream.close();
字符缓冲流
- 字符缓冲输入流-BufferedReader
BufferedReader类,所属java.io包,继承自Reader类,缓冲区读取内容,
//BufferedReader继承Reader类
public class BufferedReader extends Reader {
//默认缓冲区大小
private static int defaultCharBufferSize = 8192;
//构造方法
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}
//读取一行,但需要自己添加换行符 /n
public String readLine() throws IOException {
return readLine(false);
}
}
//使用BufferedReader读取时,可能出现中文乱码,可以使用转换流 InputStreamReader
new BufferedReader(new InputStreamReader(new FileInputStream("path"),"utf-8"));
- 字符缓冲流输出-BufferedWriter
BufferedWriter类,所属java.io包下,继承自Writer类。
public class BufferedWriter extends Writer {
public BufferedWriter(Writer out) {
this(out, defaultCharBufferSize);
}
public BufferedWriter(Writer out, int sz) {
super(out);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.out = out;
cb = new char[sz];
nChars = sz;
nextChar = 0;
lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
}
}
//实例化
BufferedWriter writer = new BufferedWriter(new FileWriter("G:\\FileTest\\test.txt"));
//写入数据
//write(int c) 写入单个字符
//write(char cbuf[], int off, int len) 写入字符数据某一部分
//write(String s, int off, int len) 写入字符串的某部分
writer.write("你好世界!!!!");
//换行
writer.newLine();
//刷新
writer.flush();
//关闭流
writer.close();
文件缓冲流
- 文件缓冲输入流-FileReader
FileReader类,所属java.io包,继承自InputStreamReader类,按字符读取流中的数据。
public class FileReader extends InputStreamReader {
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
public FileReader(FileDescriptor fd) {
super(new FileInputStream(fd));
}
}
FileReader reader = new FileReader(new File("G:\\FileTest\\test.txt"));
int value ;
//读取单字符-使用char数组,规定长度。
while((value = reader.read()) !=-1){
System.out.print((char) value);
}
//关闭管道
reader.close();
- 文件缓冲输出流-FileWriter
FileWriter类,所属java.io包下,继承自OutputStreamWriter类,字符向流中写入数据,追加数据,在FileWriter构造方法中**,传递boolean的append为true**。
public class FileWriter extends OutputStreamWriter {
public FileWriter(String fileName) throws IOException {
super(new FileOutputStream(fileName));
}
//append-是否将数据增加到,指定文件的末尾。
public FileWriter(String fileName, boolean append) throws IOException {
super(new FileOutputStream(fileName, append));
}
public FileWriter(File file) throws IOException {
super(new FileOutputStream(file));
}
//append-是否将数据增加到,指定文件的末尾。
public FileWriter(File file, boolean append) throws IOException {
super(new FileOutputStream(file, append));
}
public FileWriter(FileDescriptor fd) {
super(new FileOutputStream(fd));
}
}
//实例化
FileWriter writer = new FileWriter(new File("G:\\FileTest\\test.txt"),true);
//增加内容
String appText = "通过FileWriter类增加的内容!!!!";
//write(char cbuf[]) 写入如char数组内容
//write(char cbuf[], int off, int len) 写入char数组中部分内容
//write(String str) 写入字符串
//write(String str, int off, int len) 写入字符串部分内容
//换行-"\n"
writer.append("\n");
//直接进数据增加到指定文件的末尾位置。
writer.append(appText);
//刷新流管道
writer.flush();
//关闭流管道
writer.close();
转换流
转换流,就是将字节流转换为字符流,并指定转换的编码集,字符集常见的为ASICII(英文编码)、UTF-8(万国码,中文占三字节)、GBK(汉字内码扩展规范,中文占二字节)。
在将字节流转换为字符流时,可能会出现乱码问题,就是编码字符集与解码字符集不同。
- 字节字符输入转换流-InputStreamReder
InputStreamReader类,所属java.io包下,字节输入流转换为字符输入流的连接桥梁。
//继承自Reader类
public class InputStreamReader extends Reader {
//构造方法是对该属性进行初始化
private final StreamDecoder sd;
//构造方法
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
//设置字符集-charsetName
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
//常见方法
//获取字符集
public String getEncoding() {
return sd.getEncoding();
}
//读取单个字节
public int read() throws IOException {
return sd.read();
}
}
//创建转化流-并指定字符集
InputStreamReader reader = new InputStreamReader(new FileInputStream("G:\\FileTest\\test.txt"),"UTF-8");
//创建缓冲流
BufferedReader bufferedReader = new BufferedReader(reader);
//获取一行
System.out.println(bufferedReader.readLine());
//关闭流管道
bufferedReader.close();
- 字节字符输出转换流-OutputStreamWriter
OutputStreamWriter类,所属java.io包下,继承自Writer类,字节输出流转换为字符输出流的连接桥梁,并指定字符集。
public class OutputStreamWriter extends Writer {
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException
{
super(out);
if (charsetName == null)
throw new NullPointerException("charsetName");
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
}
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("G:\\FileTest\\test.txt"),"UTF-8");
BufferedWriter writer = new BufferedWriter(outputStreamWriter);
writer.append("我的博客网址:juwuhe.top");
writer.flush();
writer.close();