Java基础(三)--常用工具类

第三章、常用工具类

一、Java异常

1、什么是异常

在程序运行过程中,意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常。在Java中,通过Throwable及其子类描述各种不同的异常类型。
在这里插入图片描述
Throwable有两个重要的子类:Exception和Error。

Error
Error是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,表示代码运行时JVM出现的问题。
这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。因此我们编写程序时不需要关心这类异常。

Exception
Exception是程序本身可以处理的异常。
Exception类的异常包括checked exception 和 unchecked exception。

unchecked exception
编译器不要求强制处理的异常。Java编译器不会检查这些异常,在程序中可以选择捕获处理,也可以不处理。包含RuntimeException类及其子类异常。如:NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)。

checked exception
编译器要求必须处置的异常。Java编译器会检查这些异常,当程序中可能出现这类异常时,要求必须进行异常处理,否则编译不通过。RuntimeException类及其子类以外其他Exception类的子类。如IOException、SQLException。

2、异常处理

抛出异常:
当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统处理。异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。

捕获异常:
在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。若未找到合适的异常处理器,则运行时系统终止,即Java程序终止。
简单地说,异常总是先被抛出,后被捕获的。

try-catch-finally

public void method(){
	try{
		//可能产生异常的代码段
	}catch(异常类型1 ex){
		//对异常进行处理的代码段1
	}catch(异常类型2 ex){
		//对异常进行处理的代码段2
	}finally{
		//无论是否发生异常都会执行的代码段,一般释放资源的代码
	}
}

try块后可以接零个或多个catch块,如果没有catch,则必须跟一个finally块。
catch语句的顺序:先子类,后父类
发生异常时按顺序逐个匹配,只执行第一个与异常类型匹配的catch语句。

注:
1.不执行finally块的唯一情况:在finally之前中断程序,如用System.exit(1)
2.在catch 中有return关键字时先执行finally语句,再执行return语句。

3、常见的异常类型

Exception 异常层次结构的父类
ArithmeticException 算术异常
ArrayIndexOutOfBoundsException 数组下标越界异常
NullPointerException 空指针异常
ClassNotFoundException 不能加载所需的类
IIIegalArgumentException 方法接受到非法参数
ClassCastException 类型转换异常
NumberFormatException 数字格式转换异常

4、throws

如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明出用throws子句来声明抛出异常。throws语句用在定义方法时声明该方法要抛出的异常类型,该方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法。

public void method() throws Exception1,Exception2,...ExceptionN{
	//可能产生异常的代码
}

throws的使用规则:
1、如果是不可检查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能通过,但是运行时会被系统抛出。
2、如果一个方法中可能出现可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则编译错误。
3、当抛出了异常,则该方法的调用者必须处理货重新抛出该异常。
4、当子类重写父类抛出异常的方法时,声明的异常必须是父类方法所声明异常的同类或子类。

5、throw

throw用来抛出一个异常。
例如:throw new IOException();
throw抛出的只能是Throwable或者其子类的实例对象。抛出异常后,可以用try-catch语句捕获,也可以用throws子句声明将它抛出。

6、自定义异常

所谓自定义异常,就是定义一个类,去继承Throwable类或者它的子类。
自定义异常的常见问题:
1、自定义异常属于检查异常还是非检查异常,要看继承的父类。
2、几个异常处理的方法:
e.toString(); 获得异常类型和描述信息,当直接输出对象e时,默认调用。
e.getMessage(); 获得异常描述信息。
e.printStackTrace(); 打印出异常产生的堆栈信息,包括种类、描述信息、出错位置等。
3、自定义异常需要先经过throw抛出,才能被catch捕获,是无法自动被程序捕获并处理的。

7、异常链

捕获一个异常后再抛出另一个异常,将异常发生的原因一个传一个串起来,即把底层的异常信息传给上层,这样逐层抛出。
可以借用在异常的根类Throwable中提供的带参构造方法Throwable(String message, Throwable couse)以及初始化方法initCause(Throwable couse)实现异常链信息的传递。

二、包装类

1、包装类

基本类型对应的包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

装箱 =》基本数据类型 转换成 包装类
1、自动装箱

int t1 = 2;
Integer t2 = t1;

2、手动装箱

Integer t3 = new Integer(t1);
Integer t6 = Integer.valueOf(t1);

拆箱 =》 包装类 转换成 基本数据类型
1、自动拆箱

int t4 = t2;

2、手动拆箱

Integer t5 = t2.intValue();

