主线1:JavaSE

354 阅读51分钟

Java——一门基于面向对象的编程语言

1.Java体系的划分

①JavaSE:标准版 ②JavaEE:企业版 ③JavaME:微型版

2.关于JDK,JRE,JVM

定义:

JDK----->Java开发工具箱 JRE----->Java运行环境 JVM----->Java虚拟机 (重点)

关系:

①JDK,JRE存在独立的安装包; ②安装JDK时自动安装JRE与JVM; ③安装JRE时自动安装JVM; ④JVM在不同的操作系统上不同,但Java程序均相同。

3.Java的加载与执行

在这里插入图片描述

4.Java的使用与运行步骤

编译期: ①在硬盘任意位置新建一个“xxx.java”文件; ②使用文本编辑器打开该文件; ③在“xxx.java”文件里编写正确的源代码; ④保存并使用编译器(javac[JDK安装后自带])对文件编译; ⑤报错或生成“xxx.class”文件; 运行期: ⑥若上述流程在Windows系统上编译,可将class文件拷贝到其他操作系统上; ⑦使用JDK自带命令工具(java)执行字节码; ⑧JVM程序处理并与硬件交互。

5.Java编译环境的配置与各阶段详解

(1).配置环境遍历path:

"计算机"属性----> "高级系统"中设置----> 环境变量(系统变量作用域全体计算机用户,用户变量仅作用于该用户)----> 点击path----> 修改变量值。

(2).配置环境变量的实质:

将“javac.exe”加入到DOS命令窗口可用范围步骤: 即将java\jdk...\bin配置到path中(用分号间隔前后路径)并重启DOS即可。

(3).查看javac的版本:

命令:javac -version。

(4).编译阶段

①使用命令: javac + java源文件路径 ②快捷方法: 将java源文件拖入DOS命令窗口中; 直接在地址栏上输入“cmd”即可。

(5).运行阶段

①运行前提:class生成了; ②使用命令:java + 类名(无后缀); ③特殊的,在高版本的JDK中,可直接java+源文件路径(编译+运行),但不会生成class文件。

6.代码编写基础理论

(1).HelloWorld实例:

public class HelloWorld {
	//(类体)
	public static void main(String[] args){
		//(方法体)
		System.out.println("Hello World");
	}
}

①class表示一个类,“HelloWorld”是一个类名,后面必须存在{},被称为类体; ②main方法是程序的入口,方法必须放在类体中。

(2).public class与class:

①一个java源文件中可定义多个class; ②public的类不是必须的,可以没有; ③只要有一个class定义,必须生成一个"xxx.class文件"; ④public的类应与源文件名一致; ⑤public的类只能有一个。

(3).标识符:

定义:

凡是程序员可修改(有权利自主命名)的单词;

作用域:

类名,方法名,变量名,接口名,常量名。

命名规则:

①只能由数字,字母,下划线,$(美元符)组成; ②不能以数字开头; ③关键字(public,class等)无法作标识符; ④严格区分大小写; ⑤理论上无长度限制。

命名规范:

①若不遵守规范,不会报错,但可读性较差; ②见名知意; ③遵循驼峰方式(合理利用大小写); ④类名,接口名首字母大写,后面每个单词首字母大写。

(4).关键字:

定义:

官方定义号的一些由特殊含义的单词;

特征:

必须全部用小写;

(5).变量与字面量(含数据类型):

字面量定义:

在java语言中的数据被称为“字面量”;

字面量种类:

①整数型 ②浮点型 ③布尔型 ④字符型 ⑤字符串型

变量定义:

内存中储存数据的基本单位。

数据类型: 基本数据类型:

①整型:byte(一个字节),short(两个字节),int(四个字节),long(八个字节); ②浮点型:float(四个字节),double(八个字节); ③布尔型:boolean(一个字节); ④字符型:char(两个字节);

引用数据类型:

典例:String,自定义类等等; 在java中除了基本数据类型,其他均为引用数据类型。

计算机储存单位:

一个字节等于八个比特位 1byte = 8bit; 1KB = 1024byte; 1MB = 1024KB; 1GB = 1024MB; 1bit即为计算机中二进制中的“0”,“1”; 在二进制中,最左边的数字代表符号:“0”为正,“1”为负;

存储范围:

①byte [-128127]即2^7; ②short [-3276832767]即2^15; ③int [-21474836482147483647]即2^31; char [065535]即2^16;

数据类型的转换:

①在Java中,整数型变量被默认当作int处理,若需转换成long应在字面量后加L; ②小容量可直接赋值为大容量,称为自动类型转换; ③大容量转小容量可加强制类型转换,可使编译通过,但可能会损失精度; ④当一个整数赋值给char时,会根据ASCII码值自动转换成对应字符; ⑤多种数据类型作混合运算时,最终结果类型为容量最大的类型; ⑥int作除法时会取整,可能会降低精度。 ⑦double比float存储值更加准确; ⑧java中规定,浮点数被默认当作double处理,若需用float,则在数字后面加f。 ⑨布尔类型仅有true和false俩个值,用于逻辑的判断。

变量三要素:

类型+名字+值(类型决定空间大小)。

变量的声明:

数据类型+变量名; 在java中,变量必须先声明再赋值才能正常运行。

变量的分类:

①成员变量:在方法体外; ②局部变量:在方法体内; ③局部变量仅在方法体中有效,出了方法便会释放内存。

(6).运算符:

算术运算符:

①算术运算符分为+ - * / % ++ --; ②++使变量自加一; ③当++出现在变量后时,会优先做赋值运算,再自加一; ④当++出现在变量前时,会优先自加一,再做赋值运算;

关系运算符:

①关系运算符分为> < = >= <= == !=; ②对关系运算符中,结果只可能输出布尔类型数据; ③“=”是赋值操作,而“==”是关系运算符;

逻辑运算符:

①逻辑运算符包括&(逻辑与) |(逻辑或) ! &&(短路与) ||(短路或); ②逻辑运算符中,结果只可能输出布尔类型数据; ③逻辑和短路运算符输出结果完全相同; ④短路运算符会出现短路现象,即逻辑符号右边表示不执行; ⑤&&(||)效率比&(|)高,一般用&&(||);

赋值运算符:

①赋值运算符包括+= -= *= /= %=; ②x+=20与x=x+20不完全相同 例如: byte x = 100;x+=20;sout(x)=>120; byte x = 100;x = x+20;sout(x)=>编译报错,类型不匹配;

三目运算符:

①语法格式:布尔表达式?表达式1:表达式2; ②执行原理: 当布尔表达式为true时,执行表达式1; 当布尔表达式为false时,执行表达式2。

+的作用详解:

①求和作用:对于两边都是数字的情况; ②字符串的拼接:对于两边都是字符串的情况,结果仍是字符串。

(7).选择语句(if,switch):

if语句:

①if语句语法: if(布尔表达式){ java语句 }; ②执行条件:当布尔表达式为true时,执行java语句; ③if...else...执行语法: if(布尔表达式){ java语句}else{ java语句}

switch语句:

④switch语句(仅支持int类型和String类型)语法: switch(变量){ case 值1: java语句 break; case 值2: java语句 break; ... default: java语句 } ⑤在switch语句中,务必不要忘记break,否则会发生穿透现象。

(8).循环语句(for,while,do...while):

for语句:

①语法规则: for(初始化表达式;条件表达式;更新表达式){ 循环体; } ②初始化表达式仅在语句中执行一次; ③条件表达式必须是布尔类型; ④执行顺序:初始化表达式->条件表达式->循环体->更新表达式;

while语句:

①语法规则: while(布尔表达式){ 循环体;}

do...while语句:

①语法规则: do{ 循环体;}while(布尔表达式); ②执行原理: 先执行一次循环体,再判断布尔式。

(9).转向语句:

break语句:

①使用条件: a.用于switch语句,终止循环的执行; b.用于循环语句,终止循环的执行; ②运行原理: break语句会终止离它最近的循环; ③额外用法: 当由多层循环时,可对for做修饰,终止指定的循环: 例如: a:for(){} b:for(){} break a; //终止a的循环

continue语句:

①作用: 终止本次循环,直接进入下一循环; 额外用法与break语句相似;

(10).方法(c语言中的函数):

定义:

一段可以被重复利用的代码片段,可以用来完成某种特定功能;

