Java笔记(二)
枚举(Enumeration)和注解(Annotation)
[toc]
一、枚举
引出枚举类:
需求:创建Season类,包含季节的值,且季节的值是有限的4个(春夏秋冬),此类只读,不需要修改。
枚举类
自定义枚举类
规范
- 不提供
set()
方法,枚举类通常只读; - 对枚举对象/属性使用
final static
共同修饰,实现底层优化; - 枚举对象名通常使用全部大写,常量的命名规范;
- 枚举对象根据需要可以有多个属性。
- 不提供
特点
- 构造器私有化;
- 本类内部创建一组对象;
- 对外暴露对象(通过为对象添加
public final static
修饰符) - 可以提供
get()
方法,但不提供set()
方法。
代码演示
1 | public class test1 { |
enum枚举类
- 代码演示
1 | public class test1 { |
注意事项
- 当使用
enum
关键字实现枚举类时,默认继承Enum
类,而且是一个final
类; public final static Season SPRING = new Season("春天","温暖");
用SPRING("春天","温暖")
代替,根据其对应参数(的个数)确定使用的构造器;- 若使用无参构造器创建对象,则可以省略括号和实参列表;
- 当有多个枚举对象时,使用
,
间隔,最后用;
结尾; - 枚举对象必须放在枚举类的首行。
- 当使用
enum
实现接口- 使用
enum
关键字后,就不能再继承其他类了,因为enum
会隐式继承Enum
类,而Java是单继承机制; - 枚举类和普通来一样可以实现接口,形式:
enum 类名 implements 接口1,接口2{}
- 使用
Enum
成员方法
toString()
:Enum
类默认的toString()
直接返回对象名,但可以重写;name()
:返回对象名称;value()
:返回Season[],遍历取出定义的所有对象;valueOf()
:将字符串转换成枚举对象,要求字符串必须为已有变量名,否则报异常;compareTo()
:比较两个枚举常量,比较的是编号(前面对象的编号-后面对象的编号)。
代码演示:(Season类见上节)
1 | public class test1 { |
二、注解
注解:也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、 构造器、局部变量等数据信息。
和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在
JavaSE
中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE
中注解占据 了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。
基本的注解介绍:
使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。
➢三个基本的Annotation:
@Override
:限定某个方法,是重写父类方法,该注解只能用于方法;@Deprecated
:用于表示某个程序元素(类,方法等)已过时;@SuppressWarnings
:抑制编译器警告。
Override
@Override
只能修饰方法,不能修饰属性、类、包等;- 如果写了
@Override
注解,编译器就会去检查该方法是否真的重写了父类的方法,若重写,则编译通过,否则不通过; @interface
表示一个注解类。
Deprecated
@Deprecated
修饰某个元素,表示元素已过时;- 即使不推荐使用,但人可以使用;
- 可以修饰方法、类、字段、包、参数、等;
@Deprecated
可以做版本升级过渡使用。
SuppressWarnings
- 编写代码时,如果不希望看到警告,可以用
@SuppressWarnings
; @SuppressWarnings({"all"})
,在{""}
中写入不想看到的警告,all
表示全部警告不显示;@SuppressWarnings
的作用范围与放置位置有关;
元注解*
- JDK的元Annotation用于修饰其他Annotation;
- 元注解的种类(使用不多,了解,不用深入研究)
Retention
//指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
Target
//指定注解可以在哪些地方使用Documented
//指定该注解是否会在javadoc
体现Inherited
//子类会继承父类注解
异常(Exception)
一、基本介绍
- 基本概念
- Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两大类
Error
(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error 是严重错误,程序会崩溃。Exception
:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常
[程序运行时,发生的异常]和编译时异常
[编程时,编译器检查出的异常]。
二、异常体系图
异常体系图
- 异常体系图体现了继承和实现的关系;
- 虚线代表实现接口,实线代表继承。
小结
- 异常分为两大类,运行时异常和编译时异常;
- 运行时异常,编译器不要求强制处置的异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。
java.lang.RuntimeException
类及它的子类都是运行时异常,对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响; - 编译时异常,是编译器要求必须处置的异常。
三、五大运行时异常
NullPointerException
:空指针异常- 当应用程序试图在需要对象的地方使用
null
时,抛出该异常,如下:
String name = null; System.out.println(name.length());
- 当应用程序试图在需要对象的地方使用
ArithmeticException
:数学运算异常- 当出现异常的运算条件时,抛出此异常,如除0等。
ArraylndexOutOfBoundsException
:数组下标越界异常- 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ClassCastException
:类型转换异常当试图将对象强制转换为不是实例的子类时,抛出该异常。
代码示例:
1 | public class test1 { |
NumberFormatException
:数字格式不正确异常当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常==>使用该异常我们可以确保输入是满足条件数字。
代码示例:
1 | public class test1 { |
四、编译异常
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
- 常见的编译异常
SQLException
:操作数据库时,查询表可能发生异常;IOException
:操作文件时,发生的异常;FileNotFoundException
:当操作一个不存在的文件时,发生异常;ClassNotFoundException
:加载类,而该类不存在时,异常;EOFException
:操作文件,到文件末尾,发生异常;IllegalArguementException
:参数异常。
五、异常处理
try-catch
方式
try-catch-finally
:程序员在异常中捕获发生的异常,自行处理。- 处理机制:
1 | try { |
- 使用细节
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到
catch
块; - 如果异常没有发生,则顺序执行
try
的代码块,不会进入到catch
; - 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码
finally
; - 可以有多个
catch
语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception
在后,NullPointerException
在前),如果发生异常,只会匹配一个catch
。 - 可以进行
try-fianll
配合使用,这种用法相当于没有捕获异常因此程序会直接崩溃/退出。应用场景:执行一段代码,无论是否发生异常,都必须执行某个业务逻辑。
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到
1 | public class test1 { |
throws
方式
throws
:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM。处理机制:(如果没有任何处理代码,则默认
throws
)
使用案例
throws
后面的异常类型可以是方法中产生的异常类型,也可以是它的父类,如Exception
;throws
关键字后也可以是异常列表,即可以抛出多个异制。
1 | public void f2() throws FileNotFoundException,NullPointerException { |
- 使用细节
- 对于编译异常,程序中必须处理,比如
try-catch
或者throws
; - 对于运行时异常,程序中如果没有处理,默认就是
throws
的方式处理,出现异常的地方将异常抛给调用它的类或对象,类或对象再向上抛,直至抛给JVM
虚拟机,JVM
虚拟机接受的异常后输出异常、结束程序; - 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型;
- 在
throws
过程中,如果有方法try-catch
,就相当于处理异常,就可以不必throws
。
- 对于编译异常,程序中必须处理,比如
1 | class Father |
六、自定义异常
- 一般情况下,用运行异常
RuntimeException
,好处是我们可以使用默认的处理机制。
1 | public class test1 { |
七、throw和throws的区别
意义 | 位置 | 后面跟的 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
常用类
一、包装类
包装类(
Wrapper
)的分类- 针对八种基本数据类型相应的引用类型-包装类;
- 有了类的特点,就可以调用类中的方法。
| 基本数据类型 | 包装类 |
| —————— | ————- |
| boolean | Boolean |
| char | Character |
| byte | Byte |
| short | Short |
| int | Interger |
| long | Long |
| float | Float |
| double | Double |装箱和拆箱
以
Integer <--> int
为例:- jdk5前的手动装箱和拆箱方式,装箱:基本类型->包装类型,反之,拆箱;
- jdk5以后(含jdk5)的自动装箱和拆箱方式;
- 自动装箱底层调用的是
valueOf
方法,比如Integer.valueOf()
; - 其他类型的一样。
1 | public class test1 { |
包装类型和
String
类型的相互转换以
Integer
和String
为例:
1 | public class test1 { |
- 常用的包装类方法
1 | Integer integer = Integer.valueOf(str4); |
Integer
创建机制- 经典面试题1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class test1 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//False (两个新对象,用 == 比较为False)
//所以,这里主要是看范围-128~127就是直接返回
Integer m = 1;//底层Integer.valueOf(1);->阅读源码,valueOf(n)根据n的范围确定返回的对象是否为新创建的
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n);//True (返回2个1,同一个对象)
//所以,这里主要是看范围-128~127就是直接返回,否则,就new Integer(xx);返回新对象
Integer x = 128;//底层Integer.valueOf(1);
Integer y = 128;//底层Integer.valueOf(1);
System.out.println(x == y);//False (返回两个新创建的对象)
/*
valueOf()底层代码:
当传入 i >= -128 && i <= 127 时直接从数组返回,否则就 new Integer(i)
public static Integer valueOf(int i) {
if (i >= Integer.IntegerCache.low && i <= Integer.IntegerCache.high)
return Integer.IntegerCache.cache[i + (-Integer.IntegerCache.low)];
//cache[]数组在类加载的时候就创建好了,缓存了-128~127之间的数,用的时候调用
return new Integer(i);
}
*/
Integer a = new Integer(127); //新建对象
Integer b = 127; //cache数组中取出
System.out.println(a == b);//False
//只要有基本数据类型,只判断值是否相等
Integer c = new Integer(127);
int d = 127;
System.out.println(c == d); //True
}
}二、String类
- 经典面试题1
String类结构剖析
String类的理解
- String对象用于保存字符串,也就是一组字符序列;
- 字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、”boy“等;
- 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节;
- String类较常用构造器(其它看手册):
String s1=new String();
String s2=new String(String original);
String s3=new String(char[] a);
String s4=new String(chartl a,int startlndex,int count);
- String是
final
类,不可被继承; - String有属性
private final char value[]
,用于存放字符串内容,value
是final
修饰的,不可修改(指不可修改地址指向,但单个字符内容可以改变);
String结构图
两种创建String对象的方法
- 方式一:直接赋值
String s="hello";
- 先从常量池查看是否有”hello”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址;
- 方式二:调用构造器
String s2=new String("hello");
- 先在堆中创建空间,里面维护了
value
属性,指向常量池的hsp空间。如果常量池没有”hello”,重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
- 先在堆中创建空间,里面维护了
- 方式一:直接赋值
String对象特性
字符串的特性
String是一个
final
类,代表不可变的字符序列;字符串是不可变的,一个字符对象一旦被分配,其内容是不可变的。
string s="a";
创建了一个字符串s+="b"
,实际上原来的"a"
字符串对象已经丢弃了,现在又产生了一个字符串s+"b"
(也就是"ab"
)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。=>结论:如果我们对字符串做大量修改,不要使用String。
相关例题
- 如
String s1 = "hell0"; s1 = "haha";
共创建了2个对象(s1重新指向常量池中的”haha”,不会销毁”hello”)。 - 如
String a = "hello"+"abc";
共创建了1个对象,编译器底层会优化,自动拼接再创建对象,相当于String a = "helloabc";
。 - 如下:
- 如
1 | public class test1 { |
- 重要规则
String c1 = "ab"+"cd";
常量相加,看常量池;String c = a + b;
变量相加,看堆。
String类的常见方法
format()
1 | public class test1 { |
- 其它见
Java API
三、StringBuffer类
基本介绍
java.lang.StringBuffer
代表可变的字符序列,可以对字符串序列进行增删;很多方法与
String
相同,但StringBuffer
是可变长度的;StringBuffer
是一个容器。StringBuffer
的直接父类是AbstractstringBuilder
;StringBuffer
实现了Serializable
,即StringBuffer
的对象可以串行化(可以进行网络传输,可以保存到文件);- 字符序列存放在在父类中
AbstractstringBuilder
属性char[] value
,不是final
;该value
数组存放字符串内容,因此存放在堆中; StringBuffer
是一个final
类,不能被继承。
StringBuffer对象的创建
1 | //1.创建一个大小为 16 的char[],用于存放字符内容 |
String VS. StringBuffer
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低原因:
private final char []value
; - StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高;
- 因为StringBuffer字符内容是存在
char[]value
,有扩容机制,所以在变化(增加/删除)时不用每次都更换地址(即不是每次创建新对象),所以效率高于String(char []value
放在堆中); String-->value[]-->常量池
;StringBuffer-->value[]-->堆
。
String和StringBuffer相互转换
1 | //1.String --> StringBuffer |
StringBuffer常用方法
- 增:
append()
; - 删:
delete(start,end)
; - 改:
replace(start,end,string)
;//将strat~end
间的内容替换掉,不含end - 查:
indexOf()
//查找子串在字符串第一次出现的索引,若找不到返回-1 - 插:
insert
- 获取长度:
length()
踩坑
1 | public class test1 { |
四、StringBuilder类
基本介绍
- 一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全)。该类被设计用作 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快;
- 在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。
StringBuilder
的直接父类是AbstractstringBuilder
;StringBuilder
实现了Serializable
,即StringBuiler
的对象可以串行化(可以进行网络传输,可以保存到文件);StringBuilder
对象字符序列仍然是放在其父类AbstractstringBuilder
的char []value
中,因此字符序列在堆中;StringBuilder
的方法,没有做互斥
的处理(即无synchronization
修饰),因此在单线程下使用。
常用方法
- 与
StringBuffer
一样。
五、String、StringBuffer、StringBuilder
三者的比较
- StringBuffer和StringBuilder非常类似,均可代表字符序列,而且方法也一样;
- 执行效率:
StringBuilder > StringBuffer > String
; String
:不可变字符序列,效率低,但复用性高;StringBuffer
:可变字符序列,效率高(增删),线程安全;StringBuilder
:可变字符序列,效率最高,线程不安全;如果要对字符串进行大量修改,不要使用String,使用
StringBuffer(多线程)
或StringBuilder(单线程)
。
三者的使用场景
- 如果字符串存在大量的修改操作,一般使用
StringBuffer
或StringBuilder
; - 如果字符串存在大量的修改操作,并在单线程的情况,使用
StringBuilder
; - 如果字符串存在大量的修改操作,并在多线程的情况,使用
StringBuffer
; - 如果我们字符串很少修改,被多个对象引用,使用
String
,比如配置信息等。
六、Math类
Math类主要使用其方法。
Math类中的方法都为静态(
static
),可直接通过Math.方法名()
调用。
- 常用方法
abs()
:绝对值pow()
:求幂ceil()
:向上取整floor()
:向下取整round()
:四舍五入sqrt()
:求开方random()
:求随机数(返回0~1之间的一个随机小数)x=(int)(2+Math.random()*6)
返回整数x(2<= x <= 7)x=Math.random()*6
返回小数x(0<= x < 6)x=2+Math.random()*6
返回小数x(2<= x < 8)
max()
:求两数最大值min()
:求两数最小值
七、Arrays类
常用方法
Arrays包含了一系列静态方法,用于管理和操作数组(如排序和搜索)。
toString()
:返回数组的字符串形式;sort()
:排序自然排序和定制排序;binarySearch()
:通过二分法查找,要求排好序,如int index=Arrays.binarySearch(arr,3);
;copyOf()
:数组元素的复制(Integer[] newArr=Arrays.copyOf(arr,arr.length);
);fill()
:数组元素的填充(Integer[] num=new Integer[]{9,3,2};
);equals()
:比较两数组元素内容是否一致(boolean equals=Arrays.equals(arr,arr2);
);asList()
:将一组值转换成List(List<Integer> asList=Arrays.asList(2,3,4,5,6,1);
);
sort排序的解读
- 自然排序和定制排序
1 | public class test1 { |
- 定制排序实现冒泡排序
1 | import java.util.Arrays; |
八、System类
常用方法
exit
:退出当前程序;arraycopy
:复制数组元素,比较适合底层调用;currentTimeMillens
:返回当前时间距离1970-1-1
的毫秒数;gc
:运行垃圾回收机制System.gc()
;
九、大数处理:BigInteger和BigDecimal
BigerIntege
- 创建大数:
BigInteger bigInteger = new BigInteger("999999999999999999999999999999");
(字符串形式); - 四则运算:不能直接用
+-*/
,需要用对应的方法操作:add()
:加;subtract()
:减;multiply()
:乘;divide()
:除
1 | import java.math.BigInteger; |
BigDecimal
当我们需要保留一个精度很高的数时,
double
不够用,可以用BigDecimal
;创建精度高的小数:
BigDecimal bigDecimal = new BigDecimal("199.99999999999999999999999999");
(字符串形式);四则运算方法同
BigerIntege
,但进行除法运算时,若出现结果是无限小数时,会出现ArithmeticException
异常,解决方法:在除法运算时,用BigDecimal.ROUND_CEILING
指定精度即可,如下:System.out.println("除:"+bigDecimal1.divide(bigDecimal2,BigDecimal.ROUND_CEILING));
//若出现无限小数,则结果的精度为分子的精度
十、日期类
第一代日期类(Date)
Date
:精确到毫秒,代表特定的瞬间;SimpleDateFormat
:格式和解析日期的类,SimpleDateFormat 格式化和解析日期的具体类。它允许进行格式化(日期->文本)、解析(文本->日期)和规范化。- 代码演示:
1 | import java.text.ParseException; |
第二代日期类(Calendar)
Calendar
是一个抽象类,并且构造器是protected
类,需要用getInstance()
获取实例,如:Calender c = Calender.getInstance();
直接输出
Calender
实例会输出一个含有一系列字段的字符串,可根据需求选取;Calender
没有专门的格式化方法,可根据需求组合输出。
1 | import java.util.Calendar; |
第三代日期(LocalDateTime)
前面两代日期类的不足分析:
JDK1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类之后被弃用了。而Calendar也存在问题是:
- 可变性:像日期和时间这样的类应该是不可变的;
- 偏移性:Date中的年份是从1900开始的,而月份都从0开始;
- 格式化:格式化只对Date有用,Calendar则不行;
- 此外,它们也不是线程安全的;不能处理闰秒等(每隔2天,多出1s);
声明方式
LocalDateTime ldt=LocalDateTime.now();
//获取年月日时分秒LocalDateTime ldt = LocalDate.now();
//只能获取年月日LocalDateTime ldt = LocalTime.now();
//只能获取时分秒
DateTimeFormatter
格式化:类似于SimpleDateFormat:DateTimeFormatter dtf = DateTimeFormatter.ofPattern("格式");
String str= dtf.format(日期对象);
Instant
时间戳:类似于Date,提供了一系列DateTimeFormatter
和Date
相互转换的方式:- Instant —> Date:
Date date = Date.from(instant);
- Date —> Instant:
Instant instant = date.toInstant();
- Instant —> Date:
LocalDateTime
提供了plus
和minus
方法可以对当前时间进行加或者减;plusDay()、plusMonths()、plusHours()
等等;minusDay()、minusMonths()、minusHours()
等等;
代码演示
1 | import java.time.LocalDateTime; |