注:
1、类型特点:包装类是引用类型,拥有方法和属性;基本数据类型只包含数值信息。
2、存储方式:包装类对象实例化,借由new在堆空间里进行空间分配,对象栈空间中存储地址引用;基本数据类型变量对应栈空间中存储的是具体数据值。通常,包装类的效率会比基本数据类型的效率低,空间占用大。

int one = 12;
Integer two = new Integer(20);

在这里插入图片描述
3、初始值:基本数据类型有各自默认初始值,包装类的对象未初始化时,初始值均为null。

2、字符串与基本数据类型转换

基本数据类型转字符串:
使用包装类的toString()方法

字符串转换为基本数据类型:
自动拆箱调用包装类的parseXxx()静态方法
调用包装类的valueOf()方法转换为基本类型的包装类,自动拆箱

3、包装类的比较

1、拆箱后的数据是基础数据类型。用==判断相等性,比较的都是数值,如果是字符,比较的是ASCII值。
2、装箱后如果用==比较对象的内存地址,除Double、Float外,如数据值在缓存区范围内(-128~127),则相同;反之会重新生成对象,为不同。
3、调用equals方法时,当类型相同,且数值相同时,返回true;反之,返回false。当比对方为基本数据类型时,会进行自动装箱操作,后进行比较。

三、String 类

1、创建String对象的方法

String s1 = "hello";//创建一个字符串对象hello,名为s1
String s2 = new String();//创建一个空字符串对象,名为s2
String s3 = new String("hello");//创建一个字符串对象hello,名为s3
char[] ch = {'a','b','c'};
String s4 = new String(ch);//abc
String s5 = new String(ch,1,2);//bc 1表示从第几位开始,2表示几个元素
byte[] b = [54,69,70,71,72];
String s6 = new String(b,"utf-8");

2、String的常用方法

在这里插入图片描述
注:字符串索引是从0开始的。

3、字符串的存储

在这里插入图片描述

4、字符串StringBuilder

1)String和StringBuilder的区别:

String具有不可变性,而StringBuilder不具备。

建议:
当频繁操作字符串时,使用StringBuilder。

2)StringBuilder和StringBuffer

二者基本相似

StringBuffer是线程安全的,StringBuilder则没有,所以性能略高

在执行速度方面的比较:StringBuilder > StringBuffer

3)StringBuilder类的常用方法

在这里插入图片描述

四、集合

集合框架的体系结构
在这里插入图片描述
集合和数组的区别
1、数组的长度是固定的,集合的长度可以动态扩展。
2、数组只能存储相同数据类型的数据,而集合可以存储不同数据类型的数据。
3、数组可以存储基本数据类型数据,也可以是引用类型,而集合只能是引用类型。

1、List(列表)

List是元素有序并且可以重复的集合,称为序列
List可以精确的控制每个元素的插入位置,或删除某个位置的元素
List的两个主要实现类是ArrayList和LinkedList

ArrayList
ArrayList底层是由数组实现的
动态增长,以满足应用程序的需求
在列表尾部插入或删除非常有效
更适合查找和更新元素
ArrayList中的元素可以为null

LinkedList
与 ArrayList 一样,LinkedList 也按照索引位置排序,但它的元素之间是双向链接的
适合快速地插入和删除元素
LinkedList 实现 List 和 Queue 两个接口

2、Set

Set是元素无序并且不可以重复的集合,被称为集。

HashSet
HashSet是Set的一个重要实现类,称为哈希集(底层数组+链表实现)
HashSet中的元素无序并且不可以重复
HashSet中只允许一个null元素
具有良好的存取和查找性能

Iterator(迭代器)
Iterator接口可以以统一的方式对各种集合元素进行遍历
hasNext()方法检测集合中是否还有下一个元素
next()方法返回集合中的下一个元素、

hashCode和equals方法的作用
hashCode()方法用于给对象返回hash code值,equals()方法用于判断其他对象与该对象是否相等。为什么需要这两个方法呢?我们知道HashSet中是不允许添加重复元素的,那么当调用add()方法向HashSet中添加元素时,是如何判断两个元素是不同的。这就用到了hashCode()和equals()方法。在添加数据时,会调用hashCode()方法得到hash code值,通过这个值可以找到数据存储位置,该位置可以理解成一片区域,在该区域存储的数据的hashCode值都是相等的。如果该区域已经有数据了,就继续调用equals()方法判断数据是否相等,如果相等就说明数据重复了,就不能再添加了。如果不相等,就找到一个位置进行存储。

这些是基于哈希算法完成的,它使得添加数据的效率得到了提升。假设此时Set集合中已经有100个元素,那么如果想添加第101个元素,如果此时没有使用哈希算法,就需要调用equals()方法将第101个元素与前100个元素依次进行比较,如果元素更多,比较所耗费的时间就越长。