详解:

①语法格式: [修饰符列表]+返回值类型+方法名(形式参数列表){方法体} ②修饰符列表为非必选项; ③返回值类型只要是数据类型就行; ④返回值为空时,返回值类型写为void; ⑤方法实现后需要去调用,否则不会执行; ⑥只要有return 关键字执行,方法必须结束; ⑦return后跟的值必须与返回值类型相吻合; ⑧return 语句用于终止当前方法; ⑨方法名要见名知意,且首字母需小写,后面每个单词字母大写; ⑩形参的参数均为局部变量,方法结束后内存释放; ⑪方法体中代码遵循自上而下的顺序执行。

方法的调用:

①语法: 类名.方法名(实际参数列表); ②实参和形参必须一一对应,类型和个数都需一一对应; ③当调用方法和被调用方法在一个类中时,类名可省略。

方法调用与JVM:

①JVM主要有三块内存:栈内存,堆内存,方法区内存; ②方法区中最先有数据,存放代码片段,class字节码文件; ③栈内存:方法调用的时候,方法在所需内存空间中在栈中分配; ④调用方法时叫作“压栈”,执行结束后,内存释放,叫作“弹栈”; ⑤栈中储存调用方法所需内存,以及方法中的局部变量; ⑥栈有先进后出,后进先出的原则。

方法重载:

①定义:功能相似的方法可让其“方法名”相同。 ②优点: 代码较为美观且易于代码的编写; ③编译器会优先通过方法名区分方法,当存在多种方法名相同时,编译器通过方法的参数类型进行区分。 ④发生条件:在同一个类中,方法名相同,参数列表不同; ⑤方法重载仅与方法名和形参列表有关; ⑥println()是一个典型的方法重载的例子。

方法递归:

①定义:方法调用该方法自己叫作“方法递归”; ②方法递归必须存在结束条件; ③当递归时程序无结束条件,会发生“StackOverflowError”栈内存溢出错误; ④递归使用时耗费内存较大,易报错;

7.面向对象

(1).面向过程与面向对象:

面向过程开发:

①缺点:代码之间耦合度高,扩展性太差; ②优点:对小型项目或功能,面向过程效率较高,快速开发;

面向对象开发:

①优点:更符合人类的思维方式,耦合度低,扩展力强; ②面向对象的三个术语: OOA:面向对象分析(Object-Oriented Analysis) OOD:面向对象设计(Object-Oriented Design) OOP:面向对象编程(Object-Oriented Programming) ③面向对象的三大特征: 封装 继承 多态

(2).类与对象

类与对象基础:

①类:不实际存在,一个抽象的概念; ②对象:实际存在的个体; ③关系:在java中,想要得到对象,必须根据类的模板创造出来(实例化); 类中描述的是对象的共同特征。 ④类的组成: 属性+方法; ⑤类的语法: [修饰符列表] class+类名{类体} ⑥类体中包含属性和方法; ⑦对象的创建和使用语法: new+类名(); //new是一个运算符,专门负责对象的创建 ⑧new过程的详解: 凡是通过“new”创建出来的对象,都存在“堆内存”中; new的作用是在堆内存中开辟一块空间;

实例变量: ①对象的“实例变量”储存在堆内存中; ②局部变量和方法存储在栈内存中; ③对于成员变量来说,如果未手动赋值,则系统赋默认值。 ④保存对象内存地址的变量,叫作“引用”; ⑤对象又被成为“实例”,实例变量是对象级的变量; ⑥一个实例对象仅能对应一个对象; ⑦访问实例变量语法: 引用.实例变量名;

空指针异常:

①“NullPointerException”空引用访问对象的数据时出现; ②空指针异常出现在运行阶段,能正常编译;

(3).构造方法:

作用与特性:

①作用:完成对象的创建并给对象的属性赋值; ②特性:当一个类没有构造方法时,系统会默认给一个无参数构造方法;

构造方法的调用:

[修饰符列表]+构造方法名(形参列表){构造方法体}

构造方法与普通方法:

①构造方法修饰符列表使用public; ②构造方法名必须与类名一致; ③构造方法无需返回值类型; ④构造方法支持方法重载;

(4).封装:

封装的作用:

①保证内部结构的安全; ②屏蔽复杂,暴露简单; ③保证仅开发人员可知;

封装的步骤:

①属性私有化(使用private关键字对变量修饰); ②对外提供简单的入口;

利用set,get为程序设定私有化入口: ①get方法: public+返回值类型+get属性名(属性名首字母大写)(){return xxx;} ②set方法: public void set属性名(属性名首字母大写)(一个参数){ xxx = 参数;} ③可在set方法中设立关卡,使修改数据符合实际。

(5).static & this 关键字:

static关键字概述:

①所有static修饰的都是类级别的; ②static修饰的都采用“类名.”的方式访问; ③static所修饰的都是静态的:比如:静态变量,静态方法; ④没有static修饰的都是实例的,需要采用“引用.”的方式来访问; ⑤简而言之,一个对象一份是“实例变量”,所有对象一份是“静态变量”; ⑥当该类型所有对象某一个属性全部相同时,定义成静态变量; ⑦当一个方法中直接访问了实例变量,则该方法一定是实例方法。

成员变量的储存:

①静态变量存在==方法区==中; ②实例变量存在==堆==中; ③局部变量存在==栈==中;

静态代码块:

①静态代码块类加载时执行,仅执行一次; ②静态代码块在main方法前执行; ③语法结构:static{.....;} ④作用:类加载时执行代码,比如初始化程序,记录日志等功能可用;

实例代码块:

①语法结构:{......;}; ②该代码块在实例方法执行前执行; ③可提出实例方法中前面的重复代码放入该代码块中。

this关键字概述:

①一个对象对应一个this; ②this是一个“引用”,保存当前对象的内存地址,指向自身; ③this从实质上来说代表当前对象; ④this存储在堆内存中对象的内部; ⑤this只用于实例方法,谁调用这个方法,this指向谁; ⑥this大部分情况可省略;在构造方法中,this用于区分局部变量与实例变量不可省略; ⑦this可用于构造方法中,通过一个构造方法调另一个构造方法时代码复用,两个构造方法必须在同一类中,用法:this(实际参数列表),该用法仅能用于构造方法的第一行且只能写一行。

(6).继承:

继承的作用:

①子类继承父类,代码得到复用; ②因为有了继承,才有了后面的方法覆盖和多态;

继承的特性:

①B类继承A类,则B类为子类,超类,A类为父类,基类; ②java仅仅允许单继承,不能出现“class C extends A,B”(C++可以); ③尽管只允许单继承,但可间接继承,比如“class C extends B;class B extends A;”; ④java中,不可以继承构造方法,其他的都可以继承,但私有的无法在子类中直接访问; ⑤Java中无显示继承的默认继承“Object”类,“Object”是所有类的父类。

(7).super关键字:

super和this的对比:

①二者都可出现在实例方法和构造方法中; ②语法结构: this: "this."或者"this()"; super:"super."或者"super()"; ③二者大部分情况都可以省略; ④this()和super()的用法对比: this():仅出现在构造方法第一行,通过构造方法去调用“本类”中的其他构造方法,实现代码复用; super():仅出现在构造方法第一行,通过当前构造方法调用"父类"中的构造方法,目的是:创建子类对象时,先初始化父类型的特征。

super()的概述:

①作用:通过子类的构造方法调用父类的构造方法; ②当构造方法中既没this(),有没有super()时,默认有一个super(),表示通过当前子类调用父类的构造方法,所以必须保证父类的无参构造是存在的; ③this()和super()不能共存,因为其二者都只能出现在构造方法第一行; ④无论如何,父类的构造方法一定会执行。

super.的概述:

①super.大部分情况可省略,当父类和子类中有同名属性或方法时,想通过子类访问父类属性或方法时,不可省略super.;

总结super关键字:

①super.属性名; //访问父类的属性; ②super.方法名(实参); //访问父类的方法; ③super(实参); //调用父类的构造方法;

(8).方法覆盖(方法重写)与多态(toString()方法):

方法覆盖的作用:

子类继承父类时,继承过来的方法已无法满足客户的需求,子类有权对其进行重写以满足业务需求。

方法覆盖的使用条件:

