文章目录
生成随机数
导包:import java.util.Random;
Random random = new Random(); //创建一个随机数对象
int i = random.nextInt(101); //0~100的随机数,取不到101
System.out.println(i);
生成不重复的随机数并放入数组
package Demo;
import java.util.Arrays;
import java.util.Random;
public class Test_Random {
public static void main(String[] args) {
Random random = new Random();
int[] array = new int[5];
int index = 0;
for (int i = 0; i < array.length; i++) {
array[i] = -1; //起始都赋值-1,避免与生成的随机数混淆
}
while(index < array.length){
int ran = random.nextInt(5);
if(! contains(array,ran)){ //不包含才放入
array[index++] = ran;
}
}
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
/**
*
* @param arr srarch array
* @param key search key
* @return 包含返回:true 不包含返回:false
*/
public static boolean contains(int[] arr , int key){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == key){
return true;
}
}
return false;
}
}
枚举
可以一一列举出来的,当我们需要一组常量时,才建议使用枚举类型
枚举编译之后也是生成class文件
枚举也是一种引用数据类型
枚举中的每一个值可以看作是常量
(枚举的内容之后再补)
异常
异常在Java中以类的形式存在,每一个异常类都可以创建异常对象
异常有两种异常:编译时异常 与 运行时异常
处理异常有两种方式:
- 在方法声明的位置使用
throws
关键字,上抛异常。 - 使用
try...catch
语句捕获异常
package Demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Test_Exception {
public static void main(String[] args) throws Exception{ //上抛异常时可以抛出:Exception。因为它是所有异常类的父类,准没错!
m1();
}
public static void m1() throws ClassCastException,FileNotFoundException{ //上抛异常时可以写多个异常,用逗号隔开
m2();
}
public static void m2() throws FileNotFoundException{ //上抛异常,因为m1方法调用m2,所以抛给给m1方法
m3();
}
public static void m3() throws FileNotFoundException { //需要上抛异常,因为FileNotFoundException类在源码中有异常抛出的操作。且因为m2方法调用m3,所以抛给m2方法
new FileInputStream("E:\\IDEA2020\\app\\src\\Demo\\Test_Exception.java");
}
}
一般不建议给main方法使用throws,因为继续往上抛会抛给JVM,JVM的处理操作是终止程序。
因此一般建议使用try..catch
来捕获异常
注意哪些语句可以执行:
- 只要异常没有捕获,采用上抛的方式,此方法后续代码不会执行。
- try语句中某一行出现异常,该行后面的代码也不会执行,转而执行catch语句中的代码
try...catch
捕获异常后,其后续代码可以执行
package Demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Test_Exception {
public static void main(String[] args) { //上抛异常时可以抛出:Exception。因为它是所有异常类的父类,准没错!
try{
System.out.println("main begin");
m1();
System.out.println("这里的代码不会执行!");
}
catch (Exception e ){
System.out.println("文件路径出错?");
}
System.out.println("main over"); //此行在try..。catch语句之外,处理完异常后会继续执行此语句
}
public static void m1() throws ClassCastException,FileNotFoundException{ //上抛异常时可以写多个异常,用逗号隔开
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2() throws FileNotFoundException{ //上抛异常,因为m1方法调用m2,所以抛给给m1方法
System.out.println("m2 begin");
m3();
System.out.println("m2 over");
}
public static void m3() throws FileNotFoundException { //上抛异常,因为m2方法调用m3,所以抛给给m2方法
System.out.println("m3 begin");
new FileInputStream("E:\\IDEA2020\\app\\sc\\Demo\\Test_Exception.java");
System.out.println("m3 over");
}
}
/* 输出:
main begin
m1 begin
m2 begin
m3 begin
文件路径出错?
main over
*/
深入try..catch
:
- catch后面小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型
- catch可以写多个。建议catch的时候精确的一个一个处理,有利于程序调试
- catch写多个的时候,从上到下,必须遵守从小到大(子类到父类)
public static void main(String[] args) {
try{
FileInputStream file = new FileInputStream("E:\\IDEA2020\\app\\src\\Demo\\Test_Exception.java");
file.read(); //读文件
}catch (FileNotFoundException e){
System.out.println("拒绝访问 或 系统找不到指定路径");
}catch (IOException e){
System.out.println("文件不存在");
}
}
JDK8新特性
使用try..catch
时列举出多个异常一起捕获
public static void main(String[] args) {
try{
FileInputStream file = new FileInputStream("E:\\IDEA2020\\app\\src\\Demo\\Test_Exception.java");
System.out.println( 100 / 0);
}catch (FileNotFoundException | ArithmeticException | NullPointerException e){ //列举出多个异常一起捕获
System.out.println("文件不存在?数学异常?空指针异常?");
}
}
异常对象的常用方法
getMessage()
获取异常的描述信息
printStackTrace()
打印异常堆栈信息
public static void main(String[] args) {
try {
m1();
} catch (FileNotFoundException e) {
//打印异常堆栈信息
e.printStackTrace();
//获取异常简单描述信息,此信息实际是构造方法中传入的字符串参数
String msg = e.getMessage();
System.out.println(msg);
}
}
public static void m1() throws FileNotFoundException {
m2();
}
public static void m2() throws FileNotFoundException {
new FileInputStream("E:\\IDEA2020\\app\\src\\Demo\\Test_Exception.jav");
}
}
上面代码的报错信息如下:
举例异常信息如下:
java.io.FileNotFoundException: E:\IDEA2020\app\src\Demo\Test_Exception.jav (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at Demo.Test_Exception.m2(Test_Exception.java:82)
at Demo.Test_Exception.m1(Test_Exception.java:79)
at Demo.Test_Exception.main(Test_Exception.java:69)
从上往下看,跳过前面四个信息。
出问题的是 m2() 然后导致 m1() 然后导致 main()
finally
配合 try…catch使用finally子句:
- 在finally子句中的代码是最后执行的,并且一定会执行的
- finally子句必须和try一起出现,不能单独编写
通常在什么情况下使用?
通常在finally语句块中完成资源的释放/关闭,因为无论如何finally语句块都会执行,有保障
package Demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Test_ExceptionFinally {
public static void main(String[] args) {
FileInputStream fis = null;
try{
//创建文件流
fis = new FileInputStream("E:\\IDEA2020\\app\\src\\Demo\\Test_ExceptionFinally.java");
String s = null; //空指针
s.toString();
}
catch (FileNotFoundException e){
e.printStackTrace();
}
catch (NullPointerException e){
e.printStackTrace();
}
//finally的语句一定会执行
finally {
if (fis != null) {
try {
fis.close(); //关闭文件流
} catch (IOException e) { //捕获fis.close()的异常
e.printStackTrace();
}
}
}
}
}
try不能单独使用,try和finally可以联合使用。
即使try中有return,也会在执行return前执行finally
public static void main(String[] args) {
try{
System.out.println("第一执行"); //执行顺序:1
return ; //3
}
finally {
System.out.println("第二执行"); //2
}
}
异常与方法覆盖
重写之后的方法不能比重写前的方法抛出更多(更宽泛)的异常,可以更少
class People{
public void dowhat() throws Exception{
}
}
class Chinese extends People{
/*
public void dowhat(){ //父类抛出了异常,子类可以不抛出
}
*/
/*
public void dowhat() throws Exception{ //子类可以抛出一样的异常
}
*/
public void dowhat() throws NullPointerException{ //子类可以抛出更低一级的异常
}
}
final、finally、finalize的区别
final 关键字
final修饰的类无法继承
final修饰的方法无法覆盖
final修饰的变量不能重新赋值
finally 关键字
和try一起联合使用
finally语句块中的代码一定会执行
finalize 标识符
是Object类中的方法名
这个方法是由垃圾回收器GC负责调用的
throws和throw的区别
throws 在方法声明位置上使用,表示上报异常信息给调用者
throw 手动抛出异常,在方法体中使用
集合
集合实际上就是一个容器,可以用来容纳其它类型的数据。数组其实就是一个集合
集合不能直接存储基本数据类型,另外集合也不能直接存储java对象。集合当中存储的都是java对象的内存地址(或者说存储的是引用)
注意:
集合在java中本身是一个容器,是一个对象
集合中任何时候存储的都是“引用”
所有的集合类和集合接口都在java.util
包下
Java中集合分为两大类:
-
一类是单个方式存储元素。这一类集合中超级父接口是:java.util.Collection
-
一类是以键值对方式存储元素。这一类集合中超级父接口是:java.util.Map
(集合中所有的实现类)
ArrayList: 底层是数组
LinkedList 底层是双向链表
Vector 底层是数组,线程安全的,效率较低,使用较少
HashSet 底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合Key部分了
TreeSet 底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合Kep部分了
HashMap 底层是哈希表
Hashtable 底层也是哈希表,只不过线程安全的,效率较低,使用较少
Properties 线程安全的,并且Key和value只能存储字符串String
TreeMap 底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序
List集合存储元素的特点:
有序可重复
有序:存进去的顺序和去除的顺序相同
可重复:可重复存储
Set集合存储元素的特点
无序不可重复
无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标
不可重复:不可重复存储
SortedSet集合存储元素特点
首先是无序不可重复的,但是SortSet集合中的元素是可排序的。
可排序:按照大小顺序排列
Map集合的key,就是一个Set集合
往Set集合中放数据,实际上放到了Map集合的key部分
Collection接口中的常用方法:
boolean add(Object e) 向集合中添加元素
int size() 获取集合中元素的个数
void clear() 清除集合
boolean contains(Object o) 判断当前集合是否包含对象o,包含返回true,否则false
boolean remove(Object o) 删除集合中元素o
boolean isEmpty() 判断集合是否为空
Object[] toArray() 返回包含所有存储在调用集合的元素的数组
public static void main(String[] args) {
//创建一个集合对象,多态
Collection c = new ArrayList(); //无法直接将接口实例化,所以选择一个实现类进行实例化
c.add(1200); //自动装箱,实际上是放进去一个对象的内存地址
c.add(3.14); //自动装箱
c.add(true); //自动装箱
c.add(new Object());
System.out.println("当前集合中元素个数:"+c.size()); //获取集合中元素个数
c.clear(); //清空集合
System.out.println("当前集合中元素个数:"+c.size()); //获取集合中元素个数
c.add("你");
c.add("好");
System.out.println(c.contains("你")); //true
c.remove("你");
System.out.println(c.contains("你")); //false
c.add(new Object());
Object[] arr = c.toArray(); //转换成数组,数组定义为Onject型
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
Iterator迭代器
Iterator迭代器有四种方法:
default void forEachRemaining(Consumer<? super E> action) 对每个剩余元素执行给定的操作,直到处理完所有元素或操作引发异常。
boolean hasNext() 如果迭代有更多元素,则返回true
E next() 返回迭代中的下一个元素。
default void remove() 从底层集合中移除此迭代器返回的最后一个元素(可选操作)。。
使用迭代器进行循环遍历
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("aaa");
c.add("DMIND");
c.add("123");
c.add("123");
c.add(new Object());
Iterator it = c.iterator(); //获取集合对象的迭代器对象Iterator
while(it.hasNext()){ //while循环遍历元素
Object obj = it.next();
System.out.println(obj);
}
/**输出:可见ArrayList类是有序且可以重复的
* aaa
* DMIND
* 123
* 123
* java.lang.Object@7d4991ad
*/
}
Collector接口中的contains、remove方法比较的是内容
以下代码返回的结果是true
public class Test_Contains {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("abc");
String x = new String("abc");
boolean flag = c.contains(x); // 等价于x.equals(c)
System.out.println(flag);
}
}
主要原因跟ArrayList类下的contains方法源码有关,最终会调用参数的equals方法,而这里的参数是x属于String类,String类的equals方法是比较内容而非地址的,所以返回true。
而remove方法的源码和contains的一样也调用了equlas方法,所以两者类似的。
c.contains(x) 等价于 x.equals(c)
c.remove(x) 等价于 x.equals(c)
所以如果是自己定义了类的话,一般建议重新写equals方法!
集合中的元素删除
当集合的结构发生改变时,迭代器必须重新获取,如果还是使用未更新的迭代器,会出现异常:java.util.ConcurrentModificationException
在迭代元素的过程中,一定要使用迭代器Iterator的remove方法删除元素,而不是使用集合自带的remove方法删除。
通过集合删除元素,并没有通知迭代器,导致迭代器的快照和原集合状态不同。
使用迭代器去删除时,会自动更新迭代器,并且更新集合,不过会删除集合中的元素
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(1);
c.add("DMIND");
c.add(new Object());
Iterator it = c.iterator();
while (it.hasNext()){
Object obj = it.next();
it.remove(); //不能用c.remove()这种方法删除元素,会导致迭代器的状态与集合的状态不一致
System.out.println(obj);
}
System.out.println(c.size()); //0
}
List接口的特有方法
List集合存储元素特点:有序可重复
List作为Collection接口的子接口,有自己的特有的方法:
E get(int index) 通过下标获取集合元素
void add(int index, E element) 在指定位置插入指定元素
int indexOf(Object o) 返回对象o第一次出现的索引
E remove(int index) 删除指定位置的元素
E set(int index, E element) 修改指定位置的元素
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(1,"DMIND"); //往下标1放入"DMIND"
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
System.out.println(list.indexOf("DMIND")); //1
list.remove(1);
System.out.println(list.size());//3
list.set(1,"6666");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
泛型
泛型的好处:
- 集合中存储的元素类型是统一的了
- 从集合中取出的元素类型是泛型指定的类型,不需要进行大量的”向下转型“
泛型的缺点:
- 导致集合中存储的元素缺乏多样性
package Demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Generic {
public static void main(String[] args) {
//自动类型判断,钻石表达式,JDK8之后才允许
//ArrayList<这里的类型会自动判断>()
List<Animal> mylist = new ArrayList<>();
//添加元素
mylist.add(new Animal());
mylist.add(new Cat());
mylist.add(new Bird());
//遍历
Iterator<Animal> it = mylist.iterator(); //获取迭代器
while(it.hasNext()){
//使用泛型后,每一次迭代返回的数据都是Animal类型
Animal a = it.next();
a.move();
}
List<String> mylist2 = new ArrayList<>();
mylist2.add("DMIND123456");
mylist2.add("http://baidu.com");
mylist2.add("http://jingdong.com");
Iterator<String> it2 = mylist2.iterator();
while(it2.hasNext()){
//使用泛型后,每一次迭代返回的数据都是String类型
String s = it2.next();
System.out.println(s.substring(7));
}
}
}
foreach
数组、集合使用foreach会很方便地遍历各个元素。
不使用迭代器不使用下标直接循环
package Demo;
import java.util.ArrayList;
import java.util.List;
public class Test_Foreach {
public static void main(String[] args) {
String[] myArrray = {"DMIND","JAVA"}; //数组
for (String s: myArrray
) {
System.out.println(s);
}
List<String> mylist = new ArrayList<>(); //String的集合
mylist.add("字符串");
mylist.add("String类型");
for (String str: mylist) {
System.out.println(str);
}
List<Animal> mylist2 = new ArrayList<>(); //Animal类的集合
mylist2.add(new Animal());
mylist2.add(new Cat());
mylist2.add(new Bird());
for (Animal animal: mylist2
) {
animal.move();
}
}
}