如果两个对象相等,那么他们的hashCode值一定相等。反之,如果两个对象的hashCode值相等,那么这两个对象不一定相等,还需要使用equals()方法进行判断。

如果不重写hashCode()方法,默认每个对象的hashCode()值都不一样,所以该类的每个对象都不会相等。

3、Map

Map中的数据是以键值对(key-value)的形式存储的
key-value以Entry类型的对象实例存在
可以通过key值快速地查找value
一个映射不能包含重复的键
每个键最多只能映射到一个值

HashMap
基于哈希表的Map接口的实现
允许使用null值和null键
key值不允许重复
HashMap中的Entry对象是无序排列的

4、集合排序

使用Collections类的sort()方法
sort(List list) -根据元素的自然顺序对指定列表按升序进行排序。

1)Comparator接口

强行对某个对象进行整体排序的比较函数。
可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort)

int compare(T o1, T o2) 比较用来排序的两个参数。
– 如果o1<o2,返回负整数
– 如果o1==o2,返回0
– 如果o1>o2,返回正整数

//1.先定义比较器
public class NameComparator implements Comparator<Cat>{
	@Override
	public int compara(Cat o1, Cat o2){
		//按名字升序排序
		String name1=o1.getName();
		String name2=o2.getName();
		int n= name1.comparaTo(name2);
		return n;
	}
}
//2.调用排序
Collections.sort(catList, new NameComparator());
2)Comparable接口

此接口强行对实现它的每个类的对象进行整体排序。
这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
对于集合,通过调用Collections. sort方法进行排序。
对于数组,通过调用Arays.sort方法进行排序。

int compareTo(T o)方法
该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

//1.定义类的时候实现Comparable接口
public class Dog implements Comparable<Dog>{
	private int age;
	...
	@Override
	public int comparaTo(Dog o){
		//按名字升序排序
		int age1=this.getAge();
		int age2=o.getAge();
		int n= age2 - age1;
		return n;
	}
}
//2.直接调用sort方法排序
Collections.sort(dogList);

注:包装类和String都有自己的comparaTo方法

3)Comparable和Comparator的区别

1、Comparator接口位于java.util包下,而Comparable接口位于java.lang包下。
2、对于Comparator接口,可以看到它的compare()方法的参数是两个对象,所以可以有一个单独的类实现Comparator。
对于Comparable接口,它的方法只有一个对象作为参数,所以要比较的类需要实现Comparable接口,将当前对象与方法参数中的对象进行比较。
因此,如果使用Comparator接口,那么要比较的类和实现Comparator接口的类可以分开,如果使用Comparable接口,那么要比较的类就要实现Comparable接口才可以。
3、关于应用场景
一般情况下如果对某个类进行排序,比如Cat类,如果使用Comparable接口的方式,那么Cat类需要实现Comparable接口。
如果Cat类通过Comparable接口的方式实现排序,比如通过name排序了。那么我们还希望通过age进行排序,这时不希望修改Cat类,那此时就需要使用Comparator接口了。
因此,Comparable接口可以作为实现类的默认排序算法,Comparator接口则用于一个类的扩展排序。

五、泛型

1、为什么使用泛型

在Java中增加泛型之前,泛型程序设计使用继承来实现的
坏处:
-需要强制转换
-可向集合中添加任意类型的对象,存在风险。

2、泛型的使用

List list = new ArrayList();
Java SE7及以后的版本中,构造方法中可以省略泛型类型。
List list=new ArrayList<>();

注: 变量声明的类型必须匹配传递给实际对象的类型
其他的错误例子:
List list=new ArrayList();
List numbers=new ArrayList();

泛型作为方法参数
<? extends Goods>表示添加的类型是Goods或子类都是允许的,extends后面的内容也可以是接口
<? super Goods> 表示添加的类型是Goods或超类都是允许的

3、自定义泛型类

public class NumGeneric<T>{
	private T num;
	//get、set...
}
NumGeneric<Integer> i = new NumGeneric<>();
i.setNum(10);
NumGeneric<Float> i = new NumGeneric<>();
i.setNum(10.5f);

4、自定义泛型方法

泛型方法不一定写在泛型类里面

public class One{
	public <T extends Number> void printV(T t){
		sout(t);
	}
}
//调用
One one = new One();
one.printV(10);
one.printV(10.5f);

六、Java输入输出流

流是指一连串流动的字符,以先进先出的方式发送信息的通道。
文件输入————读
文件输出————写

1、File类

什么是文件?
文件可认为是相关记录或放在一起的数据的集合。
在Java中,使用java.io.File类对文件进行操作。
注:
Windows中的目录分隔符为""
Linux中的目录分隔符为"/"