①两个类必须有继承关系; ②重写前后方法具有相同的形参列表,返回值类型,方法名; ③访问权限不能更低,只能更高; ④重写之后的方法不能比之前方法抛出更多的异常; ⑤私有方法和构造方法无法被覆盖; ⑥仅针对于“实例方法”;

关于Object类中的toString方法简介:

①作用:将java对象转换成字符串形式,输出对象的内存地址; ②特点:直接调用对象时,默认调出对象的toString()方法;

向上转型与向下转型:

①前提条件:java中无论向上转型还是向下转型,两种类型必须存在继承关系; ②向上转型定义:父类型引用指向子类型对象(自动类型转换) 例子:Object obj = new Students(); ③向下转型定义:子类型引用指向父类型对象(强制类型转换) 例子:Students s = (Students)new Object(); ④向下转型语法:子类 变量 = (子类)父类对象;

多态:

①含义:父类型引用指向子类型对象; ②实质: 在编译阶段,绑定父类的方法; 在运行阶段,动态绑定子类对象的方向。

ClassCastException与instanceof运算符:

①类型转换异常:ClassCastException; ②instanceof运算符: a.instanceof可在运行阶段动态判断引用指向的对象类型; b.语法格式:引用 instanceof 类型; c.得到的结果是布尔类型,如果是true,代表引用指向的堆内存中的java对象是该类型; d.使用该运算符可用很好地避免CCE异常; ③用法实例:

if(a instanceof Cat){
  	Cat c = (Cat)	a;
  	c.catchMouse();	
}

在开发中的作用:降低程序的耦合度,提高程序的扩展力。

Java——一套实用的庞大类库

@[toc] 集成开发工具IDEA层次划分: 工程(Project)----->模块(Module)。

8.进阶模块

(1).final关键字

理解:final在英文中本意为最终的,不可变的,可便于理解为==final修饰的东西无法修改==; ②用法: <1>.final修饰的类无法继承; <2>.final修饰的方法无法被覆盖; <3>.final修饰的变量仅能赋一次值; final修饰的引用仅能指向一个对象,并且无法指向其他对象,但对象中的数值可被改变;final修饰的实例变量必须手动赋值; ③常量:无法被改变; 常量常常使用“static final”修饰,且全部大写,单词间使用下划线分隔。 例如:public static final double PI = 3.1415926;

(2).包装与导包:

package关键字:

定义:java中的“包”机制,方便程序的管理; ②语法:==package+包名;== 常常允许出现在java源代码的第一行; ③定义规范:公司域名倒序+项目名+模块名+功能名; ④编译与运行: <1>编译:==javac -d.(被编译的java文件名.java);== "-d"代表带包编译,“.”代表当前目录; <2>运行:==java (命名规范);==

import关键字:

定义:导包关键字; ②使用情况: <1>A类中使用B,A与B不在一个包,使用import; <2>A类中使用B,A与B在一个包,不使用import; <3>在java.lang.*包下无需导包import。 ③用法: <1>import只能出现在package下面,类的声明上面; <2>==import (命名规范);==

(3).抽象类和接口:

抽象类(Abstract class):

概述: <1>类和类之间存在共同特征,将共同特征提取出来,形成抽象类; <2>抽象类属于引用数据类型; <3>抽象类无法实例化,用于被子类继承; <4>final和abstract对立,不可联合使用; <5>抽象类的子类也是抽象类,存在构造方法,供子类使用; ②语法规则: ==[修饰符列表] abstract class 类名{...}==

抽象方法(Abstract method)

概述: <1>抽象方法没有方法体,例如:public abstract void dosome(); <2>抽象类不一定有抽象方法,但抽象方法必须出现在抽象类中; <3>非抽象类继承抽象类时,必须实现其中的抽象方法,或者将非抽象类改为抽象类。 ②语法: ==[修饰符列表] abstract 返回值类型 方法名();==

接口(interface)

概述: <1>接口是一种引用数据类型,是完全抽象的; <2>接口支持多继承,一个接口可继承多个多个接口; <3>接口中仅包含两部分内容: a.常量,例如:public static final int PI; b.抽象方法,例如:public abstract void xx(); <4>接口都是公开的(public),接口中的public abstract可以省略; <5>接口中的常量修饰部分public static abstract可省略; ②语法: ==[修饰符列表] interface 接口名{...}==

接口与抽象类

①类与类之间的继承关系在类与接口中叫作“实现(implements)”; ②继承使用extends关键字,实现使用implements关键字; ③当一个非抽象类实现接口时,必须将接口中的所有抽象方法实现; ④接口与接口之间支持多继承,一个类可实现多个接口; ⑤当implements和extends同时存在时,先写extends; ⑥抽象类和接口的区别: <1>抽象类是半抽象的,而接口完全抽象; <2>抽象类有构造方法,接口无构造方法; <3>接口与接口间支持多继承,类与类中仅能继承一个类; <4>一个类可同时实现多个接口,而只能继承一个类;

(4).访问控制权限:

相关关键字:public,protected,默认,private。 ②控制范围比较:public>protected>默认>private。

控制权限详解: 在这里插入图片描述

(5).数组:

数组概述:

①引用数据类型,其父类是Object,被存在堆内存中; ②可储存“基本数据类型”,也可储存“引用数据类型”; ③若数组中存的是Java对象,应存的其内存地址; ④数组一经创建,其长度不可变且自带Length属性; ⑤所储存的数据必须类型统一; ⑥数组在内存方面存储,其中的内存地址与第一个元素内存地址相同,且连续; ⑦下标从“0”开始,到Length-1结束; ⑧优缺点: <1>优点: 检索效率高,通过数学表达式可计算某个下标的内存地址,从而定位元素; <2>缺点: a.在数组上随机增删元素时,效率较低; b.很难在内存空间上找到一块特别大的连续的内存空间;

一维数组:

语法: <1>静态初始化: ==数据类型[] 变量名 = {,,,};== ==数据类型 变量名[] = {,,,};== <2>动态初始化: ==数据类型[] 变量名 = new 数据类型[数组长度];== ②数组的扩容: 新建(大容量) ---> 拷贝 ----> 删除旧的数组; ③数组的拷贝: 调用System类中的arraycopy方法: **System.arraycopy(源变量,源起点,目标变量,目标起点,长度);

二维数组:

语法: <1>静态初始化: ==数据类型[][] 变量名 = {{...},{...},...};== <2>动态初始化: ==数据类型[][] 变量名 = new 数据类型[数组长度][数组长度];== <3>元素访问: ==变量名 [下标][下标];== ②二维数组是特殊的一维数组,其中每个元素都是一个一维数组。

数组工具类Arrays:(java.util.Arrays)

static sort(数据类型[] xx); 用于给数组中元素排序。 ②static binarySearch(数组名 xx,数据类型 xx); 用于查找数组中的某个元素。

9.java.lang.String

(1).概述:

Ⅰ.JDK中凡是双引号括起来的字符串存储在方法区的字符串常量池中。 Ⅱ.new String(参数列表)将在==堆==中创建字符串对象。 Ⅲ.String类型的变量保存的是字符串常量池中的内存地址而非数据。

(2).构造方法:

//一个参数的构造方法
new String(byte[] bytes);
//三个参数的构造方法
/*
*@param bytes byte类型的数组
*@param offset 起始下标(从0开始)
*@param length 选取长度
*/
new String(byte[] bytes,int offset,int length);
/*char同理*/

(3).方法:

<1>public char charAt(int index);

使用案例:

		String string = "中国人";
        char str1 = string.charAt(1);
        System.out.println(str1);//str1 = "国"

<2>public int compareTo(String anotherString);

使用案例:

	/*
	*对比前后字符串大小。
	*@param anotherString 需要对比的字符串
	*@return result:
	*				>0  前大后小
	*				=0  两值相等
	*				<0	前小后大
	*/
		String num1 = "5";
        String num2 = "6";
        int result = num1.compareTo(num2);
        System.out.println(result);//result = -1

<3>public boolean contains(CharSequence s);

源码解析:

/*
*判断前字符串是否包含后字符串
*/
public boolean contains(CharSequence s) {
        return indexOf(s.toString()) >= 0;
    }

<4>public boolean endsWith(String suffix);

源码解析:

/*
*判断前字符串是否以suffix结尾
*/
public boolean endsWith(String suffix) {
        return startsWith(suffix, length() - suffix.length());
    }

