Java SE 04(Object类、常用API、集合与泛型、异常、线程、等待唤醒机制、Lambda表达式)
Java SE 04
一、Object类、常用API
Object类的toString方法
-
Java.lang.Object类是类层次结构的根类(最顶层)。每个类都使用了Object类作为父类(超类)。所有对象(包括数组)都实现了这个类的方法。
-
所有的类都继承了Object类,所以可以使用Object类中的toString方法
-
每次使用System.out.println()直接打印对象都会调用一次toString方法,String类也是继承了Object类但重写了toString方法,所以打印出的String类的对象是重写(override)方法中格式调整好的字符串
-
自定义一个类,其中是默认继承了Object类中toString方法,若未重写,按照原方法的格式(包名类名@地址值)打印地址值
-
直接打印对象的地址不常见,一般写类的时候要重写Object类的toString方法,也就是说在定义类的方法时除了写getXxx/setXxx方法,还要重写toString方法(idea快捷键Alt+Insert),这样打印出的对象值可以看到对象中的属性值
//toString方法输出格式:类名{成员变量1=xxx, 成员变量2=xxx} //Person{age=45, name='Jeff'} public class Person { private int age; private String name; @Override public String toString() { return "Person{" + "age=" + age + ", name='" + name + '\'' + '}'; } //... }
-
判断一个类是否重写了toString方法,直接打印这个类的对象。
若没有重写toString方法,打印的是这个对象的地址值(默认)
如果重写了toString方法,那么按照重写的方式打印
public class Demo01 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println(scanner.toString());//Scanner中重写了toString方法 Random random = new Random(); System.out.println(random.toString());//Random中未重写toString方法 ArrayList<Integer> list = new ArrayList<>(); System.out.println(list.toString());//Arraylist<Integer>中重写了toString方法 } } /* output: java.util.Scanner[delimiters=\p{javaWhitespace}+][position=0][match valid=false][need input=false][source closed=false][skipped=false][group separator=\x{2c}][decimal separator=\x{2e}][positive prefix=][negative prefix=\Q-\E][positive suffix=][negative suffix=][NaN string=\QNaN\E][infinity string=\Q∞\E] java.util.Random@61dc03ce [] */
Object类的equals方法
-
所有的类都默认继承了Object类,所以可以使用Objcet类的equals方法
-
boolean equals(Object obj)指示其他某个对象是否与此对象“相等”
-
Object类中的equals方法的源码:
public boolean equals(Object obj) { return (this == obj); }
参数:Object obj:可以传递任意的对象
方法体:
==比较运算符,返回的就是一个布尔值true,false
基本数据类型:比较的是值
引用数据类型:比较的是两个对象的地址值
关于this:哪个对象调用了方法,方法中的this就是那个对象。p1调用了equals方法,所以方法中的this就是p1这个对象
-
Object类的equals方法默认是比较两个对象的地址值,如果比较两个对象的成员变量值(属性值)是否相同,一般要重写equals方法,比较两个对象的属性(成员变量)值
重写equals方法(idea快捷键Alt+Insert)
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;//使用了反射,判断o是否为Person类型,等效于instanceof Person person = (Person) o; return age == person.age && name.equals(person.name); }
//另一种写法 @Override public boolean equals(Object object) { if(object == this) { return true; } if(object == null) { return false; } if(object instanceof Person){ //Object object = new Person(); Person person = (Person)object;//类型高转低,前提条件是先要用instanceof判断object是否是Person类new创建的,所以传参如果是Person类型的对象,就能通过该instanceof检测 return name.equals(person.name) && age == person.age; } return false; }
Objects类的equals方法
-
JDK7中添加了Objects工具类,提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或是null-tolerant(空指针容忍的)
-
Object类中equals使用空指针调用对象会报错,Objects类中的equals可以使用两个空指针进行比较
//System.out.println(null.equals(null));//Error System.out.println(Objects.equals(null, null));//output: True
-
Objects类的equals方法是个静态方法,可以直接通过类名.成员方法直接使用
-
Objects类的equals方法:对两个对象进行比较,可以防止空指针异常
//Objects的equals方法实现 public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b));//加了健壮性判断 }
Date类
-
java.util.Date是一个表示日期和时间的类,精确到毫秒(1s = 1000ms)
-
对时间和日期进行计算时,可以将日期转换为毫秒进行计算,计算完毕再将毫秒转为日期,
规定的时间原点(0毫秒):1970年1月1日00:00:00(英国格林威治时间),中国属于东八区,会把时间增加8个小时,就是计算当前日期到时间原点之间一共经历了多少毫秒(使用System.currentTimeMillis()的返回值)
-
Date类的无参构造函数
Date()就是获取当前系统的日期和时间
public class Demo02 { public static void main(String[] args) { System.out.println(new Date());//Mon May 17 14:51:54 CST 2021 } }
-
Date类的带参构造函数
Date(long date):传递毫秒值,把毫秒转换为Date日期
-
Date类的成员方法
long getTime()将当前日期转换为毫秒值,getTime()相当于使用System.currentTimeMillis()
public class Demo02 { public static void main(String[] args) { System.out.println(new Date().getTime()); System.out.println(System.currentTimeMillis()); } } /* output: 1621236536108 1621236536108 */
-
DateFormat类
java.text.DateFormat是日期/时间格式化子类的抽象类(public abstract class DateFormat extends Format),作用是可以完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换
-
格式化:按指定的格式,使用String format(Date date)成员方法从Date对象转换为String对象
import java.text.SimpleDateFormat; import java.util.Date; public class Demo03 { public static void main(String[] args) { Date date = new Date(); System.out.println(date); SimpleDateFormat dateFormat1 = new SimpleDateFormat(); SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat3 = new SimpleDateFormat("yyyy**MM**dd HH**mm**ss"); String dateStr1 = dateFormat1.format(date);//SimpleDateFormat是DateFormat的子类,所以继承了format方法,Format是抽象类只能用SimpleDateFormat实现 System.out.println(dateStr1); String dateStr2 = dateFormat2.format(date); System.out.println(dateStr2); String dateStr3 = dateFormat3.format(date); System.out.println(dateStr3); } } /* output: Mon May 17 17:27:51 CST 2021 5/17/21, 5:27 PM 2021-05-17 17:27:51 2021**05**17 17**27**51 */
-
解析:按指定的格式,使用Date parse(String string)从String对象转换为Date对象
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo04 { public static void main(String[] args) throws ParseException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = dateFormat.parse("2021-05-19 16:35:23"); System.out.println(date); } } /* Wed May 19 16:35:23 CST 2021 */
-
DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat子类,例如SimpleDateFormat
-
java.text.SimpleDateFormat extends DateFormat
构造方法:SimpleDateFormat(String pattern)用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat
模式字符串区分大小写:y年 M月 d日 H时 m分 s秒
写对应的模式,会把模式替换为对应的日期和时间
“yyyy-MM-dd HH:mm:ss”
"yyyy年MM月dd日 HH时mm分ss秒"
注意:
模式中的字母不能更改,连接模式的符号可以更改
-
Calendar类介绍
-
java.util.Calendar类是一个日历类
-
Calendar类是一个抽象类,里边提供了很多操作日历字段的方法(YEAR、MONTH、DAY_OF_MONTH、HOUR)
-
Calendar类无法直接创建对象使用,里面有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象
-
static Calendar getInstance()使用了默认时区和语言环境获得一个日历
public class Demo06 { public static void main(String[] args) { Calendar calendar = Calendar.getInstance()//多态 System.out.println(calendar); } }
Calendar类常用方法
-
成员方法的参数:int field:日历类的字段,可以使用Calendar类的静态成员变量获取
public int get(int field):返回给定日历字段的值
参数:传递指定的日历字段(YEAR, MONTH...)
注意:MONTH字段对应的值是从0-11,使用时手动+1
返回值:日历字段代表具体的值
-
void set(int field, int value);
设置字段的值
-
abstract void add(int field, int value);
改变字段的值,value为正则增加,value为负则减少
-
Date getTime()
将Calendar的对象转为Date类型的对象
打印出是日期值
import java.util.Calendar;
import java.util.Date;
public class Demo06 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
int year = calendar.get(Calendar.YEAR);
System.out.println(year);
calendar.set(Calendar.YEAR, 2019);
year = calendar.get(Calendar.YEAR);
System.out.println(year);
calendar.add(Calendar.YEAR, -2);
year = calendar.get(Calendar.YEAR);
System.out.println(year);
Date date = calendar.getTime();
System.out.println(date);
}
}
/*
java.util.GregorianCalendar[time=1621266704832,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2021,MONTH=4,WEEK_OF_YEAR=21,WEEK_OF_MONTH=4,DAY_OF_MONTH=17,DAY_OF_YEAR=137,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=51,SECOND=44,MILLISECOND=832,ZONE_OFFSET=28800000,DST_OFFSET=0]
2021
2019
2017
Wed May 17 23:51:44 CST 2017
*/
System类
-
java.lang.System类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有
-
public static long currentTimeMillis()
返回以毫秒为单位的当前时间
//用于计算一段代码的运行时间 public class Demo08 { public static void main(String[] args) { long cntTime01 = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) { System.out.println("Hello"); } long cntTime02 = System.currentTimeMillis(); double runTime = (cntTime02 - cntTime01) / (double)1000; System.out.println("running time is " + runTime); } }
-
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
将数组中指定的数据拷贝到另一个数组中
//目的数组中拷贝到的位置若有内容会被源数组的内容覆盖 public class Demo07 { public static void main(String[] args) { int[] dstArr = new int[20]; dstArr[0] = 11; dstArr[1] = 12; dstArr[2] = 13; dstArr[5] = 14; int[] srcArr = new int[10]; srcArr[0] = 1; srcArr[1] = 2; srcArr[2] = 3; srcArr[3] = 4; srcArr[4] = 5; srcArr[5] = 6; System.out.println(Arrays.toString(srcArr)); //public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) System.arraycopy(srcArr, 0, dstArr, 0, 5); System.out.println(Arrays.toString(dstArr)); } } /* [1, 2, 3, 4, 5, 6, 0, 0, 0, 0] [1, 2, 3, 4, 5, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] */
-
StringBuilder类
-
String类中字符串是常量,他们的值在创建之后不能再修改。字符串的底层是一个被final修饰的数组,是一个常量。private final byte[] value;
-
使用String类进行字符串相加,内存中就会有多个字符串,占空间多,效率低下
String s = "a" + "b" + "c" ;这个字符串相加操作内存中本身产生3个字符串,还会产生"ab", “abc”,一共要产生5个字符串
-
StringBuilder类是字符串缓冲区,可以提高字符串操作的效率,可以看成是长度可变的字符串
底层也是数组,但没有被final修饰,byte[] value = new byte[16];(数组初始容量是16)
-
StringBuilder在内存中始终是一个数组,占用空间少,效率高。
如果超出了StringBuilder的容量,会自动扩容
StringBuilder的构造方法
StringBuilder的常用构造方法有2个:
- public StringBuilder():构造一个空的StringBuilder容器
- public StringBuilder(String str):构造一个StringBuilder容器,并将字符串添加进去
StringBuilder的常用方法
StringBuilder的常用方法有2个:
-
public StringBuilder append(...):添加任意类型数据的字符串形式,并返回当前对象自身。
因为append方法返回的也是StringBuilder类型的,所以可以使用链式方法调用连续添加元素.
使用append方法无需接收返回值
public class Demo09 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); StringBuilder sb2 = sb.append("Hello").append(3).append("World").append('@'); System.out.println(sb2); System.out.println(sb == sb2);//地址值一样,相当于append方法返回的是this } }
-
public StringBuilder reverse();反转StringBuilder中的元素
-
public String toString():将当前StringBuilder的对象转为String对象
StringBuilder和String可以相互转换
- String转StringBuilder可以使用StringBuilder类的构造方法
- StringBuilder转String可以使用StringBuilder的toString方法
包装类
-
基本数据类型的数据,使用十分方便,但是没有对应的方法来操作这些数据,所以我们可以使用一个类,把基本数据类型的数据包装起来,这个类叫包装类。在包装类中可以定义一些方法,用来操作基本的数据类型。
-
Java中8种基本数据类型都有对应的包装类,在java.lang包下,一般使用无需导包。
int->Integer short->Short long->Long byte->Byte
float->Float double->Double
char->Character
boolean->Boolean
包装类装箱与拆箱
-
装箱:从基本数据类型转换为对应的包装类对象
构造方法:
Integer(int value):构造一个新分配的Integer对象,它表示指定的int值
Integer(String s):构造一个新分配的Integer对象,它表示String参数所指示的int值,其中传递的字符串必须是基本类型的字符串,否则会抛异常,“100”表示正确,"a"抛异常
静态方法:
static Integer valueOf(int i)返回一个表示指定的int值的Integer实例
static Integer valueOf(String s)返回保存指定的String值的Integer对象
-
拆箱:从包装类对象转换为对应的基本类型
成员方法:
int intValue()以int类型返回该Integer的值
public class Demo10 {
public static void main(String[] args) {
Integer integer = new Integer(22);
Integer integer1 = new Integer("3245");
Integer integer2 = Integer.valueOf(23);
System.out.println(integer2);
Integer integer3 = Integer.valueOf("4590");
System.out.println(integer3);
int num1 = integer2.intValue();
System.out.println(num1);
int num2 = integer3.intValue();
System.out.println(num2);
}
}
/*
output:
23
4590
23
4590
*/
自动装箱和自动拆箱
-
JDK1.5之后出现的新特性,基本类型的数据和包装类之间可以自动地相互转换
-
自动装箱:直接把int类型的整数赋值给包装类
Integer num = 1; 就相当于Integer num = new Integer(1);
-
num是包装类的对象不能直接参与运算,但是可以自动转换为基本类型的数据,再参与计算
num + 2; 就相当于num.intValue() + 2得到int类型的数字3
num = num + 2;就相当于num = new Integer(3);
-
ArrayList集合无法直接存储整数,可以存储Integer包装类
ArrayList<Integer> list = new ArrayList<>(); list.add(1);//自动装箱, 相当于自动执行了list.add(new Integer(1)); int num = list.get(0);//自动拆箱,相当于自动执行了list.get(0).intValue();
基本类型与字符串类型相互转换
-
基本类型转为字符串
-
基本类型数据值+""(加一个空字符串)
...
public class Demo11 { public static void main(String[] args) { int num = 1; String str = 1 + "" + 2; System.out.println(str);//output: 12 } }
-
使用包装类中的静态方法
static String toString(int i)返回指定整数的String对象
...
public class Demo11 { public static void main(String[] args) { String str = Integer.toString(3); System.out.println(str + 2);//output: 32 } }
-
使用String类中的静态方法
static String valueOf(int i)返回int参数的字符串表示形式
...
public class Demo11 { public static void main(String[] args) { String str = String.valueOf(32); System.out.println(str + 1);//output: 321 } }
-
-
字符串转基本类型
-
使用包装类的静态方法parseXx(String str)
static int parseInt(String s)
static double parseDouble(String s)
public class Demo11 { public static void main(String[] args) { int num = Integer.parseInt("787"); System.out.println(num + 2);//output: 789 } }
-
static Integer valueOf(String s)返回保存指定的String值的Integer对象
int num2 = Integer.valueOf("4545"); int num3 = Integer.parseInt("45"); System.out.println(num2 + 2);//output: 4547 System.out.println(num3 + 1);//output: 46
-
二、Collection集合、泛型
集合和数组的区别
-
集合:集合是Java中提供的一种容器,可以用来存储多个数据
-
区别:
-
数组的长度是固定的,而集合的长度是可变的
-
数组中存储的是同一类型的元素,可以存储基本的数据类型值或引用类型变量值。
集合不能存储基本数据类型变量值,集合存储的都是引用类型变量值,而且这些变量的引用类型可以不一样。在开发中当使用多种引用类型变量时,使用集合进行存储。
-
集合框架
(单列集合的体系结构)
-
Collection接口(单列集合最顶层的接口)有两大子接口(extends了Collection)List和Set
定义的是所有单列集合中共性的方法,所有的单列集合都可以使用共性的方法。
注意:Collection接口中没有带索引的方法
-
两大接口List和Set
List接口的特点:
- 有序的集合(存储和取出元素顺序相同)
- 允许存储重复的内容
- 有索引(可以使用普通的for循环遍历)
- List接口的实现类有Vector、ArrayList(底层是数组实现的,查询快,增删慢)、LinkedList(底层是链表实现的,查询慢,增删快)
Set接口的特点:
- 不允许存储重复的元素
- 没有索引(不能使用普通for循环遍历)
- TreeSet和HashSet是无序集合(存储和取出元素的顺序有可能不一致),但LinkedHashSet是有序集合
- Set接口实现类有TreeSet(底层是二叉树实现。一般用于排序)、HashSet(底层是哈希表+红黑树实现),其中HashSet的子类有LinkedHashSet(底层是哈希表+链表实现)
Collection中的方法
java.util.Collection接口是所有单列集合的最顶层的接口,里面定义了所有单列集合共性的方法
所有的单列集合都可以使用Collection接口中共性的方法
- boolean add(E e);向集合中添加元素, 成功添加返回true
- boolean remove(E e);删除集合中的某个元素,若没有该元素则返回false
- void clear();清空集合中的所有元素
- boolean contains(E e);判断集合中是否包含某个元素,不包含该元素返回false
- boolean isEmpty();判断集合是否为空,不空返回false
- int size();获取集合的长度, 返回集合元素的个数
- Object[] toArray();集合转成一个数组
public class Demo01 {
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();//创建集合可以使用多态
System.out.println(coll);//output:[] //说明ArrayList父类的父类中重写了toString方法
}
}
Iterator接口(迭代器)
Java.util.Iterator接口:迭代器(对集合进行遍历,因为Collection中没有带索引的方法,所以规定也不能使用索引进行遍历,只能用迭代器来实现遍历,其实也是为了兼顾一些没有索引的集合)
-
两个常用方法
boolean hasNext()如果仍有元素可以迭代,则返回true
E next()返回迭代的下一个元素
-
Iterator迭代器,是一个接口,无法直接使用,需要使用Iterator接口的实现类对象,获取方式比较特殊
Collection接口中有一个方法叫iterator(), 这个方法返回的就是迭代器的实现类对象,Iterator
iterator() 返回在此collection(Collection类型的对象)的元素上进行迭代的迭代器 -
注意事项:
迭代器的泛型要和集合一致
Iterator
接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型 -
迭代器的使用步骤:
- 使用某个集合的对象调用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收一个实现类(多态)
- 使用Iterator接口中的方法hasNext()判断有没有下一个元素
- 使用Iterator接口中的方法next()取出集合中的下一个元素
public class Demo02 { public static void main(String[] args) { Collection<Integer> coll = new ArrayList<>();//多态 coll.add(5); coll.add(6); coll.add(2); coll.add(8); //Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型 Iterator<Integer> iter = coll.iterator();//iterator()方法返回的的是Iterator接口的实现类对象,这里用Iterator类型的变量去接收实现类的对象是多态的现象 while(iter.hasNext()) { System.out.println(iter.next()); } System.out.println("============="); Iterator<Integer> iter2 = coll.iterator(); for(;iter2.hasNext();) { System.out.println(iter2.next()); } } } /* output: 5 6 2 8 ============= 5 6 2 8 */
迭代器的实现原理
假设把ArrayList的对象想象成一个数组,它的0索引前还有-1索引,在最开始获取迭代器的实现类对象时假想有一个指针指向-1索引,当iter.hasnext()判断出有下一个元素,使用iter.next()取出下一个元素并且把当前指针向后移动一个位置
public class Demo02 {
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();//多态
coll.add(5);
coll.add(6);
coll.add(2);
coll.add(8);
//Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
Iterator<Integer> iter = coll.iterator();//获取迭代器的实现类对象,并且会把指针(索引)指向集合-1索引
while(iter.hasNext()) {//在这里会判断还有没有下一个元素
System.out.println(iter.next());//iter.next()作用是:1. 取出下一个元素 2. 会把指针向后移动一位
}
System.out.println("=============");
Iterator<Integer> iter2 = coll.iterator();
for(;iter2.hasNext();) {
System.out.println(iter2.next());
}
}
}
增强for循环(for each)
-
增强for循环也称for each循环是JDK1.5之后出现的一个高级for循环,专门用来遍历数组和集合。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删改查
-
Collection
接口继承了Iterable 接口,Collection extends Iterable public interface Iterable
实现这个接口允许对象成为"for each"语句的目标 所以所有的单列集合都可以使用增强for
格式:
for(集合/数组元素的数据类型 变量名(循环每次执行时,表示遍历到的第n个元素) : 集合名/数组名) {
System.out.println(变量名);
}
import java.util.ArrayList;
public class Demo04 {
public static void main(String[] args) {
newForLoopArr();
System.out.println("========");
newForLoopArrList();
}
public static void newForLoopArr() {
int[] arr = new int[] {1, 3, 4, 3, 2 ,1};
for(int i : arr) {
System.out.println(i);
}
}
public static void newForLoopArrList() {
ArrayList<Integer> list = new ArrayList<>();
list.add(6);
list.add(1);
list.add(3);
list.add(2);
for(int i : list) {
System.out.println(i);
}
}
}
/*
output:
1
3
4
3
2
1
========
6
1
3
2
*/
泛型
-
泛型是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型
-
泛型也可以看作是一种变量,用来接收数据类型
-
E是未知的数据类型,创建集合对象的时候,就会确定泛型的数据类型(会把数据类型作为参数传递)
E e : Element元素
T t: Type类型
//E是未知的数据类型 public class ArrayList<E> { public boolean add(E e) {} public E get(int index) {} } //创建集合对象的时候,就会确定泛型的数据类型(会把数据类型作为参数传递) public class ArrayList<String> { public boolean add(String e) {} public String get(int index) {} } public class ArrayList<Student> { public boolean add(Studnet e) {} public Student get(int index) {} }
使用泛型的好处
-
创建集合对象,不使用泛型
好处:集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
弊端:不安全,会出现异常
public class Demo05 { public static void main(String[] args) { show01(); } public static void show01() { ArrayList list = new ArrayList<>(); list.add("abc"); list.add("def"); list.add(5); Iterator iter = list.iterator(); while(iter.hasNext()) { Object obj = iter.next(); String str = (String)obj;//ClassCastException,当iter.next()为数字时,这里不可转为String类型,所以报错 System.out.println(str); System.out.println(str.length()); } } }
-
创建集合对象,使用泛型
好处:
- 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
- 把运行期的异常(代码运行之后会抛出的异常),提升到了编译期(写代码时会报错)
弊端:
泛型是什么类型,只能存储什么类型的数据
public class Demo06 { public static void main(String[] args) { show02(); } public static void show02() { ArrayList<String> list = new ArrayList<>(); list.add("abc"); list.add("peach"); Iterator<String> iter = list.iterator(); while(iter.hasNext()) { String str = iter.next(); System.out.println(str); System.out.println("len = " + str.length()); } } } /* output: abc len = 3 peach len = 5 */
含有泛型的类
-
泛型的定义
public class Demo07<E> { private E name; public E getName() { return name; } public void setName(E name) { this.name = name; } }
-
泛型的使用
在创建对象的时候确定泛型
public class Demo08 { public static void main(String[] args) { Demo07 nameUnknown = new Demo07();//创建对象时没有确定泛型 nameUnknown.setName("Mike"); Object newName = nameUnknown.getName();//不是多态,当返回泛型变量时就会返回Object类的变量 System.out.println(newName); Demo07<String> name = new Demo07<>();//创建对象时确定为String name.setName("Nick"); System.out.println(name.getName()); Demo07<Integer> nameNum = new Demo07<>();//创建对象时确定为Integer nameNum.setName(57); System.out.println(nameNum.getName()); } } /* output: Mike Nick 57 */
含有泛型的方法
-
定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
-
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){ 方法体; }
-
含有泛型的方法,在调用方法的时候确定泛型的数据类型
传递什么类型的参数,泛型就是什么类型
//含有泛型的静态方法,使用类名.方法创建,传递什么类型参数,泛型就是什么类型
public static <E> void genericClass(E e) {
System.out.println(e);
}
//含有泛型的普通方法,使用new创建对象再访问方法,传递什么类型参数,泛型就是什么类型
public <E> void genericClass2(E e) {
System.out.println(e);
}
含有泛型的接口
-
第一种:定义接口的实现类,实现接口,指定接口的泛型
public interface Iterator<E> { E next(); }
实现类中实现类名不用加泛型,实现的接口要加泛型的具体类型
//Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String public final class Scanner implements Iterator<String> { public String next() { } }
在实现类中确定泛型是什么类型,然后在实例化实现类可以不写泛型的部分,若使用多态方式用接口变量指向实现类对象,最好要写泛型,否则用到的泛型是Object类型
//InterfaceGeneric<String> ig2= new InterfaceGenericImpl();//多态的写法也可以 InterfaceGenericImpl ig = new InterfaceGenericImpl(); String str = ig.printInfo(); System.out.println(ig.printInfo()); System.out.println(ig.printString("Apple pen"));
-
第二种:含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
public interface List<E> { boolean add(E e); E get(int index); }
在实现类中实现类的类名后要加泛型,实现的接口后面也要加泛型
public class ArrayList<E> implements List<E> { public boolean add(E e) {} public E get(int index) {} }
在实例化实现类时确定泛型的类型,所以实例化时类名后最好加泛型的具体类型
//InterfaceGeneric<String> ig2 = new InterfaceGenericImpl2();//多态写法也可以 InterfaceGenericImpl2<String> ig2 = new InterfaceGenericImpl2(); String str2 = ig2.printInfo(); System.out.println(ig2.printInfo()); System.out.println(ig2.printString("Banana"));
泛型通配符
-
当使用泛型类或者接口时,传递参数的时候,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用
-
此时只能接收数据,不能往集合中存储数据
-
泛型通配符:
?代表任意的数据类型
-
使用方式:不能创建对象使用,只能作为方法的参数传递使用<?>(方法传参不能使用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具