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 [-128
127]即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可省略。