<5>public boolean startsWith(String prefix);

/*
*判断某字符串是否以prefix开始
*/

<6>public boolean equalsIgnoreCase(String anotherString);

/*
*判断两字符串是否相等,忽略大小写
*/

<7>public int indexOf(String str);

/*
*@return 返回str在当前字符串中第一次出现的下标
*/

<8>public byte[] getBytes();

/*
*将字符串对象转换为byte[]数组
*/

<9>public boolean isEmpty();

源码解析:

/*
*判断该字符串是否为空
*/
public boolean isEmpty() {
        return value.length == 0;
    }

<10>public int lastIndexOf(String str);

/*
*@return 返回str在当前字符串中最后一次出现的下标
*/

<11>public String replace(String target,String replacement);

/*
*使用replacement替换原来的target部分
*@return 返回新的字符串
*/

<12>public String substring(int beginIndex);

/*
*截取字符串
*@param beginIndex:起始下标
*@return 截取后的字符串
*/

<13>public String substring(int beginIndex,int endIndex);

/*
*截取字符串
*@param beginIndex:起始下标 endIndex:结尾下标 范围:[begin,end)
*@return 返回截取后的字符串
*/

<14>public char[] toCharArray();

/*
*将字符串转换为char[]数组
*/

<15>.public String toLowerCase();

/*
*将字符串转为小写
*/

<16>public String toUpperCase();

/*
*将字符串转换为大写
*/

<17>public static String valueOf(String str);

/*
*将非字符串转换为字符串
*/

10.java.lang.[StringBuilder]&[StringBuffer]

(1)简介:

用于字符串的拼接

(2)构造方法:

new StringBuffer();
new StringBuffer(int capacity);//手动设置容量

StringBuffer底层创建一个容量为16的byte[]数组,可自动扩容。

(3)拼接方法:

StringBuffer buffer = new StringBuffer(16);
buffer.append("中");
buffer.append("国");
buffer.append("人");
System.out.println(buffer);

(4)性能优化与对比

优化StringBuffer性能:

Ⅰ.在创建对象时可给定一个初始化容量; Ⅱ.最好减少数组的扩容次数,给预估的初始化容量。

对比StringBuilder和StringBuffer:

Ⅰ.方法,构造方法均类似; Ⅱ.本质区别是 StringBuffer是线程安全的; StringBuilder是线程不安全的;

对比String和StringBuffer:

Ⅰ.StringBuffer对象的内容可以修改,而String对象一旦产生后就不可以被修改; Ⅱ.StringBuffer的内部实现方式和String不同,StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。 Ⅲ.String:在String类中没有用来改变已有字符串中的某个字符的方法,由于不能改变一个java字符串中的某个单独字符,所以在JDK文档中称String类的对象是不可改变的。然而,不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的(方法区中的【字符串常量池】)。

11.包装类(java.lang.(包装类))

(1).八种包装类的简介:

    基本数据类型    父类(均在java.lang包)    包装类型             byte           java.lang.Byte                   Byte

            short          java.lang.Short                 Short

            int              java.lang.Integer               Integer

            long           java.lang.Long                  Long

            float           java.lang.Float                  Float

            double       java.lang.Double              Double

            boolean     java.lang.Boolean            Boolean

            char           java.lang.Character         Character

(2).装箱与拆箱:

装箱:利用包装类将“基本数据类型”转为“引用数据类型” 利用Integer的构造方法:

//将int类型包装为Integer类型(已过时)
Integer i = new Integer(int value);

//将String类型包装为Integer类型(已过时)
Integer i = new Integer(String str);

//使用Integer的静态方法valueOf()(主流方法)
int inti = Integer.valueOf(int value);

拆箱:利用一些方法将“引用数据类型”转为“基本数据类型” 利用包装类自带的((数据类型)+Value())方法

Integer i = new Integer(123);
int inti = i.intValue();

(3).获取数据类型的极限值:

//获取int类型的极限值
int max_Int = Integer.MAX_VALUE;//max_Int=2^31-1
int min_Int = Integer.MIN_VALUE;//min_Int=-2^31
int min_byte = Byte.MIN_VALUE;//min_byte=-2^7
int min_short = Short.MIN_VALUE;//min_short=-2^15

(4).IndexOutOfBoundsException:

简介: 数字格式化异常。 案例:

Integer x = new Integer("中文");

(5).包装类的优点:

由于包装类是高级数据类型,合理利用包装类可有效预防NullPointerException

(6).int,String,Integer三者转换:

int inti = 123;
Integer i = 123;
String stri = "123";

//int--->Integer(装箱)
(Integer) i = Integer.valueOf(inti);
//Integer--->int(拆箱)
(int) inti = i.intValue();
//int--->String
(String) stri = String.valueOf(inti);
(String) stri = inti+" ";
//String--->int
(int) inti = parseInt(stri);
//String--->Integer
(Integer) i = Integer.valueOf(stri);
//Integer--->String
(String) stri = String.valueOf(i);

12.日期类

(1).java.util.Date:

构造方法:

//输出当前时间
Date date = new Date();
System.out.println(date.toString);

(2).java.text.SimpleDateFormat:

作用: 专门负责日期的格式化; 构造方法:

//有参构造
/*
*@param pattern 输出日期的格式
比如:标准格式 "yyyy-MM-dd HH:mm:ss:SSS"
*/
public SimpleDateFormat(String pattern);

方法: a.调用该类中的format(Date)方法可输出修改后格式的时间; b.调用System类中的currentTimeMills()方法可知道从【1970-1-1 00:00:00】到现在的时间毫秒数。

//以指定格式输出当前时间:
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
String currentTime = sdf.format(date);
System.out.println(currentTime);

System类功能拓展:

Ⅰ.System.out //输出函数包 Ⅱ.System.gc() //启动垃圾回收机制 Ⅲ.System.currentTimeMills() //获取从1970-1-1 00:00:00到当前的毫秒数 Ⅳ.System.exit(0) //退出JVM

13.数字类

数字格式的介绍:

#:任意数字 ,:千分位 .:小数点 0:位数不够时补零

(1).java.text.DecimalFormat:

作用:专门负责数字的格式化。 构造方法:

/*
*param pattern:输出数字的格式
*例如:标准格式:#,###.##
*/
new DecimalFormat(String pattern);

方法:

//输出更改格式后的数字
DecimalFormat df = new DecimalFormat("#,###.##");
String format = df.format(Integer.MAX_VALUE);
System.out.println(format);

(2).java.math.BigDecimal:

简介:属于引用数据类型,称为“大数据”。 构造方法:

/*
*@param var:需要在大数据中存入的变量
*形参处存在字符,字符串,浮点型,数组等等。
*/
new BigDecimal(int var);

方法:

BigDecimal bd1 = new BigDecimal(-123);
BigDecimal bd2 = new BigDecimal(456);
//求和函数
BigDecimal addResult = bd1.add(bd2);//addResult=333
//绝对值函数
BigDecimal absResult = bd1.abs();//absResult=123
//求差函数
BigDecimal subResult = bd1.subtract(bd2);//subResult=-579

14.随机数类

作用:生成一定范围的随机数。 构造方法:

new Random();

方法:

Random r = new Random();
//生成int范围内的随机数
int numble = r.nextInt();
//生成指定范围内的随机数[0,bound)
int numble = r.nextInt(int bound);

15.枚举

作用:一种引用数据类型,结果超过两种可用一枚一枚列举出的结果。 语法规则:

//季节枚举
enum Season{
	SPRING,SUMMER,AUTUMN,WINTER
}

16.异常

(1).概述:

Ⅰ.异常时程序执行过程中的不正常情况; Ⅱ.其作用是增强程序的健壮性; Ⅲ.异常在java中以类的形式存在,每个异常类都可以实例化对象; Ⅳ.编译时异常(受检异常)和运行时异常(未受检异常)都发生在运行阶段。

(2).异常的处理:

Ⅰ.方法声明位置上,使用throws关键字抛出异常(谁调用方法抛给谁); Ⅱ.使用try...catch语句进行异常捕捉; Ⅲ.throw手动抛出异常; Ⅳ.只要异常没有被捕捉,那么采用throws方法时,后续代码不会执行; Ⅴ.try...catch捕捉后,后续代码可以执行。

