JavaSE | 接口| 枚举| 注释| 异常
包
1、包的作用:
(1)避免类的同名(区分类);类的全名称:包.类名
例如:java.util.Scanner、 java.util.Arrays、 java.lang.String
(2)可以限定某些类或成员的可见范围;(权限)
如果某个类或成员省略了权限修饰符,它的可见范围是本包
(3)用于组织管理项目中众多的类
2、包的声明的格式:package 包名
声明的要求:
(1)这句package语句必须在“源文件”代码的首行
(2)包名:A:所有单词都小写,每一个单词之间使用.分割; B:一般习惯上用公司的域名倒置加模块名 com.atguigu.xxx;
3、
编译:javac 源文件名.java; ----->>> 编译:javac -d . 源文件名.java
运行:java 类名 ----->>> 运行:java 包.类名
-d:directory文件夹,目录; .:表示当前目录
4、如何使用其他的包?
前提:被使用的类必须是可见的
(1)使用类的全名称
(2)使用import语句先导包,然后使用类的简名称; import 包.类名;
特殊情况:要使用的两个类同名,包名不一样;那就一个导包用简名称,一个使用全名称
* 导包:
* (1)import 包.类名;
* (2)import 包.*;
* (3)import static 包.类名.静态成员;
* (4)import static 包.类名.*;
*
* (3)(4)就是静态导入,JDK1.5之后引入
*
* JDK1.5增加的:
* (1)foreach
* (2)枚举
* (3)注解
* (4)可变参数
* (5)静态导入
* JDK1.8增加的:
* (1)接口的默认方法和静态方法
final关键字
* 关键字:final,最终的 * 它也是一个修饰符 * * 1、它可以修饰什么? * final可以修饰:类(包括外部类和内部类)、变量(包括局部变量、实例变量和类变量)、方法(静态方法和非静态方法); final不能用于修饰构造方法 * * 2、它修饰后有什么不同? * (1)修饰类,即class前面出现了final * 表示这个类是最终类,即这个类不能被继承了,没有子类,它是太监类,没有子孙后代 * 例如:String类 * * (2)修饰方法,即返回值类型前面出现final * 表示这个方法是最终实现版本,即这个方法不能被重写 * * (3)修饰变量,即在变量数据类型前面出现final; 被final修饰的变量只能在变量定义时初始化。 * 表示这个变量的值不能被修改,它是常量,一般要求常量名大写,每个单词之间使用_,常量必须赋值 * * 例如:Math.PI
native关键字
* 关键字:native * 它也是修饰符 * * 1、native可以修饰什么? * 可以修饰方法 * * 2、native修饰的方法是什么意思? * 表示这个方法是一个“原生”的方法,即它的方法体的实现不是用Java语言实现的,是有C/C++等语言实现的, * 实现后编译为.dll文件,然后由Java代码调用。 * * 对应调用者,使用者来说,可以把它当成Java实现的方法一样使用。 * 而且子类可以选择用Java代码重写native方法 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestNative { public static void main(String[] args) { Object obj = new Object(); int hashCode = obj.hashCode(); System.out.println(hashCode); } } class Student{ private int id; private String name; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
接口
* 1、声明一个接口 * 【修饰符】 interface 接口名{ } //类似于类的声明(class)
可以用来修饰interface的有public, static也对; private和protected都不对;接口的内部成员都是public
* 2、接口的成员 * JDK1.8之前: * (1)全局静态的常量 public static final 如 int MAX_SPEED = 20000; * (2)公共的抽象的方法 public abstract 如:void run(); * JDK1.8之后:增加了两个成员 * (1)默认方法 * (2)静态方法 * * 3、如何使用接口? * (1)全局静态的常量:通过接口名直接调用 * (2)抽象方法,由实现类(就是子类)来实现它;;;“子类“要实现接口重写接口的方法,不一定要继承父类。 * * 【修饰符】 class 实现类名 implements 接口们{ * //如果这个类不是抽象类,那么必须实现接口的所有的抽象方法 * } * * 接口可以比喻成“干爹”,可以有好几个 * 继承可以比喻成“亲爹”,只能有一个,所以单继承 * * 4、接口的特点 * (1)接口也是不能直接创建对象 * (2)一个类可以同时实现多个接口 * (3)一个类可以同时继承它的父类,又实现接口,要求必须继承在前,实现在后 * 【修饰符】 class 实现类名 extends 父类 implements 接口们{ * //如果这个类不是抽象类,那么必须实现接口的所有的抽象方法 * } * (4)接口和父类有点像,所以接口的变量与实现类对象可以构成多态引用 * (5)一个实现类实现接口时,必须实现接口的所有的抽象方法,否则这个实现类也得是抽象类 * (6)接口可以继承接口,并且可以继承多个接口。 * 【修饰符】interface 接口名 extends 接口们{ * } * * 关系: * 类与类之间:继承,单继承 extends * 类与接口之间:类实现接口,可以多实现 implements * 接口与接口之间:继承,多继承 extends *
for(int i = 1; i < arr.length; i++){ for(int j = 0; j < arr.length - i; j++){ /* * 假设传入的是Student[]数组,里面都是学生对象 * 如何表示前面的学生对象arr[j]比arr[j+1]大 */ //需要调用arr[j]的compare方法,怎么才能调用compary方法呢, //参数为Object类型向下转型为Student类型,它重写了compary方法 //通过arr[j].compare(arr[j+1])>0,就可以确定arr[j]比较arr[j+1]大 Sortable left = (Sortable) arr[j];//这一步很关键啊 if(left.compare(arr[j + 1]) > 0){ //if(arr[j] > arr[j+1]){ //交换两个元素,temp和元素的类型一样就可以 Object temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }
* JDK1.8之后,接口增加了两个成员: * 1、静态方法 * 和类中的静态方法的格式一样。 * 调用时:通过“接口名."调用即可 * * 原来的核心类库中,有Collection接口和Collections工具类(全部是静态方法), * Path接口和Paths工具类(全部是静态方法) * 后面的工具类是为前面的接口服务的。 * 原来为了为某个接口提供工具类,还要增加一个类,现在就不同了,直接在接口中声明静态方法即可。 * 而且这些静态的实现和接口的实现类是什么类型无关。 * * 2、默认方法 * 语法格式: * 【修饰符】 interface 接口{ * 【修饰符】 default 返回值类型 方法名([形参列表]){ * 方法体 * } * } * * 如何调用? * 必须通过接口的"实现类对象."才能调用 * * 某一个接口的某个抽象方法,可能它的多数实现类对该方法的实现是一样的,那么就没必要每一个实现类都写一遍。 * 把这样的方法实现,提取到接口中声明为默认方法,如果其他不一样的实现类,可以选择“重写”。 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestInterface { public static void main(String[] args) { MyInter.test(); //接口中的静态方法直接 接口名.静态方法; MyImp myimp = new MyImp(); myimp.fun();//通过接口的实现类对象来调用 接口的default默认方法; myimp.method();//通过接口的实现类对象来调用 接口的default默认方法; } } interface MyInter{ public static void test(){ System.out.println("接口中的静态方法直接接口名.来调用哦"); } public default void fun(){ System.out.println("接口中的默认方法fun通过接口的实现类创建对象来调用"); } public default void method(){ System.out.println("接口中默认方法method通过接口的实现类创建对象来调用"); } } class MyImp implements MyInter{ /*@Override //接口中的方法不是抽象方法,可以不用重写; public void fun() { // TODO Auto-generated method stub MyInter.super.fun(); } */ }
/*
* 默认方法的冲突问题:
* 1、当某个类实现了多个接口,多个接口的默认方法的方法签名是一样的
* 必须重写,但是在重写的时候可以选择(1)保留其中一个(2)选择完全重写
*
* 2、当某个类继承了父类,又实现了接口,但是父类中有一个方法和接口中的默认方法的方法签名是一样的
* 默认选择保留“父类”,即子类创建对象默认调用的是父类中的方法,如果子类重写了父类的方法就按照子类重写的方法;
当然也可以重写接口中的方法,就会调用接口中的方法,如 InterD.super.test( ) ;
当然也可以选择重写(1)选择父接口的(2)完全重写
*/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestDefaultMethod { public static void main(String[] args) { Sub s = new Sub(); s.test(); Son son = new Son(); son.test(); //如果子类没有重写,就默认调用父类的 } } interface InterA{ public default void test(){ System.out.println("我是接口A的默认方法test"); } } interface InterB{ public default void test(){ System.out.println("我是接口B的默认方法test"); } } class Father{ } class Sub extends Father implements InterA, InterB{ @Override public void test() { // TODO Auto-generated method stub InterA.super.test();//保留A接口的默认实现 InterB.super.test();//保留A接口的默认实现,也可以实现两个都保留;也可以进行重写; System.out.println("我重写,两个都不要了"); } } interface InterD{ public default void test(){ System.out.println("我是接口D中的默认方法test"); } } class Fu{ public void test(){ System.out.println("我是父类Fu中的方法test"); } } class Son extends Fu implements InterD{ public void test(){ InterD.super.test(); //重写接口D中的方法; 子类重写父类的方法了,就不会调用父类的方法了调它自己的 System.out.println("我是子类Son中的方法test,重写了父类的了"); } }
用实现类代替接口
public class TestInterface { public static void main(String[] args) { //AA aa = new BB(); //需要一个接口,给个实现类即可 AA aa = new CC(); //多态的传递 // AA aa = new BB(); BB bb = new CC(); 构建CC,等同于构建他的父类,父类可以代替接口实现;能代替不代表实现了接口,class CC extends BB implements AA这样子就实现了接口 //System.out.println(aa); System.out.println(CC.class.getInterfaces().length); //CC并没有实现接口AA } } interface AA{ } class BB implements AA{ } class CC extends BB{ }
简单工厂模式
* 简单工厂模式(了解):
* (1)当创建某个对象比较麻烦
* (2)想要与被创建的对象的类型解耦合
*
* 工厂模式的目的:对象的创建者与对象的使用者角色分离
*
* 代码的结构:
* (1)接口
* (2)有接口的很多实现类
* (3)有一个工厂类,专门new接口的各种实现类的对象
* (4)使用接口的实现类对象的地方,通过工厂类之间获取对象,而不直接new实现类对象
*
* 耦合:依赖,关联
*/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestFactory { public static void main(String[] args) { String str = new String(); //TestFactory与String类发生耦合,依赖 //对象的使用者TestFactory //想要获取宝马车的对象; new出BMW对象,接口类型Car b Car c = new BMW();//与BMW发生耦合,对象的使用者和创建者都是TestFactory c.run(); //对象的使用者TestFactory不负责BMW和Aodi对象的创建,与BMW和Aodi类解耦合 Car b = Factory.getCar("宝马"); b.run(); Car a = Factory.getCar("奥迪"); a.run(); } } class Father{ } class Son extends Father{ } interface Car{ void run(); } class BMW implements Car{ @Override public void run() { System.out.println("宝马来了。。。"); } } class AoDi implements Car{ @Override public void run() { System.out.println("奥迪来了。。。"); } } class Factory{ //工厂只负责创建对象;创建一个getCar方法来生产Car的对象 public static Car getCar(String type){ if("宝马".equals(type)){ return new BMW(); }else if("奥迪".equals(type)){ return new AoDi(); } return null; } }
代理模式
* 代理:代购、中介
* 把麻烦,多变交给代理,核心的不易变化的还是自己来做。
*
* 静态代理和动态代理。
*
* 代理模式:(了解)
* 1、接口:主题接口
* 2、被代理者
* 3、代理者
* (1)被代理者和代理者都要实现主题接口。
* (2)要指定代理者为谁代理,即代理者中必须持有被代理者对象的引用
*
* 静态代理:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestProxy { public static void main(String[] args) { DaiGou d = new DaiGou(new Customer()); //用有参构造器,创建DaiGou的对象d,并给他赋值初始化 d.buy(); //构造器中的形参Subject target, 把实参new出Customer对象传给它;最后由d对象来调用方法 } } interface Subject{ void buy(); //抽象方法省略了public abstract } //被代理者 --Customer类实现了Subject接口; class Customer implements Subject{ @Override public void buy() { // 最核心的自己写 System.out.println("一手交钱一首交货"); } } class DaiGou implements Subject{ private Subject target; //类DaiGou的属性,接口类型的引用数据类型targer; 被代理者对象的引用 public DaiGou(Subject target) { super(); this.target = target; //在构造器中给属性targer初始化 } @Override public void buy() { System.out.println("筛选商品"); target.buy(); //一手交钱一首交货 System.out.println("反馈下"); } }
枚举
* 枚举:也是一个种类型,也是一个类
* 1、什么是枚举?
* 中文:枚举 的近义词 列举,穷举,罗列
* Java中,枚举,罗列出该类型的所有对象
*
* 2、什么情况会用枚举?
* 当某个类型它的对象是有限的几个,这个时候就可以使用枚举。
* 3、如何声明枚举类
* JDK1.5之前
* JDK1.5之后
public class TestEnum { public static void main(String[] args) { // Season s = new Season();//构造器不可见 //如何获取春天对象 Season spring = Season.SPRING; 类型 类名 = 类型.静态对象 System.out.println(spring); Season summer = Season.SUMMER; System.out.println(summer); } } //JDK1.5之前 /* * (1)限定使用者创建对象; 2)把有限的几个对象,提前创建好,并存起来,大家共享 * 存对象:肯定用变量才能存; 共享:类变量,静态变量; 随处可用:public */ class Season{ //(2)把有限的几个对象,提前创建好,并存起来,大家共享 public static final Season SPRING = new Season(); public static final Season SUMMER = new Season(); public static final Season FALL = new Season(); public static final Season WINTER = new Season(); //(1)限定使用者创建对象,把构造器私有化 private Season(){ } //可以定义其他的成员 public String toString(){ if(this == SPRING){ return "春眠不觉晓,处处闻啼鸟。"; }else if(this == SUMMER){ return "小荷才露尖尖角,早有蜻蜓立上头"; }else if(this == FALL){ return "停车坐爱枫林晚,霜叶红于二月花。"; }else{ return "千山鸟飞绝,万径人踪灭。"; } } }
* JDK1.5之后: * 1、枚举类的声明格式 * 【修饰符】 enum 枚举类名{ * 枚举类的常量对象列表 【; * 其他成员列表 * 】 * } * * 说明: * (1)如果枚举常量对象列表后面要写其他成员,那么需要在常量对象列表后面加;分隔,如果没有其他成员,那么;可写可不写 * (2)构造器一定是私有化的 * (3)枚举类型的默认父类不是Object,而是java.lang.Enum类,当然Enum也是Object的子类 * 枚举类型不能再继承别的类型 * * java.lang.Enum类: * (1)构造器 * protected Enum(String name,int ordinal) * name是为Enum类中的name属性赋值,自动赋值为常量对象的名称 * ordinal是Enum类中的ordinal属性赋值,自动赋值为常量对象的“序号”,这个序号是从0开始 * * 他们的get方法没有按照普通类的get方法的命名,而是直接用属性名作为get方法的名称:name(),ordinal() * * 因为父类Enum只有唯一的一个有参构造,意味着我们自己写的枚举类的构造器和普通类(默认调用父类的无参构造)不一样, * 默认调用的是父类的有参构造。 * * (2)Enum实现了java.lang.Comparable接口 * 所有的枚举类型都支持比较大小,因为Enum实现了java.lang.Comparable接口,按照常量对象的顺序比较大小。 * 父类中把这个int compareTo()加了final,子类不能重写 * * (3)两个API中没有的方法 * static 枚举类型[] values():用来返回枚举类型的所有常量对象 * static 枚举类型 valueOf(字符串类型的常量对象名) -->> 枚举类名.valueof(“ ”)
Week sunday = Week.SUNDAY; System.out.println(sunday); //对象sunday打印的是 toString的内容; System.out.println(sunday.name()); System.out.println(sunday.ordinal()); System.out.println("----------------"); Week[] values = Week.values(); for (Week week : values) { System.out.println(week); } Week w = Week.valueOf("MONDAY"); System.out.println(w);
System.out.println(Week.valueOf("SUNDAY")); --->> 星期天 //(SUNDAY("星期天"); )
public enum Status { BUSY("忙得热火朝天,无法再安排其他任务"), //创建的对象时候用的是有参构造; FREE(), //用的是无参,默认null VOCATION(), LEFT("离职了"); private String desc; private Status() { } private Status(String desc) { this.desc = desc; } Status busy = Status.BUSY; Employee[] emp = new Employee[5]; emp[0] = new Employee("kris", 10000, Status.BUSY);//busy for (Employee employee : emp) { System.out.println(employee.toString());//employee <==> employee.toString(); }
注释
注解(annotation):它又称为注释,它本质上也是注释,只不过它是代码级别的注释,即它用代码注释代码。
* 因为说注释容易让人误解为单行和多行的普通注释,很多人就故意叫做“注解”
* 注解是JDK1.5之后引入。
*
* 普通的注释:用文字去注释代码
* (1)单行注释
* (2)多行注释
*
* 文档注释:用代码和文字一起去注释代码。
*
* 注解长什么样? @注解
* 一个完整的注解,它应该有三部分组成:
* (1)定义,声明
* 可能是程序员自己声明的,也可能是别人声明好的;* 开发中,绝大多数都是用别人声明好的。
* (2)注解的使用
* 你看到在类上面、方法上面、属性上面....用到了@注解,就是在使用注解
* (3)读取注解
* 得有代码去读取这个注解,理解代码的用意,信息...
* 读取注解需要用代码去读取,这个代码我们称为“注解处理流程”,大多数情况下,这个代码也是别人写好的,
* 一般都是谁定义,谁复制编写读取注解的代码。
*
* 注解在后面的框架中使用最多,它的作用是用来替代xml的配置文件,对代码进行解释。
* 因为xml的配置方式,一个是复杂,另一个与所解释的代码是独立的,所以有的地方不用xml,用注解
常见的注解
* 常见的注解:
*
* 一、系统定义的最基本的三个注解(必须都认识,会写,会用)
* 1、@Override:
* 这个注解的声明是在JDK的核心类库中声明的,
* 这个注解的读取是编译器来读取的,编译器:javac.exe,eclipse中有自己编译器
* 这个注解的使用由程序员来使用,用在所有的重写的方法的上面,表示让编译器,按照重写的要求对该方法进行格式检查和编译。
*
* 重写的要求:
* (1)方法名和形参列表必须相同
* (2)返回值类型
* 如果是基本数据类型和void,必须相同
* 如果是引用数据类型,必须<= 子类重写的方法的返回值类型<=父类被重写的方法的返回值类型
* (3)权限修饰符:必须>= 子类重写的方法的权限修饰符的可见范围>=父类被重写的方法的权限修饰符的可见范围
* (4)抛出的异常列表类型范围:?
* (5)其他修饰符:被重写的方法不能是static,private,final
*
* 2、@SuppressWarings(xx)
* 这个注解的声明是在JDK的核心类库中声明的,
* 这个注解的读取是编译器来读取的,编译器:javac.exe,eclipse中有自己编译器
* 这个注解的使用由程序员来使用,用在任意需要抑制警告的地方
*
* @SuppressWarnings("all")
* @SuppressWarnings({ "rawtypes", "unused" })
*
* 3、@Deprecated
* 这个注解的声明是在JDK的核心类库中声明的,
* 这个注解的读取是编译器来读取的,编译器:javac.exe,eclipse中有自己编译器
* 这个注解的使用由程序员来使用,用在任意需要标记为“已过时”的元素上面,可能是方法,可能是类,
* “已过时”的东西,不建议程序继续使用的,如果使用可能有问题
*
* 删除的行为一定要谨慎。
*
* 编译器如果遇到了程序员使用了已标记为“已过时”的元素时,它会弹出警告
文档注解
二、文档注解,文档注释
* 文档注解的声明,也有JDK核心类库声明过了; * 读取:javadoc.exe来读取
* @author:表示作者
* @version:表示版本
* @since:起始版本
* @see:另请参考
* @param:形参
* @return:返回值
* @throws:抛出的异常列表
*
* @param、@return、@throws只能在方法上使用
* @param 形参名 形参的数据类型 形参的解释
* @return 返回值类型 说明
* @throws 异常类型 说明
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import javax.management.RuntimeErrorException; /** * @author Administrator * @version 1.8 * @since 1.0 * @see java.lang.Object * @see java.lang.Math */ public class TestAnnotation { public static void main(String[] args) { /**java程序主入口 * @param args String[] 命令行参数 */ @SuppressWarnings(value = { "unused" }) int a = 10; } public static int getMax(int a, int b){ /** * 从两个整数中找出最大值 * @param a int * @param b int * @return int 返回最大值 */ return a > b ? a :b; } public static int divide(int a, int b) throws RuntimeErrorException{ /** * 这是一个求两个整数的商 * @param int a * @param int b * @return int * @throws RuntimeException,当b为0时抛出异常 */ if(b == 0){ throw new RuntimeException("除数不能为0"); } return a/b; } @Deprecated public void print(){ System.out.println("过时的方法啊"); } @Override public String toString() { return "TestAnnotation [toString()=" + super.toString() + "]"; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test(){ System.out.println("Hello"); } @Before public void test1(){ System.out.println("hi"); } @After public void test2(){ System.out.println("Test之后"); } @BeforeClass public static void test3(){ System.out.println("嘿嘿"); } @AfterClass public static void test4(){ System.out.println("嘿哈"); }
自定义注释
* 枚举看成是类 * 注解看成是接口 * * 注解: * (1)声明 * (2)使用 * (3)读取 * * 一、自定义注解 * 1、语法格式: * 【修饰符】 @interface 注解名{ * } * * 2、加元注解说明一下 * 元注解:注解注解的注解,给注解加的注解。它们在java.lang.annotation包下 * (1)@Target * @Target(ElementType.xx) * @Target({ElementType.xx,ElementType.xx,...}) * ElementType是一个枚举类型,有很多常量对象:例如:TYPE,METHOD(方法),FIELD(属性),CONSTRUCTOR.... * TYPE(包含 Class, interface (including annotation type), or enum declaration ) * @Target作用:限制自定义的注解的使用的目标位置 * * (2)@Retention * @Retention(RetentionPolicy.SOURCE):源代码阶段 该注解只能被编译器读取 * @Retention(RetentionPolicy.CLASS):字节码阶段 该注解只能被编译器和类加载器读取 * @Retention(RetentionPolicy.RUNTIME):运行时阶段 该注解可以在运行时仍然被读取 * * SOURCE < CLASS < RUNTIME * * @Retention的作用:限制自定义注解的寿命,可以“滞留”到什么阶段 * RetentionPolicy也是枚举类型,常量对象只有三个:SOURCE,CLASS,RUNTIME * * (3)@Documented * 标记某个注解是否可以被javadoc读取到API中。 * * (4)@Inherited * 标记某个注解是否可以被子类继承 * */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.atguigu.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class TestDefineAnnotation { public static void main(String[] args) { //读取MyClass类上面的注解,用到了反射 Class clazz = MyClass.class; MyAnnotation my = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class); System.out.println(my); Class clazz2 = MySub.class; MyAnnotation my2 = (MyAnnotation) clazz2.getAnnotation(MyAnnotation.class); System.out.println(my2); } } //@YoursAnnotation class MyClass{ //@YoursAnnotation //不能在属性上使用,因为它规定了只能使用在METHOD即方法上 @MyAnnotation private String filed; @MyAnnotation @YoursAnnotation public void test(){ } } class MySub extends MyClass{ } @Inherited //标记这个注解可以被子类继承; @Retention(RetentionPolicy.RUNTIME) //可以在运行时阶段读取; @interface MyAnnotation{ //自定义一个注解; } @Documented @Target(ElementType.METHOD) //限制自定义注解使用的目标位置 @Retention(RetentionPolicy.SOURCE) //只能被编译器读取; @interface YoursAnnotation{ }
* 自定义注解 * * 1、语法格式: * @元注解 * 【修饰符】 @interface 注解名{ * } * * * @元注解 * 【修饰符】 @interface 注解名{ * 配置参数 * } * * 没有配置参数的注解: * (1)@Override * (2)@Deprecated * (3)@Documented * (4)@Inherited * (5)@Test * * 有配置参数的 * (1)@SuppressWarnings(xx) * (2)@Target(xx) * (3)@Retention(xx) * (4)@WebServlet(xx) * * 1、配置参数的声明 * (1)声明格式: * 数据类型 参数名(); * (2)配置参数在声明时,可以有默认值 * 数据类型 参数名() default 默认值; 如 String info() default "atguigu"; * (3)数据类型 * 注解的配置参数的数据类型的要求:8种基本数据类型,4种引用数据类型(String,Class,枚举,注解) * 可以是它们的数组类型,例如:int[],String[],枚举类型[]等 * * 2、如果有配置参数,在使用时 *(1) @注解名(参数名1 = 参数值1,参数名2 = 参数值2,。。。。) *(2)如果这个注解只有一个配置参数,并且它的名字叫做value,那么可以省略value= * *换句话说,声明配置参数时,参数名首先考虑value * * 3、读取配置参数值时, 变量 = 注解对象.参数名() * * * 回忆:default * (1)switch * (2)接口 * (3)注解配置参数的默认值 * * 数据类型: * (1)数据类型有哪些: * (2)变量的数据类型: 都可以 * (3)属性的数据类型什么要求: 都可以 * (4)形参的数据类型什么要求: 都可以 * (5)返回值类型的类型有什么要求: 都可以 * (6)switch(表达式)中表达式的类型要求:4种基本数据类型,2种引用数据类 * (7)注解的配置参数的数据类型的要求:8种基本数据类型,4种引用数据类型(String,Class,枚举,注解) * 可以是它们的数组类型,例如:int[],String[],枚举类型[]等 */
public class TestAnnotation2 { public static void main(String[] args) { @SuppressWarnings("rawtypes") Class clazz = TestA.class; AnnotationA my = (AnnotationA) clazz.getAnnotation(AnnotationA.class); System.out.println(my); String str = my.info(); //对象.方法 来进行调用; System.out.println(str); } } @AnnotationA(info = "atguigu", age = 22, value = 88.9) //赋值时像属性,变量 class TestA{ //只有一个参数时可以省略value = } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface AnnotationA{ String info() default "尚硅谷"; //可以有默认值 int age(); //配置参数的声明; char gender() default '男'; double value(); }
单元测试
* JUnit框架不是JRE核心类库,需要单独引入它的jar,类库; eclipse等IDE(集成开发工具)都直接集成了JUnit框架 * * 三、单元测试 * 声明和定义:JUnit框架中定义的* 读取:由java + JUnit框架读取 * * 如何运行? * 如果没有选择,那么当前源文件中的所有的单元测试都会运行 * 如果选中某个方法,只运行某一个; * * @Test: 要先引入import org.junit.Test; *加在需要单独测试的方法。* * @Before 导入 import org.junit.Before; 与之类似的 --->>> @After 是在@Test方法后边最后才执行。 * 加在需要在单元测试方法前面运行的方法上; 如public void test(){ }; *执行的特点:每一个@Test方法前面都会执行这个方法,再执行@Before; * * @BeforeClass @BeforeClass * 加在需要在类初始化时执行的方法上。 如public static void test(){ }; 最先执行,首行位置;* * @AfterClass 类似上边@BeforeClass的用法 * 加在需要在类所有的测试方法之后执行的方法上 *
这四个都有要求:方法所在的类必须是public * 方法本身必须是public,而且是无参无返回值的,静态的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test(){ System.out.println("Hello"); } @Before public void test1(){ System.out.println("hi"); } @After public void test2(){ System.out.println("Test之后"); } @BeforeClass public static void test3(){ System.out.println("嘿嘿"); } @AfterClass public static void test4(){ System.out.println("嘿哈"); }
异常
* 所谓异常:程序大多数情况下是可以正常,正确的运行,但是有的时候因为一些不可控的因素,导致程序运行异常,中断。
* 例如:复制文件时,因为目标盘空间不足; 聊天功能时,因为网络中断;* 计算两个数的商,用户输入的除数为0
1)语法错误; 2)逻辑错误 --->> 它们不是异常。。。
异常的作用:使得系统可以通过捕获异常对象,然后处理异常,使得程序继续运行,否则,如果一旦异常,程序就会挂了。使得程序更健壮。
* Java中是如何处理异常?(原理)
* 当某句代码发生异常时,程序会在这句代码处“停”下来(下面的代码是无法执行的),
然后JVM(大多数是JVM,有的时候也可能是程序中自己new)会创建一个“合适类型的异常的对象”,并且“抛”出来;
JVM会在该句代码的“外围”搜索是否有"try..catch"可以“捕获”这个异常,如果可以“捕获”那么程序从"try..catch"下面继续运行,
如果没有找对应的"try..catch",或者先有的"try..catch"无法“捕获”,
那么程序会把这个异常对象“抛”给“上级”,如果上级可以“捕获”,那么就从“上级”的"try..catch"下面继续运行,
否则接着往上抛,一直到main,如果main也“捕获”不了,就挂了。
异常的类型
* 异常的类型: * java.lang.Throwable:Throwable 类是 Java 语言中所有错误或异常的超类。 * (1)只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。 * (2)只有此类或其子类之一才可以是 catch 子句中的参数类型。 * * “抛”:JVM抛,throw抛 * “抓,捕获”:catch * * 两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。 * 通常,这些实例是在异常情况的上下文中新近创建的,因此包含了相关的信息(比如堆栈跟踪数据)。 * * 分为: * Error:错误,严重的异常,用于指示合理的应用程序不应该试图捕获(catch)的严重问题。 * 例如:VirtualMachineError的两个子类: OutOfMemoryError, StackOverflowError * Exception:异常 * 非受检异常/运行时异常:RuntimeException及其它的子类 * 受检异常/编译时异常:除了运行时异常,剩下的都是编译时异常 * * 编译器在检查程序时,遇到你throw或throws出RuntimeException,编译器不会强制要求你编写try..catch或继续throws处理; * 编译器在检查程序时,遇到你throw或throws出非运行时异常,那么编译器会强制要求你编写try...catch或继续throws否则编译不通过。 *
Throwable是所有错误和异常的父类,超类。
Exception是所有异常的父类,它分为RuntimeException和非RuntimeException。
try..catch..finally形式有三种:1) try..catch; 2) try...catch..finally; 3) try..finally
try...catch
* try...catch * 1、语法格式: * try{ * * 可能发生异常的语句块; * * }catch(异常类型1 异常名){ * 处理该异常的代码; * }catch(异常类型2 异常名){ //catch -->> catch --> 一定是有先后顺序的,从子类---->>>父类,高级的 * 处理该异常的代码; * }catch(异常类型3 异常名){ * 处理该异常的代码; * } * .... * * try..catch的执行过程: * (1)如果try中的代码没有异常,那么try中的代码执行完,正常执行try..catch下面的代码 * (2)如果try中的代码发生异常,首先,try中发生异常那句代码 后面的代码是无法执行,其次,在try中抛出一个 * 异常对象,catch会按从上到下,依次尝试“捕获”,如果有一个“捕获”了,下面的catch就不看。 * 所有要求子异常类型写上面,父异常类型写下面。 * (3)如果try中的代码发生异常,并且所有的catch都没有“捕获”,那么try..catch下面的代码就无法执行了, * 会把异常对象抛出“上级——调用者”,如果已经是最上级(main),就挂了。 * * 这个特点有的像: * if...else if.... * * * * 提示:把字符串类型的数字转成int的值 * Integer.parseInt(字符串)
try...catch...finally
* try...catch...finally * 2、语法格式: * try{ * * 可能发生异常的语句块; * * }catch(异常类型1 异常名){ * 处理该异常的代码; * }catch(异常类型2 异常名){ * 处理该异常的代码; * }catch(异常类型3 异常名){ * 处理该异常的代码; * }finally{ * 无论try中是否发生异常,也不管catch是否能够捕获异常,finally中一定要执行。 * } * * finally块中一般编写:释放资源,断开连接等代码。 * * * 面试题:final,finally,finalize的区别? * final是修饰符,修饰类,表示不能被继承,修饰方法,表示不能被重写,修饰变量,表示值不能被修改是常量。 * finally:是try..catch..finally的一部分,无论try中是否发生异常,也不管catch是否能够捕获异常,finally中一定要执行 * finalize:是一个方法名,在Object类中,表示当对象被“垃圾回收器GC”回收之前调用,有它在就不回收了,只有第一次有效,第二次无效会被gc回收。 * 形式: * try..catch * try..finally * try..catch...finally
public static void main(String[] args) { int num3 = test(); System.out.println("num3 = " + num3); //1 } public static int test(){ int result = 0; try { result = 1/1; System.out.println("result = " +result); //1 return result;//(1)先把1,load到“方法的返回值存放区”(3)再结束当前方法,返回“方法的返回值存放区”的值 } catch (Exception e) { return result; }finally{ result++;//(2)result变量自增,变成2,这个2就不会放到“方法的返回值存放区” //如果变成return result++; 结果还是1 ;如果是return 3; 它就变成3了 System.out.println("result = " + result); //2 }
* 结论:
* (1)finally中的部分无论如何都要执行
* (2)如果finally中有return,以finally中的return语句为主
* (3)如果finally中无return,以try或catch中的return语句为主
*/
考到try...catch...finally和return一起的时候:
(1)finally中代码,无论如何一定要执行,不管是否发生异常,也不管是否catch住异常;
(2)如果finally中没有return语句,那么就先执行finally,然后再回去执行try或catch中的return语句,第二个作用“结束当前方法”,返回值不受finally影响;
(3)如果finally中有return语句,那么就执行finally中的return语句,相当于try和catch中return语句“失效”。
throws
throws:显式声明当前方法中,没有处理的异常,由调用者来处理,特别是编译时异常
如果方法中有产生编译时异常,而又未处理,那么必须加throws声明,否则编译不通过
如果方法中有产生运行时异常,而又未处理,那么可以加throws也可以不加,因为编译期间检测不到运行时异常,编译可以通过。
* throws: * 1、用于在声明方法时,显式的声明当前方法中没有处理的异常,要调用者来处理。 * 调用者在使用有throws的方法时,用try...catch就知道该catch什么更具体异常,否则就按Exception
用上throws(你得知道要抛出的是什么异常如ArithmeticException),写在方法名的后边,你再try...catch.. 选择try里边的代码右键选择Sound with 选择try,
它就知道要抛什么异常了,不然它只会抛Exception异常。 * * 2、语法格式: * 【修饰符】 返回值类型 方法名(【形参列表】)throws 异常列表{ * } * * 异常列表可以写好几个,用,分割 * * 3、关于方法重写 * (1)方法名和形参列表必须相同 * (2)返回值类型 * 基本数据类型和void:必须一致 * 引用数据类型:<= 子类重写方法的返回值类型<=父类被重写方法的返回值类型 * (3)权限修饰符:>= 子类重写方法的权限修饰符的可见性范围 >= 父类被重写方法的权限修饰符的可见性范围 * (4)其他修饰符: 哪些不能重写 static,final,private * (5)抛出的异常列表的类型:<= 子类重写方法抛出的异常类型 <= 父类被重写方法抛出的异常类型 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TestThrows { public static void main(String[] args) { try { divide(1, 0); } catch (ArithmeticException e1) { e1.printStackTrace(); } try { copy("1.txt", "2.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); } Father f = new Son();//多态引用 try { f.test(); //编译时按照父类Father,运行时按子类Son,子类抛出RuntimeException异常,Exception要能够捕获到 } catch (Exception e) { //反过来按照子类抛出的Exception,这个时候发现catch就捕获不了 e.printStackTrace(); } } public static int divide(int a, int b) throws ArithmeticException{ return a/b; } public static void copy(String srcFilePath, String destFilePath) throws FileNotFoundException{ FileInputStream fis = new FileInputStream(srcFilePath); FileOutputStream fos = new FileOutputStream(destFilePath); } } class Father{ public void test()throws Exception{ } } class Son extends Father{ public void test() throws RuntimeException{ //子类的抛出的异常类型要比父类的小 //要保证子类抛出的异常父类能够接住 } }
throw用于手动抛出异常对象
* 大多数异常对象是JVM根据情况抛出。有的时候需要手动抛出异常,
* throw:用于手动抛出异常对象
* 语法结构:
throw 异常对象;
可以代替return语句,即带回异常信息,而且可以结束方法的执行
public class TestThrow { public static void main(String[] args) { Account account = new Account(); try { account.withdraw(100); System.out.println("取款成功"); } catch (Exception e) { System.out.println(e.getMessage()); // 打印的是条件不满足时抛出的异常,给用户看的 //e.printStackTrace(); //直接打印出红色异常信息,给开发者看的; } System.out.println("余额为:" + account.getBalance()); } } class Account{ private double balance = 1000; public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public void withdraw(double amount){ if(amount < 0){ //Illegal不合法,Argument参数 IllegalArgumentException e = new IllegalArgumentException("取款金额输入有误"); throw e; }if(amount > balance){ throw new RuntimeException("余额不足"); } balance -= amount; } }
自定义异常:
* (1)要继承Throwable或它子类
* 一般都继承Exception(编译时异常),或者RuntimeException(运行时异常)
* (2) 异常名要见名知意
* (3)尽量保留父类的两个构造器:一个是无参构造,一个是为message赋值的构造器
* (4)自定义异常必须手动用throw来抛出,JVM无法给你自动抛出
*
* 一个异常:
* (1)类型要见名知意
* 例如:ArrayIndexOutOfBoundsException
* ClassCastException
* FileNotFoundException
*
* (2)自己的message消息
* 异常对象名.getMessage()可以获取
(3)堆栈跟踪信息
* 异常对象经历的抛出路线,从哪个方法到哪个方法
* 异常对象名.printStackTrace(); //Trace痕迹
* System.err:异常信息,默认是红色的
* System.out:普通信息
* System.err和System.out是两个线程,谁先抢到谁先打印
public class TestMyException { public static void main(String[] args) { try { regist("admin", 22, "123@qq.com"); System.out.println("注册成功"); } catch (UsernameExistException e) { e.printStackTrace(); System.out.println(e.getMessage()); } } public static void regist(String username, int age, String email) throws UsernameExistException{ //做一个假的注册,假设我的数据库已经存"admin" if("admin".equals(username)){ throw new UsernameExistException("用户名已存在"); } } } class UsernameExistException extends Exception{ public UsernameExistException(){ super(); } public UsernameExistException(String message){ super(message); } }
异常类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//下标越界 public class TestArrayIndexOutOfBoundsException { public static void main(String[] args) { int[] arr = new int[0]; try { System.out.println(arr[0]); } catch (Exception e) { e.printStackTrace();//标准的异常信息的打印方法,信息比较全,(1)类型(2)message(3)堆栈跟踪信息 //或 System.out.println(e.getMessage());//只有message } } }
运行时异常
异常栈e.printStank
NullPointerException:空指针异常,当对象是null,却用对象调用方法,属性等。 ArrayIndexOutOfBoundsExceptiom:数组下标越界异常,当下标的值超过[0,数组的长度-]的范围 ClassCastException:类型转换异常,当对象不是该类型的实例时 NumberFormatException:数字格式化异常,当把一个非数字的字符串转为数字 ArithmeticException:算术异常,例如当除一个0时
Java可能导致内存泄露的错误代码或者用简单代码演示内存溢出
public class TestOutOfMemoryError { public static void main(String[] args) { Object[] arr = new Object[Integer.MAX_VALUE]; System.out.println("Integer.MAX_VALUE =" + Integer.MAX_VALUE); } } java.lang.OutOfMemoryError: Requested array size exceeds VM limit
---------------------------------------------------------------------------------- public class TestStackOverflowError { public static void main(String[] args) { test(); } public static void test(){ test(); } } java.lang.StackOverflowError
错误异常从最下看
最后一个Caused by :xxx,然后再从下往上