Java 面向对象知识点拆分(二)
Java 面向对象的编程思想是极其重要的,可以说是 Java 的核心,这块内容比较细,比较多,花了两周时间,整理出来 47 条知识点,帮助大家稳固基础,早日实现年薪百万,哈哈~
下面是后半部分,前一部分请https://xie.infoq.cn/article/9044cb22a1af5d69687ee5a58
20、异常
定义:是对问题的描述,将问题进行对象的封装。指程序在运行时出现不正常情况。
由来:问题也是现实生活中一个具体的事物,也可以通过 Java 的类的形式进行描述。并封装成对象。其实就是 Java 对不正常情况进行描述后的对象体现。
对于问题的划分:严重的问题、非严重的问题。
对于严重的,Java 通过 Error 类进行描述;对于 Error 一般不编写针对性的代码对其进行处理。对于非严重的,Java 通过 Exception 类进行描述。对于 Exception 可以使用针对性的处理方式进行处理。
无论 Error 或者 Exception 都具备一些共性内容。比如:不正常情况的信息,引发原因等。
对捕获到的异常对象进行常见方法操作:
String getMessage():获取异常信息。
在函数上声明异常,便于提高安全性,让调用出进行处理。不处理编译失败。
对多异常的处理:
①声明异常时,建议声明更为具体的异常,这样处理的可以更具体。②对方声明几个异常,就对应有几个 catch 块,不要定义多余的 catch 块。如果多个 catch 块中的异常出现继承关系,父类异常 catch 块放在最下面。
建立在进行 catch 处理时,catch 中一定要定义具体处理方式。不要简单的就书写一条输出语句。
当在函数内部出现了 throw 抛出异常对象,那么就必须要给对应的处理动作。要么在内部 try catch 处理。要么在函数上声明让调用者处理。一般情况下,函数内出现异常,函数上需要声明。
如何定义异常信息?
因为父类中已经把异常信息的操作都完成了。所以子类只要在构造时,将异常信息传递给父类通过 super 语句。那么就可以直接通过 getMessage 方法获取自定义的异常信息。
自定义异常:
必须是自定义类继承 Exception。自定义异常时,如果该异常的发生,无法再继续进行运算,就让自定义异常继承 RuntimeException。
继承 Exception 原因:
异常体系有一个特点:因为异常类和异常对象都被抛出。他们都具备可抛性。这个可抛性是 Throwable 这个体系中独有特点。
只有这个体系中的类和对象才可以被 throws 和 throw 操作。
当要定义自定义异常的信息时,可以使用父类已经定义好的功能。将异常信息传递给父类的构造函数。
自定义异常:按照 java 的面向对象思想,将程序中出现的特有问题进行封装。
throw 和 throws 的区别:
throws 使用在函数上。后面跟的异常类。可以跟多个;用逗号隔开。
throw 使用在函数内。后跟的是异常对象。
注意:
Exception 中有一个特殊的子类异常 RuntimeException(运行时异常)。如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。之所以不用在函数上声明,是因为不需要让调用者处理。当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,让程序员对代码进行修正。
对于异常分两种:
①编译时被检测的异常:
该异常在编译时,如果没有处理(没有抛也没有 try),编译失败。该异常被标识,代表这可以被处理。
②编译时不被检测的异常(运行时异常。RuntimeException 以及其子类)
在编译时,不需要处理,编译器不检查。该异常的发生,建议不处理,让程序停止,需要对代码进行修正。
finally 代码块:
①定义一定执行的代码。通常用于关闭资源,因为资源必须释放。
②finally 只有一种情况不会执行。当执行到 System.exit(0);finally 不会执行。
try_catch_finally 格式:
①try{}catch(){}
②try{需要被检测的代码}catch(){处理异常的代码}finally{一定会被执行的代码}
③try{}finally{}
catch 是用于处理异常。如果没有 catch 就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。
异常在子类覆盖中的体现:
①子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的父类的异常或者该异常的子类。②如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。③如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时也不可以抛出异常。如果子类方法发生了异常,就必须要进行 try 处理,绝对不能抛。
异常体系的特点:
异常体系中的所有类以及建立的对象都具备可抛性。也就是说可以被 throw 和 throws 关键字所操作。只有异常体系具备这个特点。
当函数内容有 throw 抛出异常对象,并未进行 try 处理。必须要在函数上声明,否则编译失败。注意,RuntimeExceptin 除外,也就说,函数内如果抛出的 RuntimeException 异常,函数上可以不用声明。
如果函数声明了异常,调用者需要进行处理。处理方法可以 throws 可以 try。
异常的好处:
①将问题进行封装;
②将正常流程代码和问题处理代码相分离,方便于阅读。
异常的处理原则:
①处理方式有两种:try 或者 throws;
②调用到抛出异常的功能时,抛出几个,就处理几个。一个 try 对应多个 catch。
③多个 catch,父类的 catch 放到最下面;
④catch 内,需要定义针对性的处理方式,不需要简单的定义 printStackTrace,输出语句。也不要不写。当捕获到的异常,本功能处理不了时,可以继续在 catch 中抛出。
如果该异常处理不了,但并不属于该功能出现的异常。可以将异常转换后,再抛出和该功能相关的异常。
或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道,并处理。也可以将捕获异常处理后,转换新的异常。
比如:汇款的例子。
异常的注意事项:
在子父类覆盖时:
①子类抛出的异常必须是父类的异常的子类或者子集。
②如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能 try 不能抛。
参阅例子:
老师用电脑上课、图形面积。
21、包—package
总结:
包与包之间进行访问,被访问的包中的类以及类中的成员,需要 public 修饰。 不同包中的子类还可以直接访问访问父类中被 protected 权限修饰的成员。
包与包之间可以使用的权限只有两种,public protected。
权限修饰符:
22、线程
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程定义:就是进程中的一个独立的控制单位。线程在控制着进程的执行。一个进程中至少有一个线程。(Thread:一个程序里面不同执行路径)。
扩展知识:
其实更细节说明 jvm,jvm 启动不止一个线程,还有负责垃圾回收机制的线程。
创建线程的方式:
第一种方式:继承 Thread 类
步骤:
①定义类继承 Thread;
②复写 Thread 类中的 run 方法;
目的:将自定义代码存储在 run 方法。让线程运行。
③调用线程的 start 方法;该方法有两个作用:启动线程,调用 run 方法。
为什么要覆盖 run 方法呢?
Thread 类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是 run 方法。也就是说 Thread 类中的 run 方法,用于存储线程要运行的代码。
第二种方式:实现 Runnable 接口
步骤:
①定义类实现 Runnable 接口;
②覆盖 Runnable 接口中的 run 方法;
将线程要运行的代码放在该 run 方法中。
③通过 Thread 类建立线程对象;
④将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。
因为,自定义的 run 方法所属的对象是 Runnable 接口的子类对象,所以要让线程去执行指定对象的 run 方法。
⑤调用 Thread 类的 start 方法开启线程并调用 Runnable 接口子类的 run 方法。
实现方式和继承方式有什么区别呢?
实现方式好处:
避免了单继承的局限性。在定义线程时,建议使用实现方式。
两种方式区别:
继承 Thread:线程代码存放 Thread 子类 run 方法中。
实现 Runnable:线程代码存在接口的子类的 run 方法中。
多线程特性:
随机性:谁抢到谁执行,至于执行多长,cpu 决定。
static Thread currentThread():获取当前线程对象。
getName():获取线程名称。
设置线程名称:setName 或者构造函数。
Java 对于多线程的安全问题提供了专业的解决方式——同步代码块。
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取 cpu 的执行权,也进不去,因为没有锁。
同步的前提:
①必须要有两个或者两个以上的线程;
②必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
同步的好处:
解决了多线程的安全问题。
同步的弊端:
多个线程需要判断锁,较为消耗资源。
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是 this。所以同步函数使用的锁是 this。
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是 this。因为静态方法中也不可以定义 this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class。该对象的类型是 Class。
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class。
线程间通讯:
其实就是多个线程在操作同一个资源,但是操作的动作不同。
wait();notify();notifyAll()都是用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义在 Object 类中呢?
因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁。只有同一个锁上的被等待线程,可以被同一个锁上 notify()唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在 Object 类中。
如何停止线程?
只有一种方式,run 方法结束。
分析:
开启多线程运行,运行代码通常是循环结构。只要控制住循环就可以让 run 方法结束,也就是线程结束。
特殊情况:
当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread 类提供该方法 interrupt();
join()方法:
当 A 线程执行到了 B 线程的.join()方法时,A 就会等待。等 B 线程都执行完,A 才会执行。
join 可以用来临时加入线程执行。
23、String
s1 是一个类类型变量,"abc"是一个对象。字符串最大特点:一旦被初始化就不可以被改变。
s1 和 s2 有什么区别?
s1 在内存中有一个对象。s2 在内存中有两个对象。
常见的操作:
获取:
1.1 字符串中的包含的字符数,也就是字符串的长度。
int length():获取长度。
1.2 根据位置获取位置上某个字符。
char charAt(int index)
1.3 根据字符获取该字符在字符串中位置。
int indexOf(int ch):返回的是 ch 在字符串中第一次出现的位置。
int indexOf(int ch, int fromIndex):从 fromIndex 指定位置开始,获取 ch 在字符串中出现的位置。
int indexOf(String str):返回的是 str 在字符中第一次出现的位置。
int lastIndexOf(String str, int fromIndex):从 fromIndex 指定位置开始,获取 str 在字符串中出现的位置。
判断:
2.1 字符串中是否包含某一个子串。
boolean contains(str)
特殊之处:indexOf(str):可以索引 str 第一次出现位置,如果饭回-1,标识该 str 不在字符串中存在。所以,也可以用于对指定判断是否包含。if(str.indexOf("aa")!=-1)而且该方法既可以判断,又可以获取出现的位置。
2.2 字符中是否有内容。
boolean isEmpty():原理就是判断长度是否为 0.
2.3 字符串是否是以指定内容开头。
boolean starWith(str);
2.4 字符串是否是以指定内容结尾。
boolean endsWith(str);
2.5 判断字符串的内容是否相同。复写了 Object 类中的 equals 方法。
boolean equalsIgnoreCase();
转换:
3.1 将字符数组转换成字符串。
构造函数:String(char[])
String(char[], offset, count):将字符数组中的一部分转成字符串。
静态方法:
static String copyValueOf(char[]);
static String copyValueOf(char[] data, int offset, int count)
static String valueOf(char[]):
3.2 将字符串转成字符数组。
char[] toCharArray();
3.3 将字节数组转成字符串。
String(byte[])
String(byte[], offset, count):将字节数组中的一部分转换成字符串。
3.4 将字符串转成字节数组。
byte[] getBytes();
3.5 将基本数据类型转成字符串。
static String valueOf(int)
static String valueOf(double)
特殊:字符串和字节数组在转换过程中,是可以指定编码表的。
替换:
String replace(oldchar, newchar);
切割:
String[] split(regex);
子串:
获取字符串中的一部分。
String substring(begin);从指定位置开始到结尾。如果角标不存在,会出现角标越界异常。
Striing substring(begin,end);包含头,不包含尾。
转换,去除空格,比较
7.1 将字符串转成大写或者小写。
String toUpperCase();
String toLowerCase();
7.2 将字符串两端的多个空格去除。
String trim();
7.3 对两个字符串进行自然顺序的比较。
int compareTo(string);
24、StringBuffer
是字符串缓冲区。是一个容器,而且长度是可以变化的。
常见操作:
存储:
StringBuffer append()
:将指定数据作为参数添加到已有数据结尾处。
StringBuffer insert(index,数据)
:可以将数据插入到指定 index 位置。
删除:
StringBuffer delete(start,end)
:删除缓冲区中的数据,包含 start,不包含 end。
StringBuffer deleteCharAt(index)
:删除指定位置的字符。
获取:
char charAt(int index)
:返回此序列中指定索引处的 char 值
int indexOf(String str)
:取出给定字符串的索引
int lastIndexOf(String str)
: 返回最右边出现的指定子字符串在此字符串中的索引。
int length()
:字符串的长度
String substring(int start, int end)
:返回一个新的 String,它包含此序列当前所包含的字符子序列。
修改:
StringBuffer replace(start,end,string)
:使用给定 String 中的字符替换此序列的子字符串中的字符。
void setCharAt(int index, char ch)
:将给定索引处的字符设置为 ch。
反转:
StringBuffer reverse()
:将此字符序列用其反转形式取代。
将缓冲区中指定数据存储到指定字符数组中。
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
:将字符从此序列复制到目标字符数组 dst。
JDK1.5 版本之后出现了 StringBuilder.
StringBuffer 是线程同步。
StringBuilder 是线程不同步。
开发中建议使用 StringBuilder
升级三个因素:
①提高效率②简化书写③提高安全性。
基本数据类型对象包装类
byte——Byte
short——Short
int——Integer
long——Long
float——Float
double——Double
char——Character
boolean——Boolean
基本数据类型对象包装类的最常见作用:用于基本数据类型和字符串类型之间的转换。
基本数据类型转换成字符串。
基本数据类型+"";
基本数据类型.toString(基本数据类型值);
如:Integer.toString(34);//将整数 34 转换成"34".
字符串转换成基本数据类型。
十进制转成其他进制。
toBinaryString();
toHexString();
toOctalString();
其他进制转十进制。
parseInt(string,radix)
使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
JDK1.5 以后出现的新特性:
①自动装箱
如:Integer x = new Integer(5);自动装箱后:Integer x = 5;//5 相当于 new Integer(5)。
x = x + 2;//首先对 x 进行了自动拆箱,转成 int 后参与运算,再自动装箱赋给 x。
②Integer m = 128;Integer n = 128;
25、集合框架
Collection 接口
--List 接口
--Set 接口
List:
凡是可以操作角标的方法都是该体系特有的方法。
List 集合特有的迭代器——ListIterator,是 Iterator 的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,会发生 ConcurrentModificationException 异常,属于线程安全问题。所以,在迭代时,只能用迭代器的方法操作元素,可是 Iterator 方法是有限的,只能对元素进行判断、取出、删除的操作,如果想要其他的操作如:添加,修改等,就需要使用其子接口——ListIterator.该接口只能通过 List 集合的 ListIterator 方法获取。
List
--ArrayList:底层的数据结构使用数组结构。
--LinkedList:底层使用链表数据结构。
--Vector:底层是数组数据结构。线程同步。已被 ArrayList 替代。
ArrayList 特点:查询速度快。但是增删稍慢。线程不同步。
LinkedList 特点:增删速度很快,查询稍慢。
枚举就是 Vector 特有的取出方式。其实枚举和迭代器是一样的。因为枚举的名称以及方法的名称都过长,所以被迭代器取代了。
LinkedList 特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现 NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现 NoSuchElementException
JDK1.6 出现了替代方法:
offerFirst();
offerLast();
添加元素
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回 null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回 null。
Set:
元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。其功能和 Collection 是一致的。Set 的底层使用的是 Map 集合。
Set
--HashSet:底层数据结构是哈希表。
--TreeSet:可以对 Set 集合中的元素进行排序。底层数据结构是二叉树。
HashSet 是如何保证元素唯一性的?
是通过元素的两个方法,hashCode 和 equals 来完成。如果元素的 HashCode 值相同,才会判断 equals 是否为 true。如果元素的 hashCode 值不同,不会调用 equals()。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的 hashCode 和 equals 方法。
TreeSet 保证元素唯一性的依据:
compareTo()方法 return 0.
TreeSet 排序的第一种方式:
让元素自身具备比较性。元素需要实现 Comparable 接口,覆盖 compareTo()方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet 排序的第二种方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。
26、泛型
JDK1.5 版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。
基本术语:以 ArrayList 为例:<>念着 typeof
ArrayList 中的 E 称为类型参数变量。
ArrayList 中的 Integer 称为实际类型参数。
整个称为 ArrayList 泛型类型。
整个 ArrayList 称为参数化的类型 ParameterizedType。
好处:
①将运行时期出现的问题 ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行事情问题减少,安全。②避免了强制类型转换的麻烦。
格式:
通过<>来定义要操作的引用数据类型。
在使用 java 提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。其实<>就是用来接受类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,早期定义 Object 类完成扩展。现在定义泛型来完成扩展。
泛型定义在方法上:
泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。
静态方法不能使用类定义的泛型。而应单独定义泛型。
泛型的高级应用:
? 通配符。也可以理解为占位符。
泛型的限定:
? extends E:可以接受 E 类型或者 E 的子类型。上限。
? super E:可以接受 E 类型或者 E 的父类型。下限。
注意事项:
使用泛型时,泛型类型须为引用类型,不能是基本数据类型。
两边都有泛型限定时,须一致。
一边有泛型限定,一边没有是正确的泛型。
27、Map 接口
--Hashtable:底层是哈希表数据结构,不可以存入 null 键 null 值。该集合是线程同步。jdk1.0 出现,效率低。
--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将 hashtable 替代,jdk1.2 出现,效率高。
--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给 map 集合中的键进行排序。
Map 集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
①添加
put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。
putAll(Map m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
②删除
clear() 从此映射中移除所有映射关系(可选操作)。
remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
③判断
isEmpty() 如果此映射未包含键-值映射关系,则返回 true。
containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。
containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。
④获取
get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
size() 返回此映射中的键-值映射关系数。
values() 返回此映射中包含的值的 Collection 视图。
【重点】
entrySet() 返回此映射中包含的映射关系的 Set 视图。
keySet() 返回此映射中包含的键的 Set 视图。
Map 集合的两种取出方式:
Set> entrySet(): 将集合中的映射关系存入到了 set 集合中,而这个关系的数据类型就是:Map.Entry.
Set keySet(): 将 map 中所有的键值存入到 Set 集合。因为 Set 具备迭代器,所以可以用迭代方式取出所有的键,
再根据 get()方法获取每一个键对应的值。
Map 集合的取出原理:
将 map 集合转成 set 集合。再通过迭代器取出。
将集合转成数组
指定类型的数组的长度的定义:
当指定类型的数组长度少于了集合的 size(),那么该方法内部会创建一个新的数组,长度为集合的 size();当指定类型的数组长度大于了集合的 size(),就不会创建新数组,而是使用传递进来的数组.所以创建一个刚刚好的数组最优.
为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。
28、增强 for 循环
只适用于数组和实现 Iterator 的集合。只能遍历,不能对数据进行修改。
格式:
对集合进行遍历,只能获取集合元素,不能对集合进行操作。迭代器除了遍历,还可以进行 remove 集合中元素的动作。如果使用 ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统 for 和高级 for 的区别:
高级 for 有一个局限性,即必须有别遍历的目标。
建议在遍历数组的时候,用传统 for。因为传统 for 可以定义角标。
29、可变参数
其实就是数组参数的简写形式。不用每一次都手动的建立数组对象。只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。
方法的可变参数,在使用时注意:可变参数一定要定义在参数列表最后面。
30、静态导入
格式:
import static java.包名.类名.*;//导入的是某个类中的静态成员.
例如:``import static java.util.Arrays.*;`
好处:
在程序中使用这个类中的静态方法的时候就不要再去写包名。
31、System 类
描述系统一些信息。
System: 静态方法和静态成员;
out:标准输出,默认是控制台;
in:标准输入,默认是键盘。
获取系统属性信息:
Properties getProperties();
32、Runtime 类
该类没有提供构造函数。说明不可以 new 对象。该类提供了一个非静态方法用来获取本类对象,该方法是静态的,并返回值类型是本类类型。
根据这个特点判断,该类使用了单例设计模式。
该方法是:static Runtime getRuntime(){}
33、Date: 表示特定的瞬间,精确到毫秒.
|-DateFormat:是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间.
|-SimpleDateFormat:是一个以与语言环境有关的方式来格式化和解析日期的具体类.该类使得可以选择任何用户定义的日期-时间格式的模式.
34、Calendar:
抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法.
Calendar 子类:
GregorianCalendar:
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统.
35、IO 流
字节流和字符流
字节流两个基类:
InputStreamOutputStream
字符流两个基类:
ReaderWriter
缓冲区:
缓冲区的出现是为了提高流的操作效率。所以在创建缓冲区之前,必须要先有流对象。
BufferedWriter 缓冲区提供了一个跨平台的换行符,newLine()。
装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
装饰设计模式和继承的区别:
装饰设计模式比继承要灵活,避免了继承体系的臃肿。而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能。所以装饰类和别装饰类通常是都属于一个体系中的。
java.io.Reader
|-java.io.BufferedReader
|-java.io.LineNumberReader
LineNumberReader: 跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int)和 getLineNumber(),它们可分别用于设置和获取当前行号。默认情况下,行编号从 0 开始。
流操作规律之一:
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
2,操作的数据是否是纯文本。
是,字符流。
不是,字节流。
3,当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。
1,需求:讲一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream Reader
是不是操作文本文件:
是:选择 Reader.
明确要使用该体系中的哪个对象?
明确设备:硬盘。
Reader 体系中可以操纵文件的对象是 FileReader
是否需要提高效率:是!加入 Reader 体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本:是!Writer.
设备:硬盘,一个文件。
Writer 体系中可以操作文件的对象 FileWriter.
是否需要提高效率:是!加入 Writer 体系中缓冲区 BufferedWriter.
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufr = new BufferedWriter(fw);
流操作规律之二:
2,需求:将键盘录入的数据保存到一个文件中。这个需求中有源和目的都存在。那么分别分析:源:InputStream Reader. 是不是纯文本?是!Reader.
设备:键盘。对应的对象是 System.in.
可是 System.in 对应的是字节流,为了操作键盘的文本数据方便。转成字符流按照字符串操作最方便。所以既然明确了 Reader,那么就将 System.in 转换成 Reader.用 Reader 体系中的转换流,InputStreamReader.
InputStreamReader isr = new InputStream(System.in);
需要提高效率?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
36、File 类
File 的常见操作 1:
创建
boolean createNewFile()
: 在指定位置创建文件,如果该文件已经存在,则不创建,返回 false。
和输出流不一样,输出流对象一建立创建文件,而且文件已经存在,会覆盖。
删除
boolean delete()
:删除失败返回 false。
void deleteOnExit()
:在程序退出时删除指定文件。
判断
boolean exists()
:
boolean isFile()
:
boolean isDirectory()
:
boolean isHidden()
:
boolean isAbsolute()
: 测试此抽象路径名是否为绝对路径名.
获取信息
String getName()
: 返回由此抽象路径名表示的文件或目录的名称.
String getPath()
: 将此抽象路径名转换为一个路径名字符串.
String getParent()
: 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null.
String getAbsolutePath()
: 返回此抽象路径名的绝对路径名字符串.
long lastModified()
: 返回此抽象路径名表示的文件最后一次被修改的时间.
long length()
: 返回由此抽象路径名表示的文件的长度.
File 常见操作 2
String[] list()
返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录 .
String[] list(FilenameFilter filter)
返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录 .
File[] listFiles()
返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件 .
static File[] listRoots()
列出可用的文件系统根 .
long length()
返回由此抽象路径名表示的文件的长度 .
public interface FilenameFilter
实现此接口的类实例可用于过滤器文件名.
此接口中只有一个方法:boolean accept(File dir, String name)
测试指定文件是否应该包含在某一文件列表中 .
递归:
因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。
这种表现形式,或者编程手法,称为递归.
递归要注意:
限定条件
递归的次数,尽量避免内存溢出。
Properties
Properties 是 Hashtable 的子类。
Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。也就是说它具备 map 集合的特点。而且它里面存储的键值对都是字符串。该类是集合中和 IO 技术相结合的集合容器。
该对象的特点:
可以用于键值对形式的配置文件。
37、打印流
字节打印流:PrintStream
构造函数可以接收的参数类型:
1,file 对象,File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流:PrintWriter
构造函数可以接收的参数类型:
1,file 对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer.
38、SequenceInputStream
SequenceInputStream:
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
39、操作对象流
ObjectOutputStream: 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。
通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
只能将支持 java.io.Serializable 接口的对象写入流中。
ObjectInputStream: 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectInputStream 用于恢复那些以前序列化的对象。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,
可以为应用程序提供对对象图形的持久存储。
Serializable 接口:
类通过实现 java.io.Serializable 接口以启用其序列化功能。序列化接口没有方法或字段,仅用于标识可序列化的语义。
40、管道流
PipedOutputStream:
可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。
PipedInputStream:
管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。
管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
41、RandomAccessFile 随机访问文件
该类不算是 IO 体系中的子类。而是直接继承自 Object。但是它是 IO 包中的成员,因为它具备读和写功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过 getFileRointer 获取指针位置,同时,可以通过 seek 改变指针的位置。
其实,完成读写的原理就是内部封装了字节数如流和字节输出流。
该类只能操作文件。而且有模式:只读 r,读写 rw 等。
该对象的构造函数要操作的文件不存在,会自动创建。
如果模式为只读,不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。如果模式为 rw,如果文件不存在会自动创建,如果存在则不会覆盖。
42、操作数据的流对象:DataInputStream 和 DataOutputStream
DataInputStream:
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
DataInputStream 对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责。
DataOutputStream:
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
void writeUTF(String str)
: 以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流 .
43、ByteArrayInputStream 和 ByteArrayOutputStream
ByteArrayInputStream:
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。 关闭 ByteArrayInputStream 无效。
此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
ByteArrayOutputStream:
此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
void writeTo(OutputStream out):
将此 byte 数组输出流的全部内容写入到指定的输出流参数中,这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
ByteArrayInputStream:在构造时,需要接受数据源。而且数据源是一个字节数组。
ByteArrayOutputStream:在构造时,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。也就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行 close 关闭。
44、正则表达式
**字符类 **
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
**预定义字符类 **
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
知识点:
1,匹配:matches();
2,切割:split();
3,替换: replaceAll();
4,获取。
// 切割:
// 替换
获取
步骤:
1,将正则表达式封装成对象。
2,让正则对象和要操作的字符串相关联。
3,关联后,获取正则匹配引擎。
4,通过引擎对符合规则的子串进行操作,比如取出。
边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
public final class Pattern : 正则表达式的编译表示形式。
指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。
public final class Matcher:通过解释 Pattern 对 character sequence 执行匹配操作的引擎。
boolean find(): 尝试查找与该模式匹配的输入序列的下一个子序列。
String group(): 返回由以前匹配操作所匹配的输入子序列。
45、枚举类
枚举类也是一种特殊形式的 java 类。
枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
与 java 中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的。
枚举类也可以实现接口、或继承抽象类。
JDK5 中扩展了 switch 语句,它除了可以接受 int,byte,char,short 外,还可以接受一个枚举类型。
若枚举类只有一个枚举值,则可以当作单态设计模式使用。
常用方法:
String name()
:
返回此枚举常量的名称,在其枚举声明中对其进行声明。
int ordinal()
:
返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
static > T valueOf(Class enumType, String name)
:
返回带指定名称的指定枚举类型的枚举常量
【注意】
values(): 此方法虽然在 JDK 文档中查找不到,但每个枚举类都具有该方法,它用于遍历枚举的所有枚举值。
46、反射
加载类的三种方式
反射类的字段
常用方法:
Object get(Object obj): 返回指定对象上此 Field 表示的字段的值 .
void set(Object obj, Object value): 将指定对象变量上此 Field 对象表示的字段设置为指定的新值 .
Class getType(): 返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型.
反射方法:
反射构造方法
反射主方法(两种处理方式):public static void main(String[] args)
47、内省(Introspector)
使用内省 API 操作 bean 的属性
static BeanInfo getBeanInfo(Class beanClass)
: 在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
static BeanInfo getBeanInfo(Class beanClass, Class stopClass)
:在给定的“断”点之下,在 Java Bean 上进行内省,了解其所有属性和公开的方法。
static BeanInfo getBeanInfo(Class beanClass, int flags)
:在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件,并将结果用一些控制标记表示。
PropertyDescriptor[] getPropertyDescriptors()
:获得 beans PropertyDescriptor。
大家好,我是一名正在学习 Java 的程序员,博客内容第一时间更新在公众号 `推荐学 java` ,功夫不负有心人,每天保持学习,掌握一项技能其实用不了多长时间,加油!
版权声明: 本文为 InfoQ 作者【逆锋起笔】的原创文章。
原文链接:【http://xie.infoq.cn/article/b02efb60837f5c36cc167e1b8】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论