绝对路径:是从盘符开始的路径
相对路径:是从当前路径开始的路径

//创建file对象
File file1 = new File("c:\\abc\\a.txt");
File file2 = new File("c:\\abc","a.txt");
File file3 = new File("c:\\abc\\bcd");
File file4 = new File(file3,"a.txt");
file1.isDirectory();//是否是目录
file1.isFile();//是否是文件
file3.exists();//判断是否存在
file3.mkdir();//创建一个目录
file3.mkdirs();//创建多个目录
file4.createNewFile();//创建文件
file3.isAbsolute();//是否绝对路径
file3.getPath()//获取路径(构造方法的路径)
file3.getAbsolutePath();//获取绝对路径
file4.getName();//获取文件名

2、字节流

字节输入流InputStream
在这里插入图片描述
字节输出流OutputStream
在这里插入图片描述

1)FileInputStream

从文件系统中的某个文件中获得输入字节。
用于读取诸如图像数据之类的原始字节流。

FileInputStream常用方法
在这里插入图片描述
注:如果返回值为-1,则表示已经达到文件末尾!

2)FileOutputStream

FileOutputStream常用方法
在这里插入图片描述

//**文件拷贝小案例:**
try{
	FileInputStream fit = new FileInputStream("happy.gif");
	FileOutputStream fos = new FileOutputStream("happycopy.gif");
	int n=0;
	byte[] b = new byte[1024];
	while((n=fit.read(b))!=-1){
		fos.write(b,0,n);
	}
	fit.close();
	fos.close();
}catch(FileNotFountException e){
	e.printStackTrace();
}catch(IOException e){
	e.printStackTrace();
}
3)缓冲流

缓冲输入流BufferedInputStream
缓冲输出流BufferedOutputStream

//**文件拷贝小案例:**
try{
	FileInputStream fis = new FileInputStream("happy.gif");
	BufferedInputStream bis = new BufferedInputSream(fis);
	FileOutputStream fos = new FileOutputStream("happycopy.gif");
	BufferedOutputStream bos = new BufferedOutputSream(fos);
	int n=0;
	byte[] b = new byte[1024];
	while((n=bis.read(b))!=-1){
		bos.write(b,0,n);
		bos.flush();
	}
	fis.close();
	bis.close();
	fos.close();
	bos.close();
}catch(FileNotFountException e){
	e.printStackTrace();
}catch(IOException e){
	e.printStackTrace();
}

3、字符流

字符输入流Reader
在这里插入图片描述
字符输出流Writer
在这里插入图片描述

字节字符转换流
InputStreamReader
OutputStreamWriter

//**文件拷贝小案例:**
try{
	FileInputStream fis = new FileInputStream("happy.txt");
	InputStreamReader bis = new InputStreamReader(fis,"GBK");
	FileOutputStream fos = new FileOutputStream("happycopy.txt");
	OutputStreamWriterbos = new OutputStreamWriter(fos,"GBK");
	int n=0;
	char[] b = new char[10];
	while((n=bis.read(b))!=-1){
		bos.write(b,0,n);
		bos.flush();
	}
	fis.close();
	bis.close();
	fos.close();
	bos.close();
}catch(FileNotFountException e){
	e.printStackTrace();
}catch(IOException e){
	e.printStackTrace();
}

4、对象序列化

步骤:
-创建一个类,继承Serializable接口
-创建对象
-将对象写入文件
-从文件读取对象信息

对象输入流ObjectInputStream
对象输出流ObjectOutputStream

public class Goods implement Serializable{
	...
}
Goods goods1 = new Goods("gd001","电脑",3000);
try{
	FileOutputStream fos = new FileOutputStream("a.txt");
	ObjectOutputStream oos = new ObjectOutputStream(fos);
	FileInputStream fis = new FileInputStream("a.txt");
	ObjectInputStream ois = new ObjectInputStream(fis);
	//将Goods对象信息写入文件
	oos.writeObject(goods1);
	oos.writeBoolean(true);
	oos.flush();
	//读对象数据
	try{
		Goods goods = (Goods)ois.readObject();
		sout(goods);//输出:商品信息[编号:gd001,名称:电脑,价格:3000]
	}catch(ClassNotFoundException e){
		e.printStackTrace();
	} 
	sout(ois.readBoolean());//输出:true
	fos.close();
	oos.close();
	fis.close();
	ois.close();
}catch(FileNotFountException e){
	e.printStackTrace();
}catch(IOException e){
	e.printStackTrace();
}

七、多线程

1、什么是线程

