1.泛型
概念:允许在定义类,接口的时候通过一个字母标识表示类中的属性的类别或者方法返回值,使用时传入棱形尖括号的是引用类型
泛型版本:
List list = new ArrayList(); // 1
list.add(new Integer(0)); // 2
Integer x = list.iterator().next(); // 3
注意变量list的类型声明。它指定这不是一个任意的List,而是
一个Integer的List,写作:List。我们说List是一个带一个类型
参数的泛型接口(a generic interface that takes a type parameter),本
例中,类型参数是Integer。我们在创建这个List对象的时候也指定了一个
类型参数。
另一个需要注意的是第3行不需要类型转换。
list被声明为
List类型,这告诉我们无论什么时候什么地方使用list变量,编译器
保证里面的元素是正确的类型。
E:element 元素
T:type 类
K:key 键
V:value 值
注意:
让我们测试一下我们对泛型的理解。下面的代码片断合法么?
List ls = new ArrayList(); //1
List lo = ls; //2
第1行当然合法,但是这个问题的狡猾之处在于第2行。
这产生一个问题:
一个String的List是一个Object 的List 么?大多数人的直觉是回答:
“当然!”。
好,在看下面的几行:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图把Object赋值给String
这里,我们使用lo指向ls。我们通过lo来访问ls,一个String的list。
我们可以插入任意对象进去。结果是ls中保存的不再是String。当我们试
图从中取出元素的时候,会得到意外的结果。
java编译器当然会阻止这种情况的发生。第2行会导致一个编译错误。
总之,如果Foo是Bar的一个子类型(子类或者子接口),而G是某种
泛型声明,那么G是G的子类型并不成立!!
为了处理这种情况,考虑一些更灵活的泛型类型很有用。到现在为止我
们看到的规则限制比较大。
通配符
-
通配符不能放在泛型类的声明上
-
通配符不能放在创建对象时 右边是创建对象
-
通配符放方法中,接受类型不确定
-
有限制的通配符
<? extends Number> : 上限 使用的时候必须是继承某个类,或者是实现某个接口 <= (无穷小,Number]
<? super Number> : 下限 使用的时候是Number的父类或Number本身 >= [Number,无穷大)
具体代码如下:
package com.gec.generic02;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
// 通配符不能放在泛型类的声明上
//class Test<?>{}
public class 通配符的使用 {
@Test
public void test() {
Collection<?> coll = new ArrayList<String>();
//错误的原因是因为这个问号是未知的类型
//coll.add("hello");//编译错误
List<?> list = null;
//ok
list = new ArrayList<String>();
//ok
list = new ArrayList<Integer>();
//list.add(null);
List<String> list2 = new ArrayList<String>();
list2.add("hello");
List<Integer> list3 = new ArrayList<Integer>();
list3.add(3);
printList(list2);
printList(list3);
//通配符不能放在创建对象时 右边是创建对象
HashSet<Integer> set = new HashSet<>();
}
//通配符放方法中,接受类型不确定
private static void printList(List<?> l) {
for (Object object : l) {
System.out.println(object);
}
System.out.println("---------------------");
}
@Test
public void test2() {
/*
有限制的通配符
<? extends Number> : 上限 使用的时候必须是继承某个类,或者是实现某个接口 <= (无穷小,Number]
<? super Number> : 下限 使用的时候是Number的父类或Number本身 >= [Number,无穷大)
*/
Collection<Integer> coll = new ArrayList<Integer>();
coll.add(3);
coll.add(13);
coll.add(23);
printList3(coll);
}
public static void printList3(Collection<? extends Number> coll) {
Iterator<? extends Number> it = coll.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
@Test
public void test3() {
/*
有限制的通配符
<? extends Number> : 上限 使用的时候必须是继承某个类,或者是实现某个接口 <= (无穷小,Number] 最大是Number
<? super Number> : 下限 使用的时候是Number的父类或Number本身 >= [Number,无穷大) 最小是Number
*/
Collection<Object> coll = new ArrayList<Object>();
coll.add(3);
coll.add(13);
coll.add(23);
printList4(coll);
}
public static void printList4(Collection<? super Number> coll) {
Iterator<? super Number> it = coll.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
//不能放在泛型声明的方法上面 返回值的前面
//public static <?> void test(ArrayList<?> list) {}
//类型擦除 泛型有效的时候是在编译期间,程序运行的时候是不知道这个泛型的存在的,编译期间知道
@Test
public void test5() {
List<Integer> list = new ArrayList<>();
List<Double> list2 = new ArrayList<>();
System.out.println(list.getClass() == list2.getClass());//true
}
}
HashTable
HashTable也是基于键值对的集合,线程安全的集合。
子类Properties 通常作为项目的配置文件,在持久化框架(Hibernate)作为配置连接池等信息
Properties可以读取或写出一个properties文件
方法有:store 写出文件
Load 读取properties文件 注意:在IO流字符流笔记也有该概念和相关代码
public class PropertyDemo {
public static void main(String[] args) throws IOException {
//写一个properties
//writeProperites();
//读取配置文件
loadProperties();
}
//读取一个properties文件 的信息
private static void loadProperties() throws IOException {
//创建输入流对象
InputStream is = new FileInputStream("D:\\JAVASE\\day12\\a.properties");
//创建Properties对象
Properties p = new Properties();
//调用load(instream)
p.load(is);
//调用getProperty(String name) 返回字符串值
String akeyvalue = p.getProperty("a");
System.out.println("akey对应的是:" + akeyvalue);
System.out.println("key对应的value:" + p.getProperty("key"));
System.out.println("p对应的value:" + p.getProperty("p"));
}
//写一个properties文件 出去
private static void writeProperites() throws IOException {
//创建一个输出流
OutputStream os = new FileOutputStream("D:\\JAVASE\\day12\\a.properties");
//创建Properties对象
Properties p = new Properties();
// a = b
p.setProperty("a", "b");
p.setProperty("key", "value");
//写出对象到硬盘上或网络上 要传入一个输出流对象,最后一个实参是注释
p.store(os, "this is a test");
System.out.println("写出文件成功!");
}
}
异常
1.异常概念
异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:
· 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.
2. 异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error与java.lang.Exception,平常所说的异常指java.lang.Exception。
Throwable体系:
· Error
:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
· Exception
:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
Throwable中的常用方法:
·
public void printStackTrace():打印异常的详细信息。
·
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
·
·
public String getMessage():获取发生异常的原因。
·
提示给用户的时候,就提示错误原因。
·
·
public String toString():获取异常的类型和异常描述信息(不用)。
·
出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。
3. 异常分类
我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。
异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?
编译时期异常
:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
运行时期异常
:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)
异常处理有5个关键字
Try:把有可能出错的代码放到try里面进行捕获
Catch:把出错的异常信息包装起来,进行处理
Finally:代码最后执行的代码块
Throws:抛出异常,声明方法的末尾处使用
Throw:在catch或者finally块里面使用
Try…catch组合
Try…finally组合
Try…catch…finally组合
Try…catch{throw}…finally组合
private static void test3() {
Scanner sc = new Scanner(System.in);
System.out.println("先输入一个数字");
int num = sc.nextInt();
System.out.println("再输入另外一个数字:");
int num2 = sc.nextInt();
try {
int num3 = num/num2;
System.out.println(num + "/" + num2 + "=" + num3);
int[] arr = {3,5,8};
System.out.println(arr[3]);
return;
} catch (InputMismatchException | ArithmeticException | ArrayIndexOutOfBoundsException e) {//这种写法不多见,这是并列异常
//e.printStackTrace();//不建议隐藏这错误
e.printStackTrace();// 不建议隐藏这错误,通常是在这里面记录日志 这个出错原因,代码行
}finally {
//不要在这里返回
//最后执行的代码块,一般作为释放释放资源,关闭流,对象
System.out.println("这是最后执行的");
}
}
注意:捕获异常的时候,catch的异常要从小到大 (从小到大一级一级往上抛)
例如:
package com.gec.exp;
import java.io.IOException;
class UserController {
UserService service = new UserService();
public void test() {
try {
service.test();
} catch (Exception e) {
System.out.println("调用service出异常了,有内鬼,结束交易...");
e.printStackTrace();
// 通常是在这里面记录日志 这个出错原因,代码行
}
}
}
class UserService {
UserDao dao = new UserDao();
public void test() throws Exception {
try {
dao.test();
} catch (Exception e) {
System.out.println("service有异常,抛异常给UserController");
throw new RuntimeException("service运行时异常" + e.getMessage());
}
}
}
class UserDao {
public void test() throws IOException {
try {
int num = 5 / 0;
} catch (Exception e) {
System.out.println("dao有异常,抛异常给service");
throw new RuntimeException("dao运行时异常" + e.getMessage());
}
}
}
public class ExceptionDemo2 {
public static void main(String[] args) {
UserController uc = new UserController();
uc.test();
}
}
4. 异常的产生过程解析
5自定义异常
一定是现有的异常类不能够满足项目的实际要求,而自行定义的异常来处理。
如何定义?
1)定义一个类继承RuntimeException
2)定义String类型的消息属性
3)定义一个有参的构造器
例如:
package com.gec.exp;
class RegisterException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String message;
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
/*
* 注册用户,如果用户名已被占用,提示:亲,该用户名已被使用,请重新输入
*/
public class RegisterDemo5 {
//模拟一个数据库
public static String[] names = {"张三","牛美丽","李漂亮","甑好看"};
public static void main(String[] args) {
//调用一个方法检查用户名
try {
checkName("rose");
System.out.println("注册成功");//没有占用名字就会运行这个注册成功
} catch (Exception e) {
e.printStackTrace();
}
}
private static void checkName(String name) {
for (String str : names) {
//判断
if(str.equals(name)) {
throw new RegisterException("亲" + "该名字已被使用了,请重新输入");
}
}
}
}