(3).异常对象的相关方法:

构造方法:

//无参构造
new Exception();
//有参构造
/*
*@param message:添加打印异常描述信息
*/
new Exception(String message);

方法:

Exception exception = new Exception();
//获取异常简单描述信息
String massage = exception.getMassage();
//打印异常的堆栈信息
exception.printStackTrace();

(4).finally语句:

概述:

Ⅰ.在finally子句中的代码最后执行且一定会执行; Ⅱ.finally子句必须和try...catch一起出现; Ⅲ.finally子句也可和try...语句一起使用,try语块中的return最后执行; Ⅳ.可用于关闭IO流和jdbc;

final,finally,finalize的区别:

a.final用于修饰变量,方法和类,表示不可变; b.finalize是Object中的一个方法名; c.finally用于处理异常。

17.Object类

(1).toString()方法:

Ⅰ.默认实现:类名+@(对象的内存地址16进制形式); Ⅱ.作用:将一个java对象转换为字符串形式; Ⅲ.为了得到简介,详实的输出结果,需重写toString()方法。

(2).equals()方法:

Ⅰ.作用:使用该方法判断两个对象是否相等; Ⅱ.“==”对于对象来说比较对象的内存地址而非对象内容; Ⅲ.“equals()”中存在着Ⅱ中的比较方式; Ⅳ.调用equals()方法时需重写该方法,比较内容而并非其内存地址; Ⅴ.String类中已重写了equals和toString方法,可在String中直接使用。

(3).hashCode()方法:

作用:得到对象的哈希值。

Java-----集合,IO流

1.集合(java.util.*)

(1).概述

Ⅰ.集合实际上是一个容器(载体),可存储多个对象;(数组是最简单的集合); Ⅱ.集合中不能存储基本数据类型和java对象,存储的是java对象的内存地址; Ⅲ.每个不同的集合底层对应不同的数据结构(数组,二叉树,链表,哈希表...);

(2).集合的分类和结构图

<1>.Collection接口:

超级父接口:java.util.Collection

Collection继承结构图:

在这里插入图片描述 概述:

①.Collection可存放Object的所有子类对象; ②.由于Collection是接口,无法实例化,new对象应用多态机制创建子类的实现类对象。

b.常用方法 A.向集合中添加一个元素

//向集合中添加一个元素
public boolean add(Object e);

B.获取集合中的元素个数

//获取集合中元素的个数
public int size();

C.清空集合中的元素

//清空集合中的元素
public void clear();

D.判断集合中是否包含某元素 Tips: 该方法中底层调用了equals方法,放在集合中的元素应重写equals方法。

//判断集合中是否包含某元素
public boolean contains(Object e);

E.移除集合中的某元素

//移除集合中的某元素
public boolean remove(Object e);

F.判断集合是否为空

//判断集合是否为空
public boolean isEmpty();

G.将集合转换为数组

//将集合转换为数组
public Object[] toArray();

<2>.Iterator迭代器接口:

a.获取迭代器对象:

Collection c = new ArrayList();
Iterator it = c.iterator();
//iterator()方法是Collection接口的父接口Iterable中的方法

b.迭代器中的方法:


A.判断集合是否可以迭代

//判断集合是否可以迭代
public boolean hasNext();

B.返回迭代的下一个元素

//返回迭代的下一个元素
public Object next();

C.删除当前指向的元素

//删除当前指向的元素
public void remove();

c.使用迭代器需要注意的: A.对迭代器来说,当集合结构发生改变时,迭代器必须重新获取; B.采用迭代器的remove()方法删除可自动更新迭代器。

<3>.List接口:存储元素有下标,有序可重复

a.常用方法:


A.在指定位置i处添加元素

//在指定位置i处添加元素
public void add(int index,Object element);

B.根据下标获取元素

//根据下标获取元素
public Object get(int index);

C.获取指定对象第一次出现的索引

//获取指定对象第一次出现的索引
public int indexOf(Object element);

D.获取指定对象最后一次出现的索引

//获取指定对象最后一次出现的索引
public int lastIndexOf(Object element);

E.删除指定位置的元素

//删除指定位置的元素
public void remove(int index);

F.修改指定位置的元素值

//修改指定位置的元素值
public void set(int index,Object Element);

b.ArrayList类详解:

A.初始容量 ArrayList集合底层由数组实现,初始容量为10,也可通过构造方法new ArrayList(int capacity); 指定初始化容量。 B.扩容 当存储空间不够时,该集合自动扩容1.5倍。 本质上是位运算符二进制右移一位1010>>1=0101--->5,左移同理。 C.优缺点 优点:检索效率较高,末尾加元素效率较高; 缺点:增删效率较低。


c.LinkedList类详解:

A.链表数据结构 结点Node是链表中的基本单元。 单向链表: 每一个Node中有两个属性:存储的数据和下一个结点的内存地址。 双向链表: 每一个Node中有三个属性:存储的数据,下一个和上一个结点内存地址。

B.优缺点 优点:随机增删元素的效率较高; 缺点:查询效率较低,查找元素需从头开始向下遍历。


d.Vector类详解:

A.初始容量 Vector集合底层由数组实现,初始化容量是10,也可通过构造方法new Vector(int initialCapacity);设置初始化容量。 B.扩容 Vector存储空间用满时,自动扩容2倍。 C.方法特征 Vector集合中的方法都是线程安全的(带有synchronized关键字)。


e.泛型机制:


A.作用 规定集合中存储的元素类型固定,集合中数据类型更统一。 B.语法机制

//规定List方法中仅能存储String类型的数据 例子
//方法一:
List<String> l = new ArrayList<>();
//方法二:
List l = new ArrayList<String>();
//方法三:
List<String> l = new ArrayList(); 

C.优缺点 优点: ①集合中存储的元素类型统一了; ②从集合中取出的数据是泛型指定类型,无需更多的向下转型。 缺点: 导致集合中的元素类型缺乏多样性。


f.总结集合的迭代与遍历


//创建集合以及添加元素
		List<String> l1 = new ArrayList <>();
        l1.add(0,"香辣牛肉月饼");
        l1.add(1,"莲蓉月饼");
        l1.add(2,"五仁月饼");

A.迭代器遍历

	//迭代器遍历集合
        Iterator <String> it = l1.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

B.采用List集合的下标

	//下标遍历
        for (int i = 0;i < l1.size();i++){
            System.out.println(l1.get(i));
        }

C.采用增强for进行遍历

	//foreach遍历
        for (String s:l1){
            System.out.println(s);
        }

<4>Map接口:元素无下标,无序不可重复


超级父接口:java.util.Map a.数据存储特点 Map集合以key和value的方式存储数据,即【键值对】方式。 key和value都是引用数据类型,都存对象的内存地址。

Map继承结构图

在这里插入图片描述

c.常用方法


A.向Map集合中添加键值对 Map<K,V> map = new TreeMap<>();

//向Map集合中添加键值对
public V put(K key,V value);

B.通过key值来获取value值

//通过key值来获取value值
public V get(K key);

C.清空Map集合

//清空Map集合
public void clear();

D.判断Map集合中是否包括某个key或者value

//判断Map集合中是否包括某个key或者value
public boolean containsValue(Object value);
public boolean containsKey(Object key);

E.判断Map集合是否为空

//判断Map集合是否为空
public boolean isEmpty();

F.获取Map集合中所有的Key存储至一个Set集合中

//获取Map集合中所有的Key存储至一个Set集合中
public Set<K> keySet();

G.通过key值删除键值对

//通过key值删除键值对
public V remove(Object key);

H.获取Map集合中键值对的个数

//获取Map集合中键值对的个数
public int size();

I.获取Map中所有的Value存储到一个Collection中

//获取Map中所有的Value存储到一个Collection中
public Collection<V> values();

J.将Map集合转换成Set集合

//将Map集合转换成Set集合
public Set<Map.Entry<K,V>> entrySet();

d.Map集合的遍历方法


//创建集合以及添加元素
		TreeMap map = new TreeMap<Integer,String>();
       	map.put(1,"汉堡包");
      	map.put(2,"炸薯条");
       	map.put(3,"香辣鸡翅");

A.获取所有的Key,通过get方法和迭代器遍历