进程:进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程
线程:是比进程还要小的运行单位,一个进程包含多个线程

2、线程的创建

创建一个Thread 类,或者一个Thread 子类的对象
创建一个实现Runnable接口的类的对象
Thread类
Thread 是一个线程类,位于 java.lang 包下

构造方法:
Thread() 创建一个线程对象
Thread(String name) 创建一个具有指定名称的线程对象
Thread(Runnable target) 创建一个基于Runnable接口实现类的线程对象
Thread(Runnable target,String name) 创建一个基于Runnable接口实现类,并且具有指定名称的线程对象。

Thread类的常用方法:
public void run() 线程相关的代码写在该方法中,一般需要重写
public void start() 启动线程的方法,一个线程实例只能启动一次
public static void sleep(long m) 线程休眠m毫秒的方法
public void join() 优先执行调用join()方法的线程

class MyThread extends Thread{
	public MyThread(String name){
		super(name);
	}
	public void run(){
		sout(getName()+"该线程正常执行");
	}
}
MyThread mt1 = new MyThread("线程1");
mt1.start();
MyThread mt2 = new MyThread("线程2");
mt2.start();

Runnable接口
只有一个方法run();
Runnable 是 Java 中用以实现线程的接口
任何实现线程功能的类都必须实现该接口

为什么要实现Runnable接口?
– Java不支持多继承
– 不打算重写Thread类的其他方法

class MyRunnable implements Runnable{
	int i = 1;
	@Override
	public void run(){
		while(i<=10){
			sout(Thread.currentThread().getName()+ "正在运行"+(i++));
		}
	}
}
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(pr);
t1.start();
//MyRunnable mr1 = new MyRunnable();
Thread t2 = new Thread(pr);
t2.start();
//两个线程共享一个资源,总共打印10次

3、线程的生命周期

在这里插入图片描述
sleep方法应用
public static void sleep(long millis)
作用:在指定的毫秒数内让正在执行的线程休眠(暂停执行)
参数为休眠的时间,单位是毫秒

//在run方法里写,需要捕获异常
try{
	Thread.sleep(1000);
}catch(InterruptedException e){
	e.printStackTrace();
}

join方法应用
public final void join()
作用:等待调用该方法的线程结束后才能执行

//调用线程时执行
MyThread mt = new MyThread();
mt.start();
try{
	mt.join();
}catch(InterruptedException e){
	e.printStackTrace();
}

public final void join(long millis)
作用:等待该线程终止的最长时间为millis毫秒。
如果millis 0 为 则意味着要一直等下去。

4、线程优先级

Java为线程类提供了10个优先级
优先级可以用整数 1-10 表示,超过范围会抛出异常
主线程默认优先级为5
优先级常量
MAX_PRIORITY :线程的最高优先级 10
MIN_PRIORITY :线程的最低优先级 1
NORM_PRIORITY :线程的默认优先级 5
优先级相关的方法
public void setPriority(int newPriority) 设置线程优先级的方法,在启动线程之前执行
public int getPriority () 获取线程优先级的方法,启动线程之后执行

5、线程同步

各个线程是通过竞争 CPU 时间而获得运行机会的
各线程什么时候得到 CPU 时间,占用多久,是不可预测的
一个正在运行着的线程在什么地方被暂停是不确定的

使用关键字synchronized实现同步锁
synchronized关键字用在
– 成员方法 public synchronized void saveAccount(){}
– 静态方法 public static void saveAccount synchronized (){}
– 语句块 synchronized (obj){……}

6、线程间通信

wait()方法:中断方法的执行,使线程等待
notify()方法:唤醒处于等待的某一个线程,使其结束等待
notifyAll()方法:唤醒所有处于等待的线程,使它们结束等待

//在普通方法里写
//需要中断的时候
try{
	wait();
}catch(InterruptedException e){
	e.printStackTrace();
}
//需要唤醒的时候
notifyAll();

7、创建多线程的第三种方式

实现Calleble接口,重写call()方法,call()作为线程的执行体,具有返回值,并且可以对异常进行声明和抛出;使用start()方法来启动线程。

public class ThirdThread implements Callable<String>{
	@Override
	public String call() throws Exception {
		//方法类型可以根据要返回值的类型进行确认
		String str = "多线程的第三种创建方法";
		return str;
	}
}
Callable<String> call = new ThirdThread();
FutureTask<String> ft = new FutureTask<>(call);
Thread t3 = new Thread(ft);
t3.start();
//获取call方法的返回值:先启动线程才可以获取到Call的返回值
try{
	sout(ft.get());
}catch (InterruptedException | ExecutionException e){
	e.printStackTrace();
}
  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值