Java进阶(异常,泛型,集合)
Java进阶(异常,泛型,集合)
异常
异常Throwable分为Error和Exception
- Error :代表系统级别错误,属于严重问题,不是开发人员的问题。
- Exception分为:
- RuntimeException:运行时异常,以后的开发中可以尽量使用。
- 其他异常:编译时异常,在编译阶段就会出现的异常。
- 自定义异常:可以自定义一个异常类来继承Exception,主方法中可以new这个异常类来抛出异常。
泛型
-
作用:提供在 编译阶段 约束所能操作的数据类型,并自动进行检查,这样可以避免强制类型转换,及其可能出现的异常。
-
把具体的数据类型作为参数传给类型变量。
-
泛型类:
- 格式:修饰符 class 类名<类型变量,类型变量.......>{ },变量类型一般用大写的英文字母,常用的有:E,T,K,V。
-
泛型接口:
-
修饰符 interface 接口名<类型变量,类型变量.......>{ },,变量类型一般用大写的英文字母,常用的有:E,T,K,V。
-
例:
//定义一个自定义泛型接口 //这样就可以接收多种类型的对象,这些对象再调用这个add方法 pubic interface Data<T> { void add(T date); T query(int id);//根据id来查询对象信息,返回值为第一个对象 }
-
-
泛型方法,通配符,上下限
- 修饰符 <类型变量,类型变量.......> 返回值类型 方法名( ){ }
- 通配符:就是“?”,可以在使用泛型的时候代表一切类型,E,T,K,V,是在定义泛型的时候使用。public static void go(ArrayList<?> cars){ }
- 上下限(在使用泛型的时候,用通配符表示一切类型,但又要限制在某些类里面的时候):例:
- 泛型上限:?extend Car:?表示能接收的必须是Car或者其子类。
- 泛型下限:? super Car:?表示能接收的必须是Car或者其父类。
-
泛型支持的类型
-
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)
-
泛型擦除:泛型工作在编译阶段,等编译后泛型就没用了,所以泛型在编译后都会被擦除,所有类型会恢复成object类型。
-
包装类:把基本数据类型的数据包装成对象类型
- int-->Integer char-->character 其他基本类型包装都是首字母大写。(double-->Double)
-
如何包装?
- Integer it=new Integer(100);//过时
- Integer it=Integer.valueOf(100);//把100包装成一个整型对象。推荐这个方法,因为源代码中这个方法自动缓存了-128~127的整型数据对象,使读取速度更快。
-
自动装箱:基本数据类型的数据可以直接变成包装对象的数据,不需要额外做任何事情
- Integer it=100;//直接变成整型对象类型
-
自动拆箱:把包装类型的对象直接给基本类型的数据
- int i=it(it为对象类型)
-
包装类新增的功能:
-
把基本类型数据转换成字符串
int x=22; String str=Integer.toString(x);//str="22" //或者 String str=x+"";//在两个类型相加时,有一方是String类型,那么整体为String类型,在这里相当于"22"+""。 System.out.println(str+1);//"221" -
把字符串数值转换成对应的基本数据类型
String str="100"; int i=Integer.parseInt(str); //或者 int i=Integer.valueOf(str);//推荐使用 System.out.println(i+100);//200
-
-
集合
- 集合是一种容器,用来装数据的,类似于数组,但集合的大小可变。
- 每个元素包含一个值
1. Collection单列集合:
-
代表接口,包含List
,Set 。 -
List接口系列集合:添加的元素是有序的,可重复的,有索引。
- 实现类ArrayList、LinkedList:有序的,可重复的,有索引
-
Set接口系列集合:添加的元素是无序的,不重复,无索引。
- 实现类HashSet:无序的,不重复,无索引
- 实现类LinkedHashSet:有序的,不重复,无索引
- 实现类TreeSet:按照大小默认升序排序,不重复,无索引
-
把集合转换成数组:object[] arr = list.toArray();
-
Collection遍历:
-
迭代器遍历:
Collection<String> names = new ArrayList<>(); //存入数据后 //得到这个集合的迭代器对象 Iterator<String> it=names.iterator(); System.out.println(it.next());//输出第一个元素 System.out.println(it.next());//输出第二个元素。。。以此类推 //使用一个while循环来遍历 while(it.hasNext()){//判断该位置是否有值 String name=it.next(); System.out.println(name); } -
增强for循环
-
Lambda表达式
集合名.forEach(new Consumer<String>(){ @Override public void accept(String s){ System.out.println(s); } });//最终形式为: 集合名.forEach(System.out::println); //常用形式: 集合名.forEach(s->System.out.println(s));
-
-
三种遍历方式的区别
- 在遍历集合的同时进行修改集合时:
- 如果集合支持索引,那么用for循环遍历,每删除数据后做i--;或者可以倒着遍历。
- 也可以使用迭代器遍历,并用迭代器提供的删除方法删除数据
- 增强for循环/Lambda遍历均不能解决并发修改异常问题,因此它们只适合做数据的遍历,不适合同时做增删操作。
- 在遍历集合的同时进行修改集合时:
-
List(接口)集合:
- 两个常用的实现类:ArrayList(查询多)、LinkedList(增删多)
- 它们底层采用的数据结构(存储和组织方式)不同。
- ArrayList底层基于数组存储的,数组:根据索引查询数据更快,但是增删数据效率低。 (第一次加数据的时候进行扩容,默认长度为10,超过了就扩充为1.5倍的length大小)
- LinkedList底层基于链表存储的,基于双链表实现,链表:查询数据慢,无论查询哪个数据都要从头开始找,增删相对数组来说更快。
小案例
//设计一个电影信息管理系统
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MovieService {
//准备集合容器
public static List<Movie> movies = new ArrayList<>();
private static Scanner sc = new Scanner(System.in);
public void start() {
while (true) {
// 准备cmd操作界面
System.out.println("\t电影信息操作系统\t");
System.out.println("1.上架某个电影");
System.out.println("2.下架某个电影");
System.out.println("3.查询某个电影");
System.out.println("4.封杀某个明星的电影");
System.out.println("5.退出");
System.out.println("6.展示全部电影信息");
System.out.println("请输入您要进行的操作:");
int command = sc.nextInt();
switch (command) {
case 1:
System.out.println("请输入您要上架的电影");
addMovie();
break;
case 2:
System.out.println("请输入您要下架的电影");
removeMovie();
break;
case 3:
System.out.println("请输入您要查询的电影");
queryMovie();
break;
case 4:
System.out.println("请输入您要封杀的电影");
deleteStar();
break;
case 5:
System.out.println("成功退出!");
return;
case 6:
System.out.println("展示全部电影信息:");
queryAllMovie();
break;
default:
System.out.println("命令错误!");
break;
}
}
}
//展示全部电影
private void queryAllMovie() {
for (Movie movie : movies) {
System.out.println(movie.getName()+" "+movie.getScore()+" "+movie.getActor()+" "+movie.getPrice());
}
}
//封杀某个明星,下架其所有电影
private void deleteStar() {
System.out.println("请输入你要封杀的明星名称:");
String star = sc.next();
for (int i = 0; i < movies.size(); i++) {
Movie movie = movies.get(i);
if (movie.getActor().contains(star)) {
movies.remove(movie);
i--;
}
}
queryAllMovie();
}
//下架功能
private void removeMovie() {
System.out.println("请输入你要下架的电影名称:");
String name = sc.next();
Movie movie = queryMovieByName(name);
if (movie != null) {
movies.remove(movie);
System.out.println("下架成功!");
} else System.out.println("没有找到这个电影!");
}
//上架功能
private void addMovie() {
//1.创建电影对象,封装这部电影信息
Movie movie = new Movie();
//2.给电影对象写入电影信息
System.out.println("请输入电影名称");
movie.setName(sc.next());
System.out.println("请输入电影评分");
movie.setScore(sc.nextDouble());
System.out.println("请输入电影主演");
movie.setActor(sc.next());
System.out.println("请输入电影价格");
movie.setPrice(sc.nextDouble());
//3.将电影对象添加到集合中去
movies.add(movie);
System.out.println("上架成功!");
}
//查询功能
private void queryMovie() {
System.out.println("请输入你要查询的电影名称");
String name = sc.next();
Movie movie = queryMovieByName(name);
if (movie != null) {
System.out.println("查询成功!");
System.out.println(movie.getName()+" "+movie.getScore()+" "+movie.getActor()+" "+movie.getPrice());
} else System.out.println("没有找到这个电影!");
}
//按照名称查询功能
private Movie queryMovieByName(String name) {
for (Movie m : movies) {
if (m.getName().equals(name)) {
return m;//找到了
}
}
return null;//没有找到
}
}
//电影类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Movie {
private String name;
private double score;
private String actor;
private double price;
}
//测试类
public class Test {
public static void main(String[] args) {
MovieService service = new MovieService();
service.start();
}
}
-
Set(接口)集合:
-
实现类HashSet:无序的,不重复,无索引
- 底层原理:
- 哈希值:一个int类型的随机值,Java中每个对象都有一个哈希值。可用public int hashCode( );返回对象的哈希码值。不同对象哈希值大概率不相等,但有可能会相等(哈希碰撞)。
- Set第一次加数据的时候在底层加一个默认长度为16的数组。超过1.2倍的length时,扩充为两倍大小。
- 链表长度超过8,并且数组长度>=64时,自动将链表转成红黑树(二叉平衡排序树)(树的结点,左小右大,左右子树高度相差不超过1)。
-
Set集合在进行去重操作时,若类型为引用类型(例:Student类),要在类中重写hashCode()和equals()两种方法,否则去重可能有问题。
-
实现类LinkedHashSet:有序的,不重复,无索引
-
实现类TreeSet:按照大小默认升序排序,不重复,无索引
-
2. Map双列集合
-
每个元素包含两个值(键值对)
-
常用的实现类
- :HashMap<K,V>:由键决定特点,无序,不重复,无索引。
- LinkedHashMap<K,V>:由键决定特点,有序,不重复,无索引。
- TreeMap<K,V>:由键决定特点,按大小默认升序排序,不重复,无索引。
-
遍历Map:
-
1.键找值
//1.提起Map集合的全部键到一个Set集合中去 Set<String> keys = map集合名.keySet(); //2.遍历Set集合,得到每一个键 for(String key:keys){ //根据键去找值 Integer value = map.get(key); System.out.println(key + "=" + value); } -
2.键值对
//创建Map集合,并存入数据 Map <String ,Integer>map = new HashMap<>(); map.put("a",1); map.put("b",2); map.put("c",3); //把Map集合转换成Set集合,里面的元素类型是键值对类型(Map.Entry<String,Ingeter>) Set<Map.Entry<String,Integer>> entry=map.entrySet(); //再遍历Set集合,分别把键和值取出来输出 for(Map.Entry<String,Integer> m:entry){ String key=m.getKey(); Integer value=m.getValue(); System.out.println(key+"="+value); } -
Lambda方式
map.forEach((k,v)->System.out.println(k+"="+v));
-
Map小案例

import java.util.*;
public class test {
public static void main(String[] args) {
start();
}
public static void start() {
Scanner sc = new Scanner(System.in);
//创建一个List集合来存储学生选择的景点
List<String> list = new ArrayList<>();
//用数组来设定景点的种类
String[] area={"A","B","C","D"};
Random random=new Random();
for (int i = 1; i < 80; i++) {
//随机生成[0-4)的整数
int index=random.nextInt(4);
//将随机产生的数字作为景点下标存入集合中,就是随机一个景点存入集合中
list.add(area[index]);
}
//设置一个Map容器来统计学生选择的景区及其票数
Map<String,Integer> map=new HashMap<>();
//遍历list集合
for (String s : list) {
if (map.containsKey(s)) {//如果map集合中存在该景点就让景点的票数加1
map.put(s, map.get(s) + 1);
} else {//如果map集合中不存在该景点就存入该景点作为键并且初始票数为1
map.put(s, 1);
}
}
//遍历map集合打印所有景点及其票数
map.forEach((k,v)->{System.out.println(k+":"+v);});
}
}
浙公网安备 33010602011771号