//获取所有的key,再通过get方法得到value值
	   Set<Integer> set = map.keySet();
       Iterator <Integer> it = set.iterator();
       while(it.hasNext()){
           Integer key = it.next();
           System.out.println(key+"="+map.get(key));
       }

B.通过entrySet方法将键值对转换为Set集合,再迭代器遍历

//通过entrySet将map转换为set集合,再迭代器遍历
	   Set<Integer> set = map.entrySet();
       Iterator <Integer> it = set.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }

e.HashMap类详解:

A.哈希表数据结构概述 定义: 哈希表是一个数组和单向链表的结合体,综合了以上二者的优点。 哈希下标: ①.同一个单向链表上的结点哈希值相同,因为其数组下标相同; ②.HashMap集合的key使用时应重写hashCode()equals()方法。 put(K key,V value)方法实现原理:

①.封装key,value到Node对象中; ②.调用key的hashCode()方法得出Hash值。 ③.通过哈希算法将Hash值转换为数组下标。 若下标位置无元素,则将Node添加上去; 若有元素(链表),将key与链表上每个结点key进行equals()比对:        若返回均为false,则该结点被加至末尾;        若返回存在true,则该结点的value值被覆盖。

get(K key)方法实现原理:

①.调用key的hashCode()得出哈希值; ②.通过哈希算法将哈希值转换为数组下标; ③.通过下标锁定位置。 若下标元素无元素,返回一个null; 若有元素(链表),拿参数key与单向链表上每个结点进行equals比对:        若返回均为false,则该方法返回null;        若返回存在true,则返回该结点的value值。

B.HashMap集合详解: 初始容量 HashMap集合的初始化容量为16。 也可通过new HashCode(int capacity)进行初始化容量操作,但该集合容量必须是2的整数次幂。 (即当容量达到【16x0.75=12时】,集合自动扩容值32。) 默认加载因子 定义:当集合存储元素容量达到默认加载因子时,集合自动扩容。 HashMap:该集合的默认加载因子为0.75。 存储元素信息 HashMap集合中的【key】值可以为null,【value】值也可以为null。


f.Hashtable类详解:

A.Hashtable类概述: 初始容量 Hashtable集合的初始容量为11; 默认加载因子及扩容量 Hashtable的默认加载因子为0.75,扩容量为【原容量*2+1】。 存储元素信息 ①.Hashtable集合中的【key】值和【value】值均不可为null。 ②.该集合是线程安全的。

B.properties类详解: 存储数据类型: key和value类型都应是String类型。 常用方法: 向集合中存入数据

//向集合中存入数据
public synchronized Object setProperty(String key,String value);

得到集合中的元素

//得到集合中的元素
public synchronized String getProperty(String key);

g.TreeSet和TreeMap类详解

A.概述: ①.TreeSet集合底层是个TreeMap集合,TreeMap底层是二叉树数据结构。 ②.TreeSet集合中的元素相等于放入了TreeMap集合中的key部分了。 ③.Map集合无序不可重复,TreeMap集合可自动排序。

B.排序规则: ①.TreeSet对自定义的类来说,TreeSet不可排序; ②.若给自定义的类添加【排序规则】,即实现Comparable接口和内部的comparedTo()方法,在其中写入【排序规则】即可。 ③.TreeMap和TreeSet的排序规则采取二叉树中的【中序遍历】。

C.实现排序的两种方法: ①.实现java.util.Comparable接口,通常用于排序规则不会改变; ②.构建集合对象时传入一个比较器对象comparator接口,通常用于排序规则频繁改变。

方法一测试: 准备工作:准备一个学生类Student,让其实现Comparable接口,并重写comparedTo()方法,排序规则如下书写:

//学生作key时,先让其比较学生的学号,如果学号相等,再比较学生姓名
public int compareTo(Student o) {
        if (this.no < o.no){
            return -1;
        }else if (this.no > o.no){
            return 1;
        }else{
            return this.name.compareTo(o.name);
        }
    }

书写测试类:

public static void main(String[] args) {
        Map<Student,Integer> map = new TreeMap <>();
        map.put(new Student("shawn",5),5);
        map.put(new Student("Alice",6),6);
        map.put(new Student("James",7),7);
        map.put(new Student("Jane",8),8);
        map.put(new Student("Alice",7),7);
        System.out.println(map);
    }

测试结果与总结:

{Student{name='shawn', no=5}=5, Student{name='Alice', no=6}=6, Student{name='Alice', no=7}=7, Student{name='James', no=7}=7, Student{name='Jane', no=8}=8}

总结:由上述结果可知,对于学号no较小的被排在上面,而中间学号no相等时,会比较姓名,从而达成了自定义类的排序功能。


方法二测试: 准备工作:准备一个学生类Student,让其实现Comparable接口,并重写comparedTo()方法,排序规则与上同

测试类书写:

public static void main(String[] args) {
        Map<Student,Integer> map = new TreeMap <>(new Comparator <Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if (o1.getNo()<o2.getNo()){
                    return -1;
                }else if (o1.getNo()>o2.getNo()){
                    return 1;
                }else{
                    return o1.getName().compareTo(o2.getName());
                }
            }
        });
        map.put(new Student("shawn",5),5);
        map.put(new Student("Alice",6),6);
        map.put(new Student("James",7),7);
        map.put(new Student("Jane",8),8);
        map.put(new Student("Alice",7),7);
        System.out.println(map);
    }

输出结果: (与上同!)

h.集合的工具类Collections: 常用方法: A.将集合从非线程安全变为线程安全

//将集合从非线程安全变为线程安全
public static List<T> synchronizedList(list l);

B.排序集合中的元素

//排序集合中的元素
public static List<T> sort(List l);

2.IO流(Input/Output Stream)

(1).概述

作用: 通过IO流可完成【硬盘文件的读和写】。 读和写的区分: ·读进入内存为输入【】; ·写出来内存为输出【】。 所在包: java中所有的流均在java.io.*下。 分类:

Ⅰ.java.io.InputStream:字节输入流; Ⅱ.java.io.OutputStream:字节输出流; Ⅲ.java.io.Reader::字符输入流; Ⅳ.java.io.Writer:字符输出流。

实现的接口: ①所有的【输出流】都实现了java.io.Flushable接口,存在flush()方法,作用是【刷新流】。 ②【所有流】都实现了java.io.Closeable接口,存在close()方法,作用是【关闭流】。

(2).文件类流详解:

分类:

Ⅰ.java.io.FileInputStream Ⅱ.java.io.FileOutputStream Ⅲ.java.io.FileReader Ⅳ.java.io.FileWriter

<1>.FileInputStream类详解:


定义:文件字节输出流】,可读取任意格式的文件

构造方法: 源码:

/*
*@param name:文件名
*/
public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

/*
*@param file:读取的文件对象
*/
public FileInputStream(File file);

read()方法和read(byte[] bytes)方法:

/*
*该方法一次可读取目标文件中的一个字节,效率较低。
*@return    若有读取值返回读到的字节本身,无读取值则取-1
*/
public int read();

为了减少硬盘和内存之间的交互,引入read(byte[] bytes)方法

/*
*该方法一次课读取目标文件的【bytes.length】个字节,效率较高。
*@return 	返回读取到的字节数目,无读取值则返回-1
*/
public int read(byte[] b);

使用read(byte[] b)方法时,输出字节本身可以使用String的构造方法

//将byte数组转换为字符串进行输出即可
new String(byte[] bytes,int offset,int readCount);

