文章目录
- 2 第二阶段 JAVA深化和提高
-
- 2.1 面向对象深化
- 2.2 数组深化
- 2.3 异常机制 Exception
- 2.4 常用类的基本用法
- 2.5 容器(集合)
- 2.6 IO流技术
-
- 2.6.1 IO流的基本概念
- 2.6.2 流概念细分和体系_四大抽象类
- 2.6.3 文件字节流FileInputStream与FileOutputStream
- 2.6.4 使用字节流实现文件复制
- 2.6.5 文件字符流_FileWriter类与FileReader类
- 2.6.6 缓冲字节流_缓冲字符流
- 2.6.7 转换流
- 2.6.8 打印流
- 2.6.9 数据流
- 2.6.10 对象流
- 2.6.11 序列化和反序列化
- 2.6.12 文件夹的复制
- 2.6.13 字节数组流
- 2.6.14 设计模式_装饰器模式
- 2.6.15 IO体系总结
- 2.6.16 Apache IOUtils的使用_Apache FileUtils的使用
- 2.7 多线程技术
- 2.8 网络编程技术
2 第二阶段 JAVA深化和提高
2.1 面向对象深化
2.1.1 抽象类和抽象方法
2.1.1.1 抽象方法概念
- 抽象类用abstract描述
- 抽象类有构造方法、可以包含成员变量、成员方法、静态方法、final修饰的方法、抽象方法
- final修饰的方法只能被子类调用,不允许被子类重写
- 使用abstract修饰的方法是抽象方法,含有抽象方法的类必须是抽象类
- 抽象方法只定义方法名,不能有方法体,如:
public abstract void hello();
- 抽象类的子类必须实现父类中的抽象方法(也可以只是把方法写出来,但是方法体中可以不写东西)或该子类本身也是一个抽象方法
2.1.1.2 什么情况下使用抽象方法和抽象类?
通过抽象类,可以避免子类设计的随意性。通过抽象类,我们就可以做到严格限制子类的的设计,使子类之间更加通用。(程序的可拓展和可维护性)
- 父类的名称比较抽象,创建的对象无意义,如:
Animal 动物类->设计为抽象类,定义一些抽象方法如sbstract void shout();方法
2.1.2 接口详解
2.1.2.1 接口的概念
- 如果一个类中所有的方法均为abstract方法,那么这个类就可以声明为接口(接口不是类)
- 新建一个接口类型,右键new->Interface
- 接口使用interface而不是class修饰,是一种数据类型,引用数据类型
- 接口中不允许定义构造方法
- 接口可以被另一个接口继承,使用extends关键字
- 同样一个接口中只允许有一个public修饰的接口,其它的为内部接口
2.1.2.2 接口中可以包含什么?
- 抽象方法:
Interface默认的方法就是抽象方法,默认被修饰为public abstract - 非抽象方法(jdk1.8新特性)
//必须使用default修饰
public default void method(){
}
- 属性常量:
接口中的属性默认且只能是public final static 修饰的,且必须赋值
2.1.2.3 接口与类的关系
- 类去实现接口中的实现方法:实现关系,需要使用关键字 implements
public class A implements MyInterface{
}
- 一个类既有继承关系又有实现关系:继承在前,实现在后,写法如:
public class A extends B implements C{
}
- 一个类,它的父类中有和所实现的接口完全相同的方法,他实现的方法是父类来的
- 一个类,他的父类和实现接口中有同名的方法,传递的参数不相同,那么对这个类要求他对父类和所实现的接口实现方法的重载/重写(在这个子类里写)
2.1.3 接口的特征_使用接口的意义
2.1.3.1 接口的传递性
如果接口B继承了接口A,而接口中有一个抽象方法method();那么B的一个实现将拥有且必须实现这个抽闲方法method()
public interface InterfaceA{
public abstract void method();
}
interface InterfaceB extends InterfaceA{
}
class Iml implements InterfaceB{
@Override
public abstract void method{
//TODO balabalabala
}
}
2.1.3.2 接口的继承性(多继承)
一个类可以继承多个接口,格式如下
class ClassExample implements InterfaceA,InterfaceB{ //多个接口之间使用逗号分开
}
2.1.3.3 使用接口的意义_接口实现多态的步骤
- 意义:可以实现设计与实现的分离,抽象出N多不同类的共同点
举例:飞机,鸟,炮弹,宇宙飞船这几种物体
继承:is-a 关系,三角形is a 几何图形(实例)
接口:has-a 关系 鸟has a飞行功能,飞机has a 飞行功能,我们就可以用接口的方式来定义“飞”这种能力,让其他类去实现
//接口体现的是一种能力
- 接口实现多态的步骤
1.编写接口
2.实现类实现接口中的方法(实现方法不加abstract)
3.接口(类型)new 实现类对象
这就是面向接口的编程
public interface Fly{
void flying();
}
class Bird implements Fly{
@override
public void flying{
System.out.println("小鸟在飞");
}
}
class Plane implements Fly{
@override
public void flying{
System.out.println("飞机在飞");
}
}
class Dog implements Fly{
@override
public void flying{
System.out.println("小狗被踢飞");
}
}
//以下代码在一个测试类中
public static void main(String args[]){
Fly bird = new Bird;
bird.flying();
Fly puppy =new Dog;
puppy.flying();
Fly airPlane =new Plane();
airPlane.flying();
}
2.1.4 内部类
2.1.4.1 内部类的概念
- 内部类是写在一个外部类中的类,他们的关系是嵌套关系
2.1.4.2 内部类的特点
- 内部类可以直接访问外部类的成员,反之则不行
- 内部类作为外部类的成员,可声明为private、default、protect和public
- 内部类定义在外部类中不可访问的属性,这样就在外部类中实现了此外部类的private还要下的访问权限
- 脱离了外部类,无法访问内部类
2.1.4.3 内部类的优缺点
- 优点:内部类可以直接访问外部类的私有属性
- 缺点:破坏了类的结构
2.1.4.4 内部类的访问
- 通过实例化一个外部类的对象,再实例化一个内部类的对象,来访问内部类的属性。
public class Face{
private String shape="瓜子脸";
public class Nose{
private String shape="高鼻梁";
public void breath(){
System.out.println("鼻子在呼吸");
}
}
//测试类
public void static main(String args[]){
Face f = new Face();
Nose n = f.new Nose();
n.breath();
}
}
- 静态内部类的访问使用如下格式访问
外部类名.内部类名 //对!就是这样的
当要实例一个静态内部类的对象时:
外部类名.内部类名 实例名 = new 外部内名.内部类名(); //就是这样子
2.1.4.5 内部类访问外部类
- 当内部类拥有和外部类一样的成员变量时,在内部类中访问外部类的变量需要按这样的格式访问
外部类名.this.成员变量
- 当内部类被static修饰时,内部类不能访问没有带static修饰的外部类的属性
- 如果在内部类中写了一个静态方法,那么这个内部类必须被修饰为static的
2.1.4.6 定义在方法中的内部类
定义在方法中的内部能,只能在方法中去使用,如下
public void Hello(){
int a=10;
class Inner{
public void kk(){
System.out.println("a="+a);
}
}
new Inner().kk();//只能在方法体中调用方法中的内部类,对于方法外,这个内部类毫无意义
}
2.1.4.7 匿名内部类(没有名字的内部类)
- 存在的前提是要继承或实现一个接口(最多一个接口。比较常见的是在图形界面编程中使用)
//以匿名继承内部类举例
public abstract class Father{
public abstract void dream() ;
}
class Test{
public static void main(String[] args){
Father f = new Father(){ //这个部分就是匿名内部类
@Override
public void dream() {
System.out.println("欧耶实现了父亲的梦想!");
}
};
f.dream();//调用内部内的方法
}
}
2.1.5 String类常用方法_API文档阅读
2.1.5.1 String 类的定义
- String类相当于char类的数组,数组长度一旦创建不可更改,value的数组还是用了final修饰
String str = "abc";
str = "def"; //在这里字符串并没有被改变,改变的只是指向它的地址
//在常量池中,有两个字符串常量"abc" 与"def",刚开始时前者的地址指向str,执行第二句后,变成了后者的地址指向str
2.1.5.2 String类的常用方法
- char charAt(int index) 返回字符串中第index个字符
//使用方法
字符串对象.charAt(<index>);
- boolean equalsIgnoreCase(String other)如果字符串与other相等(忽略大小写)则返回true
//使用方法
字符串对象.equalsIgnoreCase(<这里输出要比较的字符串,不区分大小写>) // 返回布尔型
- int indexOf(String str) 以及 lastIndexOf() 查找指字字符串出现的位置(找不到返回-1)
//不带参数的使用方法
字符串对象.indexOf(<需要找的字符串或字符>) //返回该字符或字符串在字符串中的位置(正向查找,从0开始)int型
字符串对象.lastIndexOf(<需要找的字符串或字符>) //正向查找最后一个符合要求的字符或字符串
//带参数的使用方法
字符串对象.indexOf(<需要查找的字符串或字符><,index>) //从index位置正向查找最近的一个符合要求的结果,返回位置
字符串对象.lastIndexOf(<需要查找的字符串或字符><,index>) //从index位置方向反向查找最近的符合要求的值,返回位置
- int length()返回字符串的长度
字符串对象.length() //括号里无需添加参数
- String replace(char oldChar,char newChar) 获得并返回一个新的替换后的字符串
字符串对象.replace()
//获得并返回一个替换后的新字符串,但并没有改变原字符串(因为字符串只能只能共享,不能改变)
- boolean startsWith(String prefix) 如果字符串以prefix开始,则返回true
- boolean endsWith(String prefix) 如果字符串以prefix结尾,则返回true
- string substring 以及String substring(int beginIndex,int endIndex)返回一个新字符串,该字符串包含从beginIndex开始到endIndex结尾的所有字符(包含beginIndex但不包含endIndex),如果不写endIndex则从beginIndex一直截取到字符串的结尾(包含最后一个)
- String toLowerCase() 返回一个新字符串,该串将原始字符串中的所有大写字母改成小写字母
- String toUpperCase() 返回一个新字符串,该字符串将原始字符串中的所有小写字母改写成大写字母
- String trim() 返回一个新字符串,该串删除了原始字符串头部和尾部的空格(如果中间有空格,中间的空格不会被删掉)
2.1.5.3 API文档
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
- JDK1.8的API文档 https://docs.oracle.com/javase/8/docs/api/
2.1.6 String类与常量池
2.1.6.1 String类的构造方法
- String类的构造方法一览
//无法上传,自定查看eclipse源码
- string类的构造方法举例
char [] c={'a','b','c'}
String str =new String(c);
2.1.6.2 通过String类学习内存空间的运作
String s1 ="abc";
String s2="a"+"b"+"c";
String s3=new String("abc");
String s4 =s3+"";
String s5= new Stirng("abc");
System.out.println(“s1==s2:”+(s1==s2)); //结果true 因为字符串已经在堆中常量池加载了,不需要开辟空间,所以s1和s2都指向常量池“abc”的地址
System.out.println(“s1==s3:”+(s1==s23); //结果false 因为使用了new,在堆中开辟了空间,s3指向的是堆中的空间的地址
System.out.println(“s1==s4:”+(s1==s4)); //结果false 变量运算时也要在堆中开辟一个空间,在此空间中运算,此空间地址指向s4
System.out.println(“s1==s5:”+(s1==s5)); //结果false 同样是因为使用了new开辟了一个空间
2.1.7 字符串相等的判断
2.1.7.1
- 使用"=="判断两个String对象的地址是否相同
- 使用String 类自带的方法equals方法判断(判断的是字符串内容是否相等)
//equals原理:比较两个对象地址是否相同,比较两个字符串是否相同
public boolean equals(Object anObject) { //传入比较的对象
if (this == anObject) { //如果当前对象和比较对象地址相等,返回true
return true;
}
if (anObject instanceof String) { //如果传入的对象不是String的一个实例,返回false,否则往下再判断
String anotherString = (String)anObject; //传入对象向下转型为String
int n = value.length;
if (n == anotherString.value.length) { //如果两个字符串的长度不相等,返回false,否则往下判断
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false; //如果有一个字符不相等,则返回false
i++;
}
return true; //字符全部相等则返回true
}
}
return false;
}
2.2 数组深化
2.2.1 多维数组
使用二维数组来讲解,二维数组就是用来存储一维数组的数组
2.2.1.1 二维数组的格式
//这里用int型举例
int[][] arrA = new int[index][] //其中index为二位数组的长度
2.2.1.2 二维数组的初始化
//静态初始化
int [][] arryA = {
{1,2},{3,4,8},{5,6,7}} //二维数组是不规则的矩阵,它包含的各一维数组可以不相同
//声明了一个二维数组,用于存储3个一维数组,每个一维数组的长度未定
int[][] arrA = new int[3][]
arrA[0]=new int[3];//声明arrA[0]的长度为3,初始化为0(详见各种数据类型的默认初始化值)
arrA[1]=new int[]{1,2,3};//直接给定该一维数组的元素
arrA[2]=new int[3];
//声明一个二维数组,且给出所有一位数组的长度
int[][] arryB = new int[3][3];
2.2.1.3 二维数组中的元素,实际上是存储一维数组的地址
2.2.1.4 二维数组的遍历
- 加强for循环
for(<数据类型> <迭代变量> : 数组名){
System.out.println(<迭代变量>);
}
- 加强for循环遍历二维数组
for(int[] arr : arrA){
for(int a : arr){ //arr和上一行的arr是同一个数组(一维数组),a是一个整型迭代变量
System.out.println(a);
}
}
2.2.2 Object数组存储表格数据
数组除了可以存储基本数据类型,还可以存储引用数据类型。
这里举个例子,我们使用一个数组来存储一组对象
//在Person类中
public class Person {
private String name;
private String gender;
private int age;
public Person(String name, String gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
public Person() {
super();
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"\t"+gender+"\t"+age;
}
}
//在Test测试类中
public class Test {
public static void main(String[] args) {
Person p[] =new Person[3];
p[0]=new Person("张三", "男", 32);
p[1]=new Person("李四","男",21);
p[2]=new Person("王二麻子","女",23);
for (int i = 0; i < p.length; i++) {
System.out.println(p[i]);
}
}
}
2.2.3 数组的拷贝
2.2.3.1 地址的拷贝(即引用的拷贝)
数组A=数组B;//将数组B对应的内存空间的地址指向数组A,执行这一句后,数组A与数组B的地址相同,它们完全相等
2.2.3.2 值的拷贝:这里使用System.arrcopy()方法
System.arraycopy(<数组a>,index1,<数组b>,index2,index3) //其中index1表示从数组a的index1位置开始复制,复制到数组b从index2开始的位置,复制index3个
2.2.4 java.util.Arrays 工具类的使用
2.2.4.1 java.util.Arrays常用工具介绍
- toString方法:打印数组元素,只可返回数组中的元素(是返回,不是输出),不能操作。此方法非Object中的toString方法
Arrays.toString(数组)
- equals(…)方法:比较两个数组是否相同
Arrays.equals(数组a,数组b) //比较两个数组以相同的顺序包含相同的元素
- copyOf(…)方法: 复制指定的数组
//举例:将数组a的元素赋值给b,内存中奖开辟新的空间,将地址指向b
int[] a = {1,2,3,4,5};
int[] b=new int[6];
b=Arrays.copyOf(a, 8); //这里的8表示新数组的长度为8,a数组填不满的用0来填
- fill()填充:将某个数组的元素填充为某个值
int [] arrA =new int [4];
Arrays.fill(arrA,9) // 表示arrA中的每个元素都填成9
- sort()数组升序排序(排序的对象需要具有比较大小的能力)
Arrays.sort(arrA)将数组arrA进行升序排序(注意是升序排序,不是排序后输出)
2.2.5 冒泡排序的基础算法
一个一维数组有N个元素,进行N-1轮比较,每轮使一个元素和下一个相邻的元素进行比较,将大的数交换到右边
public class Test {
public static void main(String[] args) {
int[] a = {11,52,23,4,56};
System.out.println(Arrays.toString(a));
for (int i = 0; i < a.length-1; i++) { //4轮比较
for (int j = 0; j < a.length-1; j++) { //每轮比较4次
int temp; //为了优化算法,temp最好定义在循环外
if (a[j]>a[j+1]) {
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
2.2.6 冒泡排序法的优化算法
通过观察,我们发现没比较一轮,就可以少比较一个数,所以有一下优化算法
public class Test {
public static void main(String[] args) {
int[] a = {11,52,23,4,56};
int temp;
System.out.println(Arrays.toString(a));
for (int i = 0; i < a.length-1; i++) { //4轮比较
for (int j = 0; j < a.length-1-i; j++) { //每轮少比较i次
temp;
if (a[j]>a[j+1]) {
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
2.2.7 二分法(折半查找法)
折半查找法只适用于已经正序升序的数组,通过不断取数组中间值来与需要查找的元素进行比较,成功则返回该元素的值,失败则返回负的当前最低值-1
此方法相当于arrays.binarySearch()方法
package 测试专用;
import java.util.Arrays;
import 测试专用.Face.Nose;
public class Test {
public static void main(String[] args) {
int[] a = {11,22,33,44,55,66};
int high=a.length-1;
int low =0;
int va=66;//需要查找的值
boolean flag =false;
while(low<=high) {
int mid=(low+high)/2;
if (a[mid]>va) {
high=mid-1;
}else if (a[mid]<va) {
low=mid+1;
}else {
System.out.println(mid);
flag=true;
break;
}
}
if (!flag) {
System.out.println("这个值不在此数组中");
}
}
}
2.3 异常机制 Exception
2.3.1 异常概念_分类
2.3.1.1 异常的概念及出现的情况
异常(Exception)就是在程序运行过程中所发生的不正常的事件,它会中断正在运行中的程序。
- 所需文件找不到
- 网络连接不通或中断
- 算数运算错
- 数组下标越界
- 装载一个不错在的类或者对null对象操作
- 类型转换异常等等
当java程序出现以上异常时,就会在所处的方法中产生一个异常对象,这个异常对象包括异常的类型,异常出现时程序的运行状态以及对该异常的详细描述。
2.3.1.2 Error 错误
Error相当于人类当中的癌症,仅靠程序本身是无法恢复的严重错误,它与异常同属一个父类Throwable
2.3.1.3 Exception 异常的分类
异常是由java应用程序抛出和处理的非严重错误,异常相当于人类中的小病症
- Checkedy异常:该类异常必须得到处理否则无法运行的异常
例如:SQLException、ClassNotFoundException等等
- RuntimeException异常:运行时异常,不要求程序必须做出处理
例如:ArithmeticException、NullPointerException、NumberFormatException等等
2.3.2 捕获异常
通过捕获异常的方式,来处理相对应的异常,让程序能够继续执行下去。
2.3.2.1 try-catch 组合
try {
需要捕获的代码体
} catch (Exception e) { //括号内为捕获的异常类型,Exception为所有异常类的父类,这里是父类引用指向子类对象,也可以写具体的异常在里面如 InputMismatchException
System.err.println("成功捕获后相对应的执行代码区"); //在异常中使用err.println输出,文字为红色
}
2.3.2.2 try-finally 组合
finally下的代码块内的代码无论如何都会被执行,即便没有报异常
try{
需要捕获的代码块
}finally{
无论如何都会执行的代码块,即使没有报异常
}
2.3.2.3 try-catch-final 组合
包含了catch和finally的功能,此种组合catch与finally位置不能互换
try{
需要捕获异常的代码块
}catch{
catch代码块
}finally{
finally代码块
}
注:如需要try下的代码块中有return,先执行finally下的代码块再回来执行return
2.3.3 声明异常
2.3.3.1 声明异常的关键字throws
该关键字用在方法名的后面,用于声明该方法可能会产生一个异常,如果方法声明的是Exception类型的异常或者是Checked Exception异常,要求方法的调用出必须做处理,RuntimeException类型的异常无预处理。处理方式有以下两种:
- 继续使用throws向上(方法的调用处)声明,例如:
public static void method() throws <异常类名>{ //异常类名笼统的可以写Exception,细致的可以写具体哪个异常类,但只有Exception类型与Checked类型的异常需要做处理
}
public static void main(String[] args) <相同的异常类名>{
method(); //当这里调用method方法时main方法名后就要添上相同的异常类名,否则使用下一种方法解决
}
- 使用try-catch-finallly进行处理
如果不使用thows向上声明,则需要添加try-catch方法
public static void method(){
}
public static void main(String[] args){
try{
method(); //需要捕获异常的方法
}catch(<异常类名> e){
}
}
2.3.3.2 继承关系中的异常声明
- 父类方法声明了Exception类型的异常,子类在重写方法时,可以声明也可以不声明。但是如果子类重写后的方法使用super关键字调用父类的方法,那么要求必须对异常进行处理。
public class Father {
public void method() throws Exception { //父类的method方法声明了一个异常类型Exception
}
}
class Son extends Father{
public void method() <此处可声明可不声明异常> {
super.method(); //但这里使用super调用了父类的方法,则一定要进行处理
}
public static void main(String[] args) {
}
}
- 如果父类的方法没有声明异常,而子类重写的方法一定会有Exception或Checked异常,那么要求子类必须使用try-catch来处理,或者将父类和子类方法都加上异常声明
- 如果子类重写父类方法时会产生一个RuntimeException类型的异常,则可处理也可不处理
2.3.3.3 throw 抛出异常对象
在捕获一个异常之前,必须有一段代码先生成异常对象并把它抛出,这个过程我们以手工做,也可以由JRE来实现,但是他们调用的都是thr