【培训】DAY11 JAVA面向对象(下)
JAVA面向对象
- 常用类、数组
- Object(老祖宗盘古)java.lang.Object
- String(字符串类)java.lang.String
- StringBuffer(字符串缓冲区)java.lang.StringBuffer
- StringBuilder(字符串缓冲区)java.lang.StringBuilder
- Integer int(对象包装类)java.lang.Integer
- System(系统类)java.lang.System
- Runtime(运行时环境类)java.lang.Runtime
- Math(数学运算工具类)java.util.Math
- Date(日期类)ava.util.Date
- Calendar(日历类)ava.util.Calendar
- File(文件类)java.io.File
- Properties
- 容器类
- 流技术
- 异常处理机制
常用类、数组
Object(老祖宗盘古)java.lang.Object
1.所有的类直接或者间接继承父类 Java认为所有的对象都具备一些基本的共性内容 这些内容可以不断的向上抽取 最终就抽取到了一个最顶层的类中(Object) 该类中定义的就是所有对象都具备的功能
2.具体方法:
boolean equals(Object obj): 用于比较两个对象是否相等 其实内部比较的就是两个对象地址
String toString(): 将对象变成字符串 默认返回的格式: 类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode()) 为了对象对应的字符串内容有意义 可以通过复写 建立该类对象自己特有的字符串表现形式
Class getClass(): 获取任意对象运行时的所属字节码文件对象
int hashCode(): 返回该对象的哈希码值 支持此方法是为了提高哈希表的性能
通常equals, toString, hashCode在应用中都会被复写 建立具体对象的特有的内容
String(字符串类)java.lang.String
java中用String类进行描述 对字符串进行了对象的封装 这样的好处是可以对字符串这种常见数据进行方便的操作 对象封装后 可以定义N多属性和行为
特点: 字符串一旦被初始化 就不可以被改变 存放在方法区中的常量池中
具体方法:
- 构造方法 将字节数组或者字符数组转成字符串
String s1 = new String(); //创建了一个空内容的字符串
String s2 = null; //s2没有任何对象指向 是一个null常量值
String s3 = ""; //s3指向一个具体的字符串对象 只不过这个字符串中没有内容
String s4 = new String("abc");
String s5 = "abc"; //一般用此写法
new String(char[]); //将字符数组转成字符串
new String(char[], offset, count); //将字符数组中的一部分转成字符串
- 一般方法
a. 获取
b. 判断length(); //获取字符串的长度 char charAt(int index); //指定位置的字符 int indexOf(int ch); //返回第一次找到的字符角标 如果不存在返回-1 int indexOf(int ch, int fromIndex); //返回从指定位置开始第一次找到的角标 如果不存在返回-1 int indexOf(String str); // 返回第一次找到的字符串角标 如果不存在返回-1 int indexOf(String str, int fromIndex); int lastIndexOf(int ch); int lastIndexOf(int ch, int fromIndex); int lastIndexOf(String str); int lastIndexOf(String str, int fromIndex); String substring(int start); //从start位开始 到length()-1为止 String substring(int start, int end); //从start开始到end为止 //包含start位 不包含end位 substring(0, str.length()); //获取整串
c.转换boolean contains(String substring); //字符串中包含指定的字符串吗 boolean startsWith(string); //字符串是否以指定字符串开头啊 boolean endsWith(string); //字符串是否以指定字符串结尾啊 boolean equals(string); //覆盖了Object中的方法 判断字符串内容是否相同 boolean equalsIgnoreCase(string) ; //判断字符串内容是否相同 忽略大小写
//可以通过字符串中的静态方法 将字符数组转成字符串 static String copyValueOf(char[] ); static String copyValueOf(char[], int offset, int count); static String valueOf(char[]); static String valueOf(char[], int offset, int count); //将基本数据类型或者对象转成字符串 static String valueOf(char); static String valueOf(boolean); static String valueOf(double); static String valueOf(float); static String valueOf(int); static String valueOf(long); static String valueOf(Object);
StringBuffer(字符串缓冲区)java.lang.StringBuffer
构造一个其中不带字符的字符串缓冲区 初始容量为16个字符
特点:
- 可以对字符串内容进行修改
- 是一个容器
- 是可变长度的
- 缓冲区中可以存储任意类型的数据
- 最终需要变成字符串
方法:
//添加
StringBuffer append(data); 在缓冲区中追加数据 追加到尾部
StringBuffer insert(index, data); 在指定位置插入数据
//删除
StringBuffer delete(start, end); 删除从start至end-1范围的元素
StringBuffer deleteCharAt(index); 删除指定位置的元素
//sb.delete(0, sb.length()); 清空缓冲区
//修改
StringBuffer replace(start, end, string); 将start至end-1替换成string
void setCharAt(index, char); 替换指定位置的字符
void setLength(len); 将原字符串置为指定长度的字符串
//查找 (查不到返回-1)
int indexOf(string); 返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(string,int fromIndex); 从指定位置开始查找字符串
int lastIndexOf(string); 返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(string, int fromIndex); 从指定的索引开始反向搜索
//获取子串
string substring(start); 返回start到结尾的子串
string substring(start, end); 返回start至end-1的子串
//反转
StringBuffer reverse(); 字符串反转
StringBuilder(字符串缓冲区)java.lang.StringBuilder
JDK1.5出现StringBuiler 构造一个其中不带字符的字符串生成器 初始容量为 16 个字符 该类被设计用作StringBuffer的一个简易替换 用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)
具体方法:
和StringBuffer一样
StringBuffer 和 StringBuilder 的区别:
- StringBuffer线程安全
- StringBuilder线程不安全
- 单线程操作 使用StringBuilder 效率高
- 多线程操作 使用StringBuffer 安全
Integer int(对象包装类)java.lang.Integer
数字格式的字符串转成基本数据类型的方法
- 将该字符串封装成了Integer对象 并调用对象的方法intValue();
- 使用Integer.parseInt(numstring); 不用建立对象 直接类名调用
将基本类型转成字符串
- Integer中的静态方法 String toString(int);
- int + “”;
将一个十进制整数转成其他进制
- 转成二进制: toBinaryString
- 转成八进制: toOctalString
- 转成十六进制: toHexString
- toString(int num, int radix);
将其他进制转换十进制: parseInt(string, radix); //将给定的数转成指定的基数进制
在jdk1.5版本后 对基本数据类型对象包装类进行升级 在升级中 使用基本数据类型对象包装类可以像使用基本数据类型一样 进行运算
Integer i = new Integer(4); //1.5版本之前的写法
Integer i = 4; //自动装箱 1.5版本后的写法
i = i + 5;
//i对象是不能直接和5相加的 其实底层先将i转成int类型 在和5相加 而转成int类型的操作是隐式的 自动拆箱: 拆箱的原理就是i.intValue(); i+5运算完是一个int整数 如何赋值给引用类型i呢? 其实有对结果进行装箱
Integer c = 127;
Integer d = 127;
System.out.println(c = = d); //true
//在装箱时 如果数值在byte范围之内 那么数值相同 不会产生新的对象 也就是说多个数值相同的引用指向的是同一个对象
System(系统类)java.lang.System
属性和行为都是静态的
long currentTimeMillis(); //返回当前时间毫秒值
exit(); //退出虚拟机
Properties getProperties(); //获取当前系统的属性信息
Properties prop = System.getProperties(); //获取系统的属性信息 并将这些信息存储到Properties集合中
System.setProperty("myname","祎哥哥"); //给系统属性信息集添加具体的属性信息
String name = System.getProperty("os.name"); //获取指定属性的信息
Runtime(运行时环境类)java.lang.Runtime
类中没有构造方法 不能创建对象
但是有非静态方法 说明该类中应该定义好了对象 并可以通过一个static方法获取这个对象 用这个对象来调用非静态方法 这个方法就是 static Runtime getRuntime(); (单例设计模式)
class RuntimeDemo {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
Process p = r.exec("notepad.exe SystemDemo.java"); //运行指定的程序
Thread.sleep(4000);
p.destroy(); //杀掉进程
}
}
Math(数学运算工具类)java.util.Math
用于数学运算的工具类 属性和行为都是静态的 该类是final不允许继承
static double ceil(double a); //返回大于指定数值的最小整数
static double floor(double a); //返回小于指定数值的最大整数
static long round(double a); //四舍五入成整数
static double pow(double a, double b); //a的b次幂
static double random(); //返回0~1的伪随机数
Date(日期类)ava.util.Date
日期类 月份从0-11(这地方需要注意)
//日期对象转成毫秒值
Date d = new Date();
long time1 = d.getTime();
long time2 = System.currentTimeMillis(); //毫秒值
//毫秒值转成具体的日期
long time = 1322709921312l;
Date d = new Date();
d.setTime(time);
//将日期字符串转换成日期对象 使用的就是DateFormat方法中的 Date parse(String source);
public static void method() throws Exception {
String str_time = "2011/10/25";
DateFormat df = new SimpleDateFormat("yyyy/MM/dd"); //SimpleDateFormat作为可以指定用户自定义的格式来完成格式化
Date d = df.parse(str_time);
}
//将日期对象转换成字符串的方式 DateFormat类中的format方法
//创建日期格式对象
DateFormat df = new SimpleDateFormat(); //该对象的建立内部会封装一个默认的日期格式 11-12-1 下午1:48
//如果想要自定义日期格式的话 可使用SimpleDateFormat的构造函数 将具体的格式作为参数传入到构造函数中 必须要参与格式对象文档
df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//调用DateFormat中的format方法 对已有的日期对象进行格式化
String str_time = df.format(d);
Calendar(日历类)ava.util.Calendar
public static void method(){
Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.YEAR) + "年" + (c.get(Calendar.MONTH)+1) + "月"
+ getNum(c.get(Calendar.DAY_OF_MONTH)) + "日"
+ "星期" + getWeek(c.get(Calendar.DAY_OF_WEEK)));
}
public static String getNum(int num){
return num > 9 ? num + "" : "0" + num;
}
public static String getWeek(int index){
//查表法 建立数据的对应关系
//最好 数据个数是确定的 而且有对应关系 如果对应关系的一方 是数字 而且可以作为角标 那么可以通过数组来作为表
String[] weeks = {"","日","一","二","三","四","五","六"};
return weeks[index];
}
File(文件类)java.io.File
将文件系统中的文件和文件夹封装成了对象 提供了更多的属性和行为可以对这些文件和文件夹进行操作 这些是流对象办不到的 因为流只操作数据
具体方法:
//创建
boolean createNewFile(); 在指定目录下创建文件 如果该文件已存在 则不创建 而对操作文件的输出流而言 输出流对象已建立 就会创建文件 如果文件已存在 会覆盖 除非续写
boolean mkdir(); 创建此抽象路径名指定的目录
boolean mkdirs(); 创建多级目录
//删除 注意: 在删除文件夹时 必须保证这个文件夹中没有任何内容 才可以将该文件夹用delete删除 window的删除动作 是从里往外删 java删除文件不走回收站 要慎用
boolean delete(); 删除此抽象路径名表示的文件或目录
void deleteOnExit(); 在虚拟机退出时删除
Properties
一个可以将键值进行持久化存储的对象 Map --> Hashtable的子类
用于属性配置文件 键和值都是字符串类型
特点: 可以持久化存储数据, 键值都是字符串, 一般用于配置文件
load(); 将流中的数据加载进集合 原理其实就是将读取流和指定文件相关联 并读取一行数据 因为数据是规则的key=value 所以获取一行后 通过 = 对该行数据进行切割 左边就是键 右边就是值 将键 值存储到properties集合中
store(); 写入各个项后 刷新输出流
list(); 将集合的键值数据列出到指定的目的地
容器类
分类
Collection
一个独立元素的序列,这些元素都服从一条或者多条规则。 List必须按照插入的顺序保存元素,而set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
Map
一组成对的“键值对”对象,允许你使用键来查找值。
区别
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
主要方法:
boolean add(Object o) // 添加对象到集合
boolean remove(Object o) // 删除指定的对象
int size() // 返回当前集合中元素的数量
boolean contains(Object o) // 查找集合中是否有指定的对象
boolean isEmpty() // 判断集合是否为空
Iterator() // 返回一个迭代器
boolean containsAll(Collection c) // 查找集合中是否有集合c中的元素
boolean addAll(Collection c) // 将集合c中所有的元素添加到该集合
void clear() // 删除集合中所有元素
void removeAll(Collection c) // 从集合中删除c集合中也有的元素
void retainAll(Collection c) // 从集合中删除集合c中不包含的元素
List接口
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
共同点:
LinkedList、ArrayList都实现了List接口,都是不同步的,线程不安全,元素是有序的、可重复
LinkedList类
基于链表的数据结构,允许null元素,增加、删除、修改元素方面效率比ArrayList高。
此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(…));
ArrayList类
基于数组的数据结构,不同步,线程不安全,查询(get set)效率高。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并 没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
一般情况下使用这两个就可以了,因为非同步,所以效率比较高。
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
Array和ArrayList的区别及各自适用的场景
Array是数组,ArrayList是Array的加强版。
- array可以保存基本类型和对象类型,arrayList只能保存对象类型
- array数组的大小是固定的不能更改,而ArrayList的大小可以改变
- Array数组在存放的时候一定是同种类型的元素。ArrayList就不一定了,因为ArrayList可以存储Object。
- ArrayList有更加丰富的方法如addAll()、removeAll()、iterator()
适用场景:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList。
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个 Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例 如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该 异常。
Stack 类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方 法,还有 peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Set接口
Set容器类主要有HashSet和TreeSet等。
共同点:
元素不重复
实现了Java.util.Set接口。
HashSet类
- 不保证集合中元素的顺序
- 允许包含值为null的元素,但最多只能有一个null元素。
public class TestHashSet{
public static void main(String [] args){
HashSet h=new HashSet();
h.add("1st");
h.add("2nd");
h.add(new Integer(3));
h.add(new Double(4.0));
h.add("2nd"); //重复元素,未被添加
h.add(new Integer(3)); //重复元素,未被添加
h.add(new Date());
System.out.println("开始:size="+h.size());
Iterator it=h.iterator();
while(it.hasNext())
{
Object o=it.next();
System.out.println(o);
}
h.remove("2nd");
System.out.println("移除元素后:size="+h.size());
System.out.println(h);
}
}
TreeSet
TreeSet描述的是Set的一种变体——可以实现排序等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成的读uixiangxulie时刻按照“升序”排列。
public class TestTreeSet{
public static void main(String [] args){
TreeSet ts=new TreeSet();
ts.add("orange");
ts.add("apple");
ts.add("banana");
ts.add("grape");
Iterator it=ts.iterator();
while(it.hasNext()){
String fruit=(String)it.next();
System.out.println(fruit);
}
}
Map集合接口
Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
主要方法:
boolean equals(Object o)比较对象
boolean remove(Object o)删除一个对象
put(Object key,Object value)添加key和value
Hashtable类
HashTable和HashMap区别
- 继承不同。
public class Hashtable extends Dictionary implements Map
public class HashMap extends AbstractMap implements Map - Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
- Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
- 两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
- 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
流技术
Java 流(Stream)、文件(File)和IO
Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
读取控制台输入
Java 的控制台输入由 System.in 完成。为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
下面是创建 BufferedReader 的基本语法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
从控制台读取多字符输入
从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下:
int read( ) throws IOException
每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回 -1。该方法抛出 IOException。
下面的程序示范了用 read() 方法从控制台不断读取字符直到用户输入 “q”。
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRRead {
public static void main(String args[]) throws IOException {
char c;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符, 按下 'q' 键退出。");
// 读取字符
do {
c = (char) br.read();
System.out.println(c);
} while (c != 'q');
}
}
从控制台读取字符串
从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。它的一般格式是:
String readLine( ) throws IOException
下面的程序读取和显示字符行直到你输入了单词"end"。
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRReadLines {
public static void main(String args[]) throws IOException {
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
}
}
控制台输出
在此前已经介绍过,控制台的输出由 print( ) 和 println() 完成。这些方法都由类 PrintStream 定义,System.out 是该类对象的一个引用。
PrintStream 继承了 OutputStream类,并且实现了方法 write()。这样,write() 也可以用来往控制台写操作。
PrintStream 定义 write() 的最简单格式如下所示:
void write(int byteval):该方法将 byteval 的低八位字节写到流中。
注意:write() 方法不经常使用,因为 print() 和 println() 方法用起来更为方便。
//演示 System.out.write().
public class WriteDemo {
public static void main(String args[]) {
int b;
b = 'A';
System.out.write(b);
System.out.write('\n');
}
}
读写文件
一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。
下图是一个描述输入流和输出流的类层次图
FileInputStream
该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
有多种构造方法可用来创建对象。
可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream(“C:/java/hello”);
也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("C:/java/hello");
InputStream out = new FileInputStream(f);
创建了InputStream对象,就可以使用下面的方法来读取流或者进行其他的流操作。
FileOutputStream
该类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
有两个构造方法可以用来创建 FileOutputStream 对象。
使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream(“C:/java/hello”)
也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);
创建OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。
import java.io.*;
public class fileStreamTest {
public static void main(String args[]) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:
import java.io.*;
public class fileStreamTest2 {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
// 构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
// 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入");
// 写入到缓冲区
writer.append("\r\n");
// 换行
writer.append("English");
// 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close();
// 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close();
// 关闭输出流,释放系统资源
FileInputStream fip = new FileInputStream(f);
// 构建FileInputStream对象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
// 构建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
// 关闭读取流
fip.close();
// 关闭输入流,释放系统资源
}
}
异常处理机制
Java 异常处理
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
异常发生的原因有很多,通常包含以下几大类:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类的层次
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
Java 内置异常类
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常方法
下面的列表是 Throwable 类的主要方法:
捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try{
// 程序代码
}catch(ExceptionName e1){
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
实例
下面的例子中声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候就会抛出一个异常。
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
以上代码编译运行输出结果如下:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
声明自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{
}
只继承Exception 类来创建的异常类是检查性异常类。
下面的 InsufficientFundsException 类是用户定义的异常类,它继承自 Exception。
一个异常类和其它任何类一样,包含有变量和方法。
实例
以下实例是一个银行账户的模拟,通过银行卡的号码完成识别,可以进行存钱和取钱的操作。
import java.io.*;
//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{
//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
在下面的 CheckingAccount 类中包含一个 withdraw() 方法抛出一个 InsufficientFundsException 异常。
import java.io.*;
//此类模拟银行账户
public class CheckingAccount
{
private double balance;
private int number;
public CheckingAccount(int number)
{
this.number = number;
}
//方法:存钱
public void deposit(double amount)
{
balance += amount;
}
//方法:取钱
public void withdraw(double amount) throws
InsufficientFundsException
{
if(amount <= balance)
{
balance -= amount;
}
else
{
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
//方法:返回余额
public double getBalance()
{
return balance;
}
//方法:返回卡号
public int getNumber()
{
return number;
}
}
下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw() 方法。
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try
{
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e) {
System.out.println("Sorry, but you are short $"
+ e.getAmount());
e.printStackTrace();
}
}
}
编译上面三个文件,并运行程序 BankDemo,得到结果如下所示:
Depositing $500...
Withdrawing $100...
Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
at CheckingAccount.withdraw(CheckingAccount.java:25)
at BankDemo.main(BankDemo.java:13)
通用异常
在Java中定义了两种类型的异常和错误。
JVM(Java虚拟机) 异常:由 JVM 抛出的异常或错误。例如:NullPointerException 类,ArrayIndexOutOfBoundsException 类,ClassCastException 类。
程序级异常:由程序或者API程序抛出的异常。例如 IllegalArgumentException 类,IllegalStateException 类。