基础篇
# 命令行
cmd:打开DOS命令行;在文件夹地址栏处输入cmd→在DOS命令行打开该文件夹
dir:查看当前目录下所有的内容
cls:清屏
# 标识符定义规则
- 由数字、字母、下划线(_)、和美元符($)
- 不能以数字开头
- 不能是关键字
- 区分大小写
# &|与&&||的区别
| 符号 | 作用 | 说明 |
|---|---|---|
| && | 短路与 | 作用和&相同,但是有短路效果 |
| || | 短路或 | 作用和|相同,但是有短路效果 |
- 逻辑与 &,无论左边真假,右边都要执行。 短路与 &&,如果左边为真,右边执行;如果 左边为假,右边不执行。
- 逻辑或 |,无论左边真假,右边都要执行。 短路或 ||,如果左边为假,右边执行;如果 左边为真,右边不执行。
# 数组初始化方式
分为动态初始化及静态初始化:
静态初始化:int[] arr = new int[]{1,2,3,4} int[] arr = {1,2,3,4}
动态初始化:int[] arr = new int[10]
动态初始化只能通过a[0]亦或是遍历数组进行赋值
# for循环
在嵌套循环内部使用break,只能结束内部的循环,如果要结束外部循环需要添加标识,如下:
A: for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
break A;//此时结束的是外部的循环
}
}
2
3
4
5
continue:跳过此次循环 break:结束当前循环 return:结束循环所在的当前方法
# Debug
F8:只查看main方法的执行流程 F7:可以看到自定义方法的执行流程 Alt+Shift+F7:可以看到源码的执行流程
# String
创建字符串对象的区别对比
String s1 = new String("abc");
使用构造方法创建对象,每一次new都会申请一个新的内存空间,此时是通过地址值找这个String对象;
String s1 = "abc";
使用“ ”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串常量池中维护,此时是在字符串常量池中找这个唯一的对象;也就是说当出现第二条String s2 = "abc";语句,这时不是新建,而是复用。
注意:JDK1.7开始字符串常量池从方法区移动到了堆内存。
有以下示例:
String s1 = "abc";
String s2 = "abc";
s1 == s2;//true
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2;//false
2
3
4
5
6
7
8
方法
| 方法名 | 说明 |
|---|---|
| public boolean equals(Object anObject) | 比较字符串的内容,严格区分大小写 |
| public boolean equalsIgnoreCase(String anotherString) | 比较字符串的内容,忽略大小写 |
| public String substring(int beginIndex, int endIndex) | 根据开始和结束索引进行截取,得到新的字符串(包含头,不包含尾) |
| public String substring(int beginIndex) | 从传入的索引处截取,截取到末尾,得到新的字符串 |
| public String replace(CharSequence target, CharSequence replacement) | 使用新值,将字符串中的旧值替换,得到新的字符串 |
| public String[] split(String regex) | 根据传入的规则切割字符串,得到字符串数组 |
| boolean startsWith(String prefix) | 检查字符串是否以指定前缀开头 |
| boolean startsWith(String prefix ,int toffset) | 检查在索引开始处字符串是否以指定前缀开头 |
| boolean endsWith(String suffix) | 检查字符串是否以指定前缀结尾 |
注意:public boolean equals(Object anObject)这个方法只有跟字符串比较才会实现内容的比较,如果跟其他的类型是不比较的,会直接返回false。
# 字符串的拼接
String的拼接实际上会分为三步:创建StringBuilder对象,使用append方法,再调用toString方法返回拼接好的字符串;也就是说字符串的拼接,拼接一次就要创建两个新对象,即耗时又耗空间。
# StringBuilder的字符串拼接实现
StringBuilder跟String相比,StringBuilder是可变的,并且在拼接字符串的时候不会创建新的对象。其实这个说法并不准确,StringBuilder在拼接字符串时还是会开辟新的内存空间。当创建StringBuilder对象时,会创建一个容器,一个容量为16的byte数组,调用append方法会将字符串放入这个数组中。当数组满了,就会创建一个更大容量的数组,将旧数组copy过去,然后将新的字符串存入数组,从而达到字符串拼接的效果。
# 访问权限修饰符
| 同一个类 | 同一个包 | 不同包子类 | 不同包非子类 |
|---|---|---|---|
| public | √ | √ | √ |
| Protected | √ | √ | √ |
| Default | √ | √ | x |
| private | √ | x | x |
# static关键字
只要被静态修饰了的,在内存中都只有一份,也是最优先被加载进内存的。
static的使用场景
- 如果某个成员变量希望被共享,则定义成静态成员,只有一份。
- 如果该方法是以执行一个通用功能为目的,且需要共享及方便访问,既可以声明成静态方法。
注意:
- 静态方法只能访问静态的成员,不可以直接访问实例成员。
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中是没有this关键字
# native关键字
方法使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,一般是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。
native 是用做java 和其他语言(如c++)进行协作时用的,也就是native 后的函数的实现不是用java写的
native的意思就是通知操作系统,这个函数你必须给我实现,因为我要使用。所以native关键字的函数都是操作系统实现的,java只能调用。
java是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就要一些其他语言的帮助,这个就是native的作用了
# 类成员变量与属性
java类的成员变量和属性是一样的,这个说法并不准确,具体得看使用情境。
属性的官方定义:属性是指get或者set方法名,去掉get或者set后,把剩余的部分首字母改为小写后,即为这个类的属性。
一个类拥有自己的属性以及行为,属性即成员方法外的变量,行为即成员方法。
这种说法的成立在程序员间不约而成的一种定义规范:我们定义一个javabean默认都是将类成员私有,然后提供每一个成员变量对外的公共接口,即get和set方法。类的反射就是通过get和set方法获取成员属性的,所以我们默认将成员变量都看作为了属性,于是就有了属性即成员方法外的变量这种说法。
# 继承
定义:将多个类的相同属性和行为抽取到单独一个类中。
为什么要先执行父类构造器?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
**注意:**如果父类没有无参构造,会报错,要手动在子类super.调用父类的有参构造。
子类构造器如何调用到父类构造器?
子类构造器的第一行语句默认都是:super(),不写也存在。
引用传递与值传递的区别?
首先引用传递与值传递传递的都相当于一个副本,实参和形参其实是互不相关的,但在引用传递中,形参是怎么影响实参的呢?是因为形参在拿到实参传递的地址值后,对地址值存放的数据进行了操作,所以导致实参的数据出现了变化。但要注意String 在引用传递中比较特殊。
String作为参数传递时,形参会不会影响实参的值?
String虽然是引用类型,但是不会因为形参的改变而影响实参,首先因为形参和实参是互不相关的,形参拿到的是实参传递的地址值,等于是形参和实参都指向了同一个地址,当形参指向的地址发生改变,只要方法内不return形参的地址赋值给实参,那么实参是不会受到影响的。
# 接口与抽象类
JDK8之前接口中只允许定义常量和抽象方法,JDK8后为优化版本更新带来的接口实现类维护繁琐,接口中允许定义默认方法以及静态方法(注意:静态方法必须用接口本身的接口名来调用)。也就是能够做到让接口的实现类只重写想要使用的接口方法,当实现类重写了接口方法后则使用重写的方法,否则使用接口中默认的方法实现。JDK9后接口中允许定义私有方法,只能在本类中被其他默认方法或者私有方法访问。
JDK9后接口就与抽象类的区别
接口:
- 成员变量只能定义常量
- 方法默认修饰符是public
- 允许定义静态方法、默认方法
- 不允许定义构造方法
抽象类:
- 普通类能定义什么抽象类也能定义
- 与普通类的区别就在于存在抽象方法
# 多态
父类 变量名 = new 子类()
方法的调用编译看左边,运行看右边:指的是编译左边父类的成员方法,运行调用子类与父类共有的,也就是说子类特有的方法无法调用,因为编译时加载的是父类,父类中没有子类特有的方法,所以没有加载进内存。运行看右边是指共性方法的实现根据创建的子类的不同而变。
变量的调用编译看左边,运行看左边:变量是没有多态性的
多态的优劣势
在多态形式下,右边对象可以实现组件化切换。
Animal a = new Dog();
a.run(); // 后续业务行为随对象而变,后续代码无需修改
2
便于扩展和维护。可以实现类与类之间的解耦。
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,更能体现出多态的扩展性与便利。但是,多态下不能使用子类特有的功能。
弊端:无法调用子类特有方法
# 工具类原理和延伸
对于一些应用程序中都有的共性的功能,可以将这些功能进行抽取成静态方法,独立封装成工具类,以便其他方法调用。作用:一是方便逻辑调用,二是提高了代码复用。
工具类是为了方便直接用类使用,但是该类还是可以创建对象,应强制该类不能创建对象,建议将构造器私有。
# 接口
JDK8之前接口中只允许定义常量和抽象方法,JDK8后为优化版本更新带来的接口实现类维护繁琐,接口中允许定义默认方法以及静态方法。(注意:静态方法必须用接口本身的接口名来调用)。JDK9后接口中允许定义私有方法,只能在本类中被其他默认方法或者私有方法访问。
# final关键字
final 修饰的特点
修饰类:表明该类是最终类,不能被继承。
修饰方法:表明该方法是最终方法,不能被重写。
修饰变量:表明该变量是常量,表示该变量第一次赋值后,不能多次被赋值。
# 内部类
成员内部类:定义在类中方法外的内部类;字节码文件格式:外部类名$内部类名.class
局部内部类:定义在方法内的内部类;字节码文件格式:外部类名$数字(1,2,3...)内部类名.class
匿名内部类:格式: ↓ 字节码文件格式:外部类名$数字(1,2,3...).class
接口/父类 = new 接口的实现类(){
@Override
重写的方法;
}
--------------------------
Runable是个接口
Runable run = new Runable()#{//#是被隐藏的实现类名,也就是所谓的匿名,实现了Runable接口
@Override
public void running(){
System.out.println("跑");
}
};
run.running();
2
3
4
5
6
7
8
9
10
11
12
13
# 枚举类型底层
- 枚举类本质上就是一个类,继承了Enum;
- 枚举类定义的枚举项,其实就是用public static final 修饰了的枚举类对象,并且用了静态代码块进行初始化,也就是将成员修饰成了常量。
- 枚举类的构造方法用private修饰,意味着我们无法实例化这个类,也就是不能创建这个类的对象,只能通过类名.成员变量的方式使用该类的成员变量。
- 枚举是一种多例模式,有多少个枚举项就有多少例
- 当枚举项只有一项时,这个枚举类就是单例模式
public enum Gender{
MALE,FEMALE;
}
-------------------------------------
public final class Gender extends Enum {
public static final Gender MALE;
public static final Gender FEMALE;
private Gender(String s, int i)
{
super(s, i);
}
static
{
MALE = new Gender("MALE", 0);
FEMALE = new Gender("FEMALE", 1);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 常用API
# System类
| 方法 | 描述 |
|---|---|
static long currentTimeMillis() | 返回当前时间(以毫秒为单位)。 |
static void exit(int status) | 终止当前运行的Java虚拟机。 |
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) | 将指定源数组中的数组从指定位置复制到目标数组的指定位置。 |
- exit(0)正常退出,非0传参异常退出,主方法内所有代码运行完会默认调用该方法。
- currentTimeMillis()用于程序耗时记录(毫秒)。从1970年1月1日,0时0分0秒开始。
- arraycopy(源数组,源数组复制开始索引,目标数组,目标数组从哪个位置开始存放,复制的数量)很多容器的扩容方式底层都是用的这个方法。
# Object类
常用方法
| 方法名 | 说明 |
|---|---|
| public String toString() | 返回对象的地址值,建议所有类都重写该方法。 |
| public boolean equals(对象) | 比较对象是否相等,默认使用==比较两个对象地址。 |
Object类的equals()方法
Object类的equals方法底层实现也是用"=="比较的,所以默认比较的也是对象的地址值。
之所以String和集合中的Integer能直接比较值是因为重写了equals和hashcode方法,当我们需要将自定义的对象参与比较时就需要重写对象的equals和hashcode方法。
**扩展:**Object类的equals方法允许与null进行比较,但是不允许null调用equals方法,因为null没有equals方法,会报空指针异常,而Objects类中的equals方法允许两个null进行比较。
public void Demo1() {
Student s1 = null;
Student s2 = new Student();
System.out.println(s2.equals(s1));
System.out.println(Objects.equals(s1, s2));
System.out.println(Objects.equals(s1, null));
}
运行结果:
false
false
true
2
3
4
5
6
7
8
9
10
11
12
# Objects类
常用方法
| 方法名 | 说明 |
|---|---|
| public String toString() | 返回对象的地址值,建议所有类都重写该方法。 |
| public boolean equals(对象) | 比较对象是否相等,默认使用==比较两个对象地址。 |
# Math类
主要用于对数据进行处理。
常用方法
| 方法名 | 说明 |
|---|---|
| public static int abs(int a) | 返回参数的绝对值 |
| public static double ceil(double a) | 向上取整 |
| public static double floor(double a) | 向下取整 |
| public static int round(float a) | 四舍五入 |
| public static double pow(double a,double b) | 返回a的b次幂的值 |
# BigDecimal类
计算机进行浮点数运算时需要将浮点数转换成二进制再进行运算,就可能会出现10/3这种除不尽的情况,所以就会导致出现精度丢失,例如:
double a = 0.1 + 0.2; //0.300000000000004
BigDecimal类就是用来解决数学运算的精度问题的,可以表示任意精度的数字。
当需要存储远超long精度的数字,就可以使用BigDecimal。
BigDecimal 类构造方法
| 构造方法 | 说明 |
|---|---|
| BigDecimal(double val) | 参数为double(不推荐) |
| BigDecimal(String val) | 参数为String(推荐使用) |
BigDecimal 类的常用方法
| 方法名 | 说明 |
|---|---|
| public BigDecimal add(另一个BigDecimal对象) | 加法 |
| public BigDecimal subtract (另一个BigDecimal对象) | 减法 |
| public BigDecimal multiply (另一个BigDecimal对象) | 乘法 |
| public BigDecimal divide (另一个BigDecimal对象) | 除法(除得尽的情况) |
| public BigDecimal divide (另一个BigDecimal对象,保留位数,RoundingMode舍入模式) | 除法(除不尽的情况) |
**总结:**对数据进行处理用Math,求运算精度用BigDecimal
# SimpleDateFormat
SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。是DateFormat的子类,它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
| 构造器 | 说明 |
|---|---|
| public SimpleDateFormat() | 构造一个SimpleDateFormat,使用默认格式 |
| public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat,使用指定的格式 |
| 方法 | 说明 |
|---|---|
| public final String format(Date date) | 将日期格式化成日期/时间字符串 |
| public Date parse(String source) | 从给定字符串的开始解析文本以生成日期 |
规范:
示例:
# Calendar日历类
对象创建
Calender是一个抽象类,不能直接创建对象,需要通过静态的getInstance() 方法创建子类对象。
Calender c = Calender.getInstance();//通过多态创建实例
Calendar类相对于Data类来说,功能更加强大,虽然获取时间可能会变得稍微繁琐一点,但是时间的设置会变得非常便利。你在系统日历中能进行的操作,基本上都可以通过这个类完成。
常用方法
| 方法 | 说明 |
|---|---|
| get(int field) | 返回指定日历字段的值。 |
| set(int field, int value) | 设置指定日历字段的值。 |
| add(int field, int amount) | 添加或减去某个日历字段的值。(向前向后一百天) |
| getTime() | 获取Date对象。 |
| getTimeInMillies() | 返回该日历时间的毫秒值。 |
**int field**字段常量 例如:**Calendar.YEAR**
| 字段值 | 含义 |
|---|---|
| YEAR | 年 |
| MONTH | 月(月份从0开始,0~11 表示 1月~12月) |
| DATE(或 DAY_OF_MONTH ) | 日 |
| HOUR | 时(12小时制) |
| HOUR_OF_DAY | 时(24小时制) |
| MINUTE | 分 |
| SECOND | 秒 |
| DAY_OF_WEEK | 周几(数字 1~7 表示 周日~周六) |
# Collections
常用方法
| 方法 | 说明 |
|---|---|
| addAll(Collection c, T... elements) | 将所有指定的元素添加到指定的集合c中。 |
| shuffle(List<?> list) | 随机打乱list集合中元素的顺序。 |
| sort(List list) | 根据自然顺序对list集合的元素进行升序排序。 |
| sort(List list, Comparator c) | 根据指定的比较器,对list集合元素进行自定义排序。 |
**补充:**Comparator接口重写compare()方法,比较规则与compareTo()方法一致:
- 如果返回值为负数,认为当前存入的元素是较小值,存左边。
- 如果返回值为正数,认为当前存入的元素是较大值,存右边。
- 如果返回值为0,认为两个元素一样,不存入集合。
# File类
常用构造方法
| 方法 | 说明 |
|---|---|
| File(String pathname) | 通过路径名字符串创建File对象 |
| File(String parent, String child) | 从父路径名字符串和子路径名字符串创建File对象 |
| File(File parent, String child) | 从父抽象路径名和子路径名字符串创建File对象 |
路径斜杠问题:\ 在字符串里要写两个 \ Windows系统支持 \ 和 / Linux只支持 /
**注意:**File对象相当于一个路径,路径可以是存在的也可以是不存在的
常用方法
| 方法 | 说明 |
|---|---|
| public boolean delete() | 删除文件或文件夹 |
| public boolean createNewFile() | 创建一个新的文件 |
| public boolean mkdir() | 创建一个单级文件夹 |
| public boolean mkdirs() 推荐使用 | 创建一个单级或多级文件夹 |
| public boolean isDirectory() | 判断是否是文件夹 |
| public boolean isFile() | 判断是否是文件 |
| public boolean exists() | 判断是否存在 |
| public long length() | 获取文件大小,单位字节 |
| 路径 | |
| public String getAbsolutePath() | 返回File对象的绝对路径 |
| public String getName() | 获取名字(路径最后一个\\右边的) |
| public String getParent() | 获取父路径(路径最后一个\\左边的) |
| 目录 | |
| String[] list() | 获取文件夹里面的内容,以字符串的形式返回 |
| public File[] listFiles() | 获取文件夹里面的内容,以File对象的形式返回 |
注意:
- delete()方法删除是不走回收站的,是直接删除,若删除的是文件夹,需清空文件夹内容才能删除
- length()方法无法获取文件夹的大小
- list()和listFiles()方法只有路径是文件夹的File对象才能调用,如果是文件调用listFiles()会返回null
路径
**绝对路径:**从盘符开始寻找 如:D:/Mytestfile/aaa
相对路径:不带盘符,默认从当前项目所在文件夹找 如:模块名/aaa
案例
**需求:**搜索某个文件夹中的所有java文件,如果文件夹中有文件夹也要找出来
**思路:**通过递归实现
代码:
public class Testcode {
public static void main(String[] args) throws Exception {
findjava(new File("D:\\Develop\\MyFileTest"));
}
public static void findjava(File dir){
// 获取目录
File[] files = dir.listFiles();
// 遍历目录
for (File file : files) {
// 判断是否是文件
if (file.isFile()){
// 判断是否是java文件
file.getName().endsWith(".java");
// 打印
System.out.println(file);
}else {
// 是文件夹则递归查找
findjava(file);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**提示:**Windows很多操作底层也是通过递归实现的,如:删除文件夹、检索文件、计算文件夹大小
UUID.randomUUID().toString();需要文件名不重复时
# 包装类的自动装拆箱底层实现
底层其实就是调用了ValueOf(基本数据类型)和基本数据类型inValue()这两个方法
Integer integer = 10;
int i = integer;
---------------------------------------
Integer integer = Integer.valueOf(10);
int i = integer.intValue();
2
3
4
5
字符串转基本数据类型:static int parseInt(String s);
# 可变参数
格式:
修饰符 返回值类型 方法名(参数类型 ... 形参名) {
}
2
3
范例:
public static void getnum(int… a) {
}
----------------------------------
// 可变参数底层是数组,可以看作成
public static void getnum(int[] a){
for(int i = 0;i < a.length;i++){//所以这个a是能作为数组遍历的
System.out.println(a[i]);
}
}
2
3
4
5
6
7
8
9
10
调用:
sum(1);
sum(1,2);
sum(1,2,3);
2
3
注意
- 可变参数本质是数组。
- 一个方法只能有一个可变参数。
- 如果方法中有多个参数,可变参数要放到最后。
# 正则表达式
String类中支持正则表达式的方法:
| 方法 | 说明 |
|---|---|
| boolean matches (String regex) | 判断该字符串是否匹配指定的正则表达式。 |
| String replaceAll (String regex, String replacement) | 将该字符串中所有匹配正则表达式的内容替换成新的字符串,并返回替换后的新的字符串。 |
| String[] split (String regex) | 根据匹配规则,把字符串分割成多个子串。 |
范围匹配
[abc]:匹配abc中任意一个字符。
[a-z]:匹配小写字母a-z中的一个。
[A-Z]:匹配大写字母A-Z中的一个。
[0-9]:匹配数字0-9中的一个。
[a-zA-Z0-9]:匹配a-z或者A-Z或者0-9之间的任意一个字符。
[a-dm-p]: 匹配a-d或m-p之间的任意一个字符。
[^abc]:匹配除a、b、c之外的任意一个字符。
[^a-z]:匹配除小写字母外的任意一个字符。
2
3
4
5
6
7
8
预定义字符
“.” : 匹配一个任意字符
"\d“: 匹配一个数字字符,相当于[0-9]
"\D“: 匹配一个非数字,相当于[^0-9]
“\s“: 匹配一个空白字符
"\S“: 匹配一个非空白字符
"\w“: 匹配一个单词字符,包括大小写字母,数字,下划线,相当于[a-zA-Z0-9_]
"\W“: 匹配一个非单词字符
---------------------
java中需要多一个\进行转义,即\\d
2
3
4
5
6
7
8
9
数量词(限定符)
? 0次或1次
* 0次或多次 (任意次)
+ 1次或多次
{n} 重复n次
{n,} 重复n次以上 (至少n次)
{n,m} 重复n到m次(包括n和m)
2
3
4
5
6
逻辑匹配
&& :并且(交集)
| :或者(并集)
2
括号分组
正则表达式中用小括号()来做分组,也就是括号中的内容作为一个整体。
示例
/*
要求:
邮箱地址必须是6~18个字符,可使用字母,数字,下划线,需要以字母开头。
邮箱后缀支持 @163.com 或者 @126.com 或者 @yeah.net。
*/
public class Demo1 {
public static void main(String[] args) {
String email = "itheima@yeah.net";
boolean result =
email.matches("[a-zA-Z]\\w{5,17}@(163\\.com|126\\.com|yeah\\.net)");
System.out.println(result);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 注解
概述
Annotation表示注解。是JDK1.5的新特性。
注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。
**注解的主要作用:**对类进行标记。通过注解可以给类增加额外的信息。
**注:**java源码中有很多没有属性的空注解,起到标记的作用
JDK的内置注解
- @Override:限定重写父类的方法,该注释只能用于方法
- @Deprecated:用于表示某个程序元素(类,方法等)已过时
- @SuppressWarnings:抑制编译器警告
# 自定义注解
自定义注解格式:
public @interface 注解名称 {
public 属性类型 属性名();
}
2
3
允许存放的属性类型:基本数据类型``String``Class``注解``枚举以及以上类型的一维数组
注意:
- 注解的属性不允许存放自定义类型。
- 同一个位置不能放同一个注解
自定义注解的使用
public @interface MyAnno2 {
// 属性和类中的成员变量一样,可以保存数据
public String name();
public double price() default 88.88; // 设置默认值
}
----------------------------------
public class Demo11 {
@MyAnno2(name = "水浒传", price = 9.9)
public static void main(String[] args) {
}
}
2
3
4
5
6
7
8
9
10
11
注意:
- 使用自定义注解时要保证注解每个属性都有值
- 注解可以使用默认值
- 当注解中只有"一个属性",并且属性名是"value",使用注解时,可以省略value属性名
# 元注解
概念:修饰注解的注解被称为元注解
| 注解 | 说明 |
|---|---|
| @Target | 指定注解能在哪里使用 |
| @Retention | 可以理解为保留时间(生命周期) |
| @Documented | 添加javadoc中的描述 |
| @Inherited | 指该注解会被子类继承 |
使用格式
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
2
3
4
元注解@Target
**作用:**用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
可使用的值定义在ElementType枚举类中,常用值如下:
- TYPE,类,接口
- FIELD, 成员变量
- METHOD, 成员方法
- PARAMETER, 方法参数
- CONSTRUCTOR, 构造方法
- LOCAL_VARIABLE, 局部变量
元注解@Retention
**作用:**用来标识注解的生命周期(有效范围)
可使用的值定义在RetentionPolicy枚举类中,常用值如下:
- SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS:注解作用在源码阶段,字节码文件阶段(会存在编译后的字节码文件中),运行阶段不存在,默认值
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
SOURCE(源代码) CLASS(字节码) RUNTIME(运行时)
xxx.java -> javac编译 -> xxx.class -> java运行 -> 程序运行
# 注解解析
概念:获得注解上数据的过程则称为注解解析。
与注解解析相关的接口
AnnotatedElement:该接口定义了与注解解析相关的方法
AnnotatedElement接口常用方法
| 方法 | 说明 |
|---|---|
| T getAnnotation(Class annotationClass) | 根据注解类型获得对应注解对象 |
| Annotation[] getAnnotations() | 获得当前对象上使用的所有注解 |
| boolean isAnnotationPresent(Class annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
# 单元测试
Junit介绍
JUnit 是一个 Java 中常用的单元测试工具。
JUnit 可以很方便的对Java中的方法进行测试,提高测试效率。
**注:**JUnit是第三方的工具,需要导包
Junit常用注解(Junit 4.xxxx版本)
| 注解 | 说明 |
|---|---|
| @Test | 单元测试方法 |
| @Before | 在每个测试的方法前运行 |
| @After | 在每个测试的方法后运行 |
| @BeforeClass | 在所有测试的方法前运行一次 |
| @AfterClass | 在所有测试的方法后运行一次 |
Junit常用注解(Junit 5.xxxx版本)
| 注解 | 说明 |
|---|---|
| @Test | 单元测试方法 |
| @BeforeEach | 在每个测试的方法前运行 |
| @AfterEach | 在每个测试的方法后运行 |
| @BeforeAll | 在所有测试的方法前运行一次 |
| @AfterAll | 在所有测试的方法后运行一次 |
**@Test要求:**必须是public修饰、返回值必须是void、不能有参数
常用方法
| 方法 | 说明 |
|---|---|
| void assertEquals(String,long,long) | 判断是否是期望值(提示信息,期望值,实际值) |
# 异常
错误:是指程序正常情况下,不应该出现的非正常情况,通过代码无法处理的;比如:电源断电了
异常:是指程序可能会出现的不正常的情况,程序员能够处理的
程序在执行过程中,出现的非正常的情况,最终会导致 JVM 的非正常停止。
注意:语法错误不算在异常体系中。
异常体系

**编译时异常:**就是在编译的时候出现的异常。
**运行时异常:**就是在运行时出现的异常。

异常的产生过程解析
- JVM调用main方法进入程序
- 执行代码发现异常
- 产生异常信息
- 检查当前代码处是否有处理异常
- 无则将异常传递上级,即main方法
- main方法也没有处理异常
- 传递给main方法调用者JVM
- JVM收到异常,打印异常信息,停止程序
异常对象的创建—throw
格式:throw new 异常类名();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入年龄:");
int age = sc.nextInt();
if (age<0){
throw new RuntimeException("年龄不能为负数");
}else {
System.out.println("age = " + age);
}
}
2
3
4
5
6
7
8
9
10
运行结果:
请输入年龄:-1
Exception in thread "main" java.lang.RuntimeException: 年龄不能为负数
at Test.Testcode.main(Testcode.java:6)
2
3
4
5
6
# Java中两种异常的处理方式
- throws 抛出异常,把异常抛给调用者处理(方法内即抛给方法,没有则继续向上抛,最终是抛给JVM)
格式:**修饰符返回值类型 **方法名(参数) throws 异常类名1, 异常类名2{ }
- try_catch_finally 捕获异常,处理异常
格式:
try {
可能出现异常的代码;
} catch (异常类名1 e) {
处理异常的代码;
} finally {
代码;
}
2
3
4
5
6
7
注意:
try_catch_finally方式catch在语法上是允许省略的,但是catch和finally不能同时省略。
这样写的话就算在try代码块中检测到了异常,也不会去捕获,一般不会这样写代码。
若try中出现异常,则try中异常语句后续代码不会执行,直接跳到对应的catch代码块。
即使在try或catch中return了,finally还是会执行。
执行流程如下:
- 先计算返回值,并将返回值缓存起来,等待返回
- 执行finally代码块
- 将之前存储的返回值返回
需要注意的是:
返回值的返回规则与值传递和引用传递的规则一致,返回值是在finally代码块执行前就确定了的,并且缓存了副本。如果返回的是基本数据类型,那么finally对这个变量进行再赋值也不会改变返回的值,因为这时返回的是缓存的副本。如果返回值是引用数据类型,返回的则是地址值的副本,只要finally不改变地址值对应的数据,也是不会影响返回的值。
finally代码块中不建议包含return,因为程序会在上述流程前提前退出,也就是说返回的不是try或catch的值。
如果在try或catch中停止了JVM,则finally不会执行,如:在trycatch中调用System.exit(0)
Throwable 的成员方法
| 方法名 | 说明 |
|---|---|
| public String getMessage() | 返回此 throwable 的详细消息字符串 |
| public String toString() | 返回此可抛出的简短描述 |
| public void printStackTrace() | 把异常的错误信息输出在控制台 |
重写时的异常注意事项
- 子类重写方法throws的异常要比父类方法throws的异常相同或更少
- 父类方法没有throws异常,子类方法只能try...catch处理