从而得出读文件的最优解: Tips:【io测试文本】中存储为【“Hello IOStream!”

	//创建流对象:(让fis对象作用域扩展,能在finally语块中读取到该对象)
        FileInputStream fis = null;
        try {
        	//指定要读取的文件
            fis = new FileInputStream("io测试文本.txt");
            int readCount = 0;
            byte[] bytes = new byte[4];
            while((readCount = fis.read(bytes)) != -1){
                System.out.print(new String(bytes,0,readCount));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            //最后必须关闭流:(加入判断为了防止【空指针异常】)
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

输出结果为: 【Hello IOStream!

其他方法: available()

//使用该方法一次读完数据
/*
*@return 返回可读取的字节数量
*/
public int available();

skip()

/*
*@param l:bytes数组需要跳过总字节数
*@return 实际跳过的字节数
*/
public long skip(long l);

<2>.FileOutputStream类详解:

定义:文件字节输出流】。

构造方法:

/*
*@param name:需要写入的文件名
*@param append:写入的数据是否在原有基础上拼接
*/
new FileOutputStream(String name,boolean append);

write(byte[] bytes)方法:

/*
*向原文件中写入数据
*@param bytes 写入的数据的byte数组
*/
public void wirte(byte[] bytes);

※:添加字符串时可调用String中的getByte(String s)方法将字符串转换为byte数组。

写入数据样例:

	//定义输出流
	FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("io测试文本.txt",true);
            String str = "我是中国人,我骄傲";
            //将字符串转换为byte数组
            byte[] bytes = str.getBytes();
            fos.write(bytes);
            //输出流需刷新流:
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                //结尾需要关闭流
                if (fos != null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

<3>.FileReader类简介:

定义:文件字符输入流】,仅能读取【普通文本】。

<4>.FileWriter类简介:

定义:文件字符输出流】,仅能读取【普通文本】。

<5>.文件拷贝功能实现:

源代码:

package IO流;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    public static void main(String[] args) {
        //输入流的定义
        FileInputStream fis = null;
        //输出流的定义
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream("E:\\highlights\\跑跑机器人.mp4");
            fos = new FileOutputStream("E:\\泡泡机器人.mp4");

            //初始化计数变量readCount和数组byte[]
            int readCount = 0;
            byte[] bytes = new byte[1024*1024];//一次性拷贝1M文件

            //一边写一边读
            while((readCount = fis.read(bytes))!= -1){
                fos.write(bytes,0,bytes.length);
            }

            //刷新输出流
            fos.flush();
        } 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();
                }
            }
        }
    }
}

结果:E:/highlights/跑跑机器人.mp4】视频文件成功被拷贝到 【E:/泡泡机器人.mp4】,在此过程中同时进行了重命名操作。

(3).缓冲区类流详解:


缓冲区类流有:

Ⅰ.java.io.BufferedReader Ⅱ.java.io.BufferedWriter Ⅲ.java.io.BufferedInputStream Ⅳ.java.io.BufferedOutputStream

BufferedReader类(BufferedWriter类同理): 定义:自带缓冲区的字符输入流; 构造方法:

new BufferedReader(Reader reader);

方法: readLine()方法:

//一次读取文件的一行内容。
public String readLine();

read()方法:

//一次读取一个字符,返回值为ASCII值。
public int read();

read(char[] chars)方法:

//一次读取chars.length个长度,存入chars数组中,返回值为读到的字符数量
public int read(char[] chars);

转换流InputStreamReader:将字节流转换为字符流:

InputStreamReader I = new InputStreamReader(new FileInputStream("io测试文本"));

(4).标准输出流详解:


<1>.PrintStream

所在包:java.io.PrintStream ①.标准的【字节输出流】,默认输出到控制台。 ②.该流无需手动关闭。 ③.手动改变输出方向:(System类下)

public static void setOut(PrintStream ps);

<2>.PrintWriter

所在包:java.io.PrintWriter ①.标准的【字符输出流】,与上同;

(5).对象类流详解:

概述:serialize,deserialize】 ①.【(反)序列化】:(反)将内存中的java对象放入硬盘文件中。 ②.参与序列化与反序列化的对象,必须实现Serializable接口。 ③.在属性前加【transient】关键字,表现该变量不参与(反)序列化。 ④.IDEA可自动生成序列化版本号。 ⑤.Map集合中的Properties类中的load()方法可将硬盘文件加载到内存中。

public synchronized void load(InputStream inStream);
public synchronized void load(Reader reader);

(6).java.io.File类详解:

概述: File类不能完成对象的读和写,File是一个路径名的抽象表现形式。 构造方法:

//文件的路径名
new File(String pathname);

方法: exists()方法:

//判断是否存在目标文件
public boolean exists()

createNewFile()方法:

//在当前目录下创建一个文件
public void createNewFile();

mkdir()方法:

//在当前目录下创建出一个子目录
public void mkdir();

getParent():

//获取文件父路径
public String getParent();

getAbsolutePath():

//获取文件的绝对路径
public String getAbsolutePath();

listFiles();

//获取当前目录下所有子文件
public File[] listFiles();

(7).拷贝目录程序设计:

/*
*该方法用于目录的拷贝
*@param srcFile:拷贝源文件对象
*@param deskFile:拷贝目标文件对象
*@param src:拷贝源文件地址
*@param desk:拷贝目标文件地址
*/
public static void copy(File srcFile,File deskFile,String src,String desk){
        //列出源目录下的所有子目录:
        File[] files = srcFile.listFiles();
        //遍历files数组拿到所有的子目录与子文件:

            //当拿到的是文件时:
            if (srcFile.isFile()){
                //使用FOS与FIS即可实现拷贝:
                FileInputStream fis = null;
                FileOutputStream fos = null;
                try {
                    fis = new FileInputStream(src);
                    fos = new FileOutputStream(desk);

                    int readCount = 0;
                    byte[] bytes = new byte[1024*1024];//一次传1MB

                    while((readCount = fis.read(bytes))!=-1){
                        fos.write(bytes,0,readCount);
                    }
                    //输出流刷新:
                    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();
                        }
                    }
                }
                return;
            }
            //当拿到目录时:
            for (File file : files){
                if (file.isDirectory()){
                    String srcDir = file.getAbsolutePath();
                    String deskDir = (deskFile.getAbsolutePath().
                            endsWith("\\")?deskFile.getAbsolutePath()
                            :deskFile.getAbsolutePath()+"\\")+srcDir.substring(3);
                    System.out.println(srcDir);
                    System.out.println(deskDir);
                    File newFile = new File(deskDir);
                    if (newFile.exists()){
                        newFile.mkdirs();
                    }
                }
                //  递归调用
            copy(file,deskFile,src,desk);
        }
    }
}

Java——多线程,反射

@[toc]

3.多线程

(1).概述:

进程】:一个应用程序,一个进程可启动多个线程; 【线程】:一个进程中的执行场景/单元。 多线程的作用和特点: 不同线程的栈内存独立,堆和方法区共享,多线程并发可提高程序处理效率。

(2).线程的生命周期:

在这里插入图片描述

(3).多线程的实现方法:

<1>.Thread类详解:

所属包名:java.lang.Thread 常用方法:


setName()方法:

/*
*修改线程的名字
*@param name:线程的新名字
*/
public final synchronized void setName(String name);

getName()方法:

/*
*获取线程的名字
*@return:线程的名字
*/
public final String getName();

interrupt()方法:

/*
*利用异常处理机制终止睡眠
*/
public void interrupt();

currentThread()方法:

/*
*获取当前线程
*return:当前线程
*/
public static native Thread currentThread();

sleep()方法:

/*
*使当前线程进入休眠,出现在哪个线程,使哪个线程进入休眠状态
*@param millis:休眠的毫秒数
*/
public static native void sleep(long millis);

<2>.继承实现多线程:

①编写一个类,使其继承java.lang.Thread类; ②重写run()方法; ③.启动线程:调用对象的【start()方法】(本质是在JVM中开辟新的栈空间)。

<3>.接口实现多线程:

①编写一个类,使其实现java.lang.Runnable接口; ②实现run()方法; ③创建线程对象,传入接口实现类; ④调用线程对象的【start()方法】。

补充对run()方法的理解:run()方法在分支线程中等同于主线程的main()

(4).账户存取款实战:

业务需求:写一个程序,要求使用多线程操控一个账户,同时进行取钱操作。寻找安全隐患问题。

源码展示: Account账户类:

package Thread222;

public class Account {
    private String name;
    private double balance;

    public Account(){}
    public Account(String name,double balance){
        this.name = name;
        this.balance = balance;
    }

    //存钱方法
    public void deposit(double money){
        setBalance(balance+money);
        System.out.println("存钱成功,当前余额:$"+balance);
    }
    //取钱方法
    public void withdraw(double money){
        if (balance < money){
            System.out.println("取钱失败,余额仅剩"+balance);
            return;
        }
        setBalance(balance-money);
        System.out.println("取钱成功,当前余额:$"+balance);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "name='" + name + '\'' +
                ", balance=" + balance +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Account account = (Account) o;
        return Double.compare(account.balance, balance) == 0 &&
                name.equals(account.name);
    }
}

主类以及线程类:

package Thread222;

public class Main {
    public static void main(String[] args) {
        Account act = new Account("QAQ",50000);
        Thread t1 = new Thread(new Thread111(act));
        Thread t2 = new Thread(new Thread222(act));
        Thread t3 = new Thread(new Thread333(act));
		//启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
class Thread111 implements Runnable{
    Account act;
    public Thread111(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

class Thread222 implements Runnable{
    Account act;
    public Thread222(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

class Thread333 implements Runnable{
    Account act;
    public Thread333(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

(5).线程安全:

<1>产生线程安全问题的情况:


通过上述程序可知,每个线程内部的执行顺序不确定,由此会产生线程安全问题。 总而言之,以下情况会产生线程安全问题: Ⅰ.多线程并发程序; Ⅱ.存在共享数据,比如上述的余额; Ⅲ.共享数据有修改的行为,比如上述案例的取钱操作。


<2>.解决方案(重点):线程同步机制

synchronized关键字: 用法①: synchronized(同步共享对象){同步代码块} 同步共享对象处可填:this,Object类,字符串。 案例:

public void withdraw(double withdrawMoney){
    synchronized (this){
      	if (withdrawMoney>=0){
            if (getBalance()<withdrawMoney){
                System.out.println("取钱失败,余额仅剩"+getBalance()+"。");
            }else{
                setBalance(getBalance()-withdrawMoney);
                System.out.println("取钱成功,余额"+selectBalance());
            }
            }else{
                System.out.println("请输入有效的数字!");
            }
        }
    }

用法②: 使用在实例方法上,但这种方法使其共享对象仅能是【this】,且可能无故扩大作用域,效率较低,这种方式叫作添加【对象锁】。例如:

public synchronized void withdraw(double withdrawMoney){
    if (withdrawMoney>=0){
         if (getBalance()<withdrawMoney){
             System.out.println("取钱失败,余额仅剩"+getBalance()+"。");
         }else{
              setBalance(getBalance()-withdrawMoney);
              System.out.println("取钱成功,余额"+selectBalance());
          }
         }else{
             System.out.println("请输入有效的数字!");
         }
    }
}

用法③: 修饰静态方法时叫作【类锁】。 【类锁】:一百个同类型对象一把类锁。 【对象锁】:一个对象一把对象锁。 【死锁】:无法解开的锁叫作死锁,比如两个锁方法互相或者嵌套调用。

高效解决线程安全问题的方法: Ⅰ.使用局部变量(存在栈中)替代成员变量;(推荐) Ⅱ.创建多个对象,使线程和对象一一对应。 Ⅲ.使用【synchronized】关键字。

(6).守护线程(后台线程):

实例:垃圾回收机制; 特点:. ①.守护线程是一个死循环; ②.所有用户线程结束后,守护线程自动结束; ③.将用户线程转换为守护线程的方法(Thread下的方法):

public final void setDaemon(boolean on);

(7).生产者模式与消费者模式:

<1>.Object类下的两个方法介绍:

①wait():

/*
* 让正在对象上活动的线程无限期等待
* 【释放之前占用的对象锁】
*/
public final void wait();

②notify()与notifyAll:

/*
* 唤醒正在对象上等待的线程
* 该方法不会释放锁
*/
public final native void notify();

<2>.业务实战:

业务需求: 巧妙地利用wait()方法与notify()方法对线程进行生产者与消费者的平衡。 源码展示:

package 多线程;

import java.util.ArrayList;
import java.util.List;

/*
* 测试生产者模式与消费者模式:
*       巧妙地利用wait()方法与notify()方法对线程进行
*       生产者与消费者的平衡。
*       wait()方法释放对象锁,让线程进行等待;
*       notify()方法不会释放锁,仅仅告知唤醒被wait的对象。
* */
public class Producer_and_Consumer {
    public static void main(String[] args) {
        //创建仓库对象:
        List list = new ArrayList();
        //创建线程对象:
        Thread t1 = new Thread(new Producer(list));
        Thread t2 = new Thread(new Consumer(list));
        //设置线程名字:
        t1.setName("生产者线程");
        t2.setName("消费者线程");
        //启动线程:
        t1.start();
        t2.start();;
    }
}

//生产者线程:
class Producer implements Runnable{
    // 传入共享对象仓库List集合:
    List list;
    public Producer(List list){
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized(list) {
                if (list.size() > 0) {
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序走到这里代表集合中无元素,需要“生产”:
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName() + "--->" + o);
                list.notify();
            }
        }
    }
}
//消费者线程:
class Consumer implements Runnable{
    //传入共享对象仓库List集合:
    List list;
    //构造方法:
    public Consumer(List list){
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized(list){
                if (list.size() == 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果程序能走到这里,说明集合需要消费:
                Object o = list.remove(0);
                System.out.println(Thread.currentThread().getName()+"--->"+o);
                list.notify();
            }
        }
    }
}

4.反射(java.lang.reflect.*)

(1).定义与作用:

定义: 反射就是把java类中的各种成分映射成一个个的Java对象。 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

作用: 通过反射可以操作字节码文件(class文件)。

(2).反射的常用功能:

<1>.获取某类的字节码:

方法①: 该方法会导致类加载,静态代码块会执行。

//该方法会导致类加载,静态代码块会执行。
Class c = Class.forName("(完整类名带包名)");

方法②:

Class c = (引用).getClass();

方法③:

Class c = (任何类型).class;

上述三种方式中: ①,③两种方式使用类名反射; ②方式使用对象反射;

<2>.通过反射机制实例化对象:

注:这种方式创建对象仅能调用其无参构造。

//注:这种方式创建对象仅能调用其无参构造。
Class c = Class.forName("Student");
Student s = (Student)c.newInstance();

<3>.获取文件的绝对路径:

String path = 
Thread.currentThread().getContextClassLoader().
getResource("全限定名称路径").getPath();

<4>.资源绑定器:

所在包:java.util.ResourceBundle

 public static final ResourceBundle getBundle(String baseName)

实例: 存在属性配置文件db.properties(src下):

id=001
name=guan
age=20
ResourceBundle bundle = ResourceBundle.getBundle("db");
String id = bundle.getString("id");
String name = bundle.getString("name");
String age = bundle.getString("age");

(3).反射应用(java.lang.reflect.*):

<1>.反射属性(~.Field):


常用方法: Class类下:

getFields()方法: 只能拿到public修饰的属性

//拿到反射类对象的以public修饰的属性
public Field[] getFields();

getDeclaredFields():

//拿到反射类对象的所有属性
public Field[] getDeclaredFields();

Field类下:

getName():

//拿到属性的名字
public String getName();

getType():

//拿到属性的类型
public String getType();

set():

//给Object的对象赋值
public void set(Object o,Object value);

get():

//获取该属性的值
public Object get(Object obj);

<2>.反射方法(~.Method):


常用方法: getReturnType():

//获取返回值类型
public Class<> getReturnType();

getModifiers():

//获取修饰符列表
Modifier.toString(method.getModifiers);
//获取返回值类型字符串
String returnType = method.getReturnType().getSimpleName();

getName():

//获取方法名
String name = method.getName();

getParameterTypes():

//获取方法的参数列表
public Class[] getParameterTypes();

//获取方法 getDeclaredMethod(...); //调用方法:

public Object invoke(Object obj,参数列表);

<3>.反射类的父类和接口

//获取父类
public Class getSuperClass();
//获取父接口
public Class[] getInterfaces();

5.注解(annotation)

(1).概述:

①.注解是一种引用数据类型,编译后也生产class文件。 ②.定义注解的方法:[修饰符列表]@interface 注解类型名{ ... }。 ③.注解可修饰属性,方法,变量,枚举,注解等... ④.注解使用时的语法格式:@注解类型名(@override)

(2).两种常见注解:

@override: 作用:编译器检查编译,自动检查方法是否重写父类方法,若没有重写,则报错。 用来注解的注解叫作【元注解】。

@Deprecated: 作用:所标注的元素已过时。

(3).注解的其他用法:

①.注解自定义可定义属性,且当有属性时,必须给属性赋值; 例如:@......(属性名=属性值)。 属性声明时格式为:数据类型+属性名(); 例如:String name() ②.如果注解中属性名为value时并且只有这一个属性时,value可省略。