泛型
什么是泛型,为什么引入泛型
这里考虑到方法代码的复用
不单单是对于功能模块的要求封装。同时需要对数据类型进行统一的约束!!!
根据用户的要求对应数据类型。
Java的泛型就是来解决对应的问题
格式:
<自定义泛型无意义大写英文字母占位符>
<T> <E> <K> <V>
泛型可以在方法中使用
泛型可以在类中使用
泛型可以在接口中使用
泛型可以在方法中使用
使用静态方法举例说明泛型在方法中的使用格式
格式:
public static <T> 返回值类型[自定义泛型] 方法名(必须存在一个对应泛型的参数) {
//方法体内可以使用自定义泛型
}
使用注意事项:
- 自定义泛型声明在返回值之前,已保证方法的参数和返回值都可以使用对应的泛型
- 方法声明的泛型,在方法的参数中必须有一个参数是对应声明的自定义泛型。当前参数是用于约束方法内所有使用到泛型的位置对应的具体数据类型是什么。
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) {
/*
* test方法使用了自定义泛型,传入的实际参数是什么类型
* 泛型T对应的具体数据类型是什么类型
*/
Integer test = test(123);
String test2 = test("你好");
Demo1 test3 = test(new Demo1());
String[] arr1 = { "烤羊排", "土豆牛肉", "黄焖鸡米饭", "烤羊肉串", "烤韭菜", "烤蒜"};
printArray(arr1);
System.out.println("-----------------------");
Integer[] arr2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
printArray(arr2);
System.out.println("-----------------------");
Demo1[] arr3 = {new Demo1(), new Demo1(), new Demo1(), new Demo1(), new Demo1()};
printArray(arr3);
}
/*
* 声明一个带有自定义泛型的方法
*/
/**
* 带有自定义泛型声明的方法
*
* @param <T> 自定义泛型无意义占位符
* @param t 指定的泛型数据类型,由用户约束
* @return 对应的T类型, 具体数据类型由用户约束
*/
public static <T> T test(T t) {
return t;
}
/*
* 展示任意类型数组中数据的方法
*/
/**
* 展示任意类型数组的方式
*
* @param <T> 自定义泛型无意义占位符
* @param arr 用户指定类型的数组,同时约束
自定义泛型对应的具体数据类型
*/
public static <T> void printArray(T[] arr) {
for (int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
泛型可以在类中使用 [有一些难]
格式:
class 类名<自定义泛型无意义大写字母占位符> {
//在类内的成员变量和成员方法都可以使用自定义泛型
//建议: 成员变量不建议使用自定义泛型
}
使用注意事项:
- 类声明自定义泛型,需要通过创建对象的方式来约束
TypeA< String> typeA = new TypeA< String>(); Eclipse标准写法
TypeA< String> typeA = new TypeA<>(); IDEA写法 - 类声明泛型约束之后,在类内的所有成员方法中使用的泛型都是类约束的泛型具体数据类型
- 如果没有约束类声明泛型,所有使用到泛型的位置都是Object,【不推荐】
- 类声明的自定义泛型不能用于类内的静态方法【没有对象】
/*
* 定义一个带有自定义泛型的类
*/
class TypeA<T> {
/**
* 使用和类名一致的自定义泛型,需要通过【类对象】来约束
*
* @param t 对应泛型具体数据类型的参数
* @return 对应泛型具体数据类型的返回值
*/
public T test1(T t) {
return t;
}
/**
* 使用和类名一致的自定义泛型,需要通过【类对象】来约束
*
* @param t1 对应泛型具体数据类型的参数
* @param t2 对应泛型具体数据类型的参数
*/
public void test2(T t1, T t2) {
System.out.println(t1);
System.out.println(t2);
}
/*
* 为什么类名自定义的泛型不能用于当前类内的静态成员方法
* 静态成员方法是在类文件的加载阶段准备完毕,已经可以明确的保证该方法的是可以执行的。
* 如果使用类声明的自定义泛型,对应泛型的具体数据类型需要在创建类对象之后才可以明确。
* 当前静态方法在类文件加载阶段无法明确泛型数据类型是哪一个,也就无法保证执行,【没有对象】
*
* 如果方法想要使用自定义泛型,自娱自乐,自己定义自己用!!!
* 要求自定义的泛型占位符和类声明泛型占位符不一致,避免没有必要的歧义,提高阅读性
*/
public static <E> E staticMethod(E e) {
return e;
}
}
public class Demo2 {
public static void main(String[] args) {
/*
* 创建带有自定义泛型类的对象
* TypeA<String> typeA = new TypeA<String>(); Eclipse标准写法
* TypeA<String> typeA = new TypeA<>(); IDEA写法
*/
// 明确告知编译器,这里泛型对应的具体数据类型是String类型
// 类内所有使用到泛型占位符的位置都是的T类型
TypeA<String> typeA = new TypeA<String>();
String test1 = typeA.test1("北京烤鸭");
typeA.test2("卤煮火烧", "豆汁");
// 明确告知编译器这里泛型对应的具体数据类型是Demo2类型
TypeA<Demo2> typeA2 = new TypeA<Demo2>();
Demo2 test12 = typeA2.test1(new Demo2());
typeA2.test2(new Demo2(),new Demo2());
/*
* 没有告知编译器泛型对应的具体数据类型,那么在类内的所有非静态
* 成员方法方法中使用到的泛型, 都是Object类型,虽然可以支持任意
* 类型传入参数,但是缺少了数据类型一致化的约束。
*
* 不推荐!!!
*/
TypeA typeA3 = new TypeA();
}
}
泛型可以在接口中使用
接口
interface 接口名 {
成员变量缺省属性:public static final 定义时必须初始化,并且初始化之后无法修改
成员方法缺省属性:public abstract 方法没有方法体
有方法体如何使用:default修饰默认方法,非强制实现方法
}
泛型在接口中使用格式:
interface 接口名<自定义泛型无意义占位符> {
//问题: 接口的泛型是否可以用于成员变量中 T t
//定义时必须初始化,但是当前泛型的数据类型是不明确的,无法进行初始化过程。和final有关。
//接口中的泛型有且只能用于成员方法!
}
使用注意事项:
-
接口声明的泛型,有且只能用于类内的成员方法。
-
遵从带有自定义泛型的接口的类,一种可以遵从过程中明确告知泛型具体类型,一种是在创建类对象是明确泛型具体数据类型。
-
class TypeA< T> implements A< String> 正确
接口的泛型明确接口中的方法,使用泛型对应的具体数据类型类声明的泛型用于自定义约束自己类内的方法 -
class TypeA< E> implements A< T> 错误
接口中的泛型没有明确数据类型,也无法通过类创建对象的过程中明确泛型对应的具体数据类型,无法编译。
interface A<T> {
void test(T t);
}
/*
* 接口带有泛型,遵从接口的实现类有两种方案可以implements当前接口
* 1. 自由
* 2. 规矩
*/
/**
* 自由方式,类名之后和接口同名自定义泛型,泛型对应的具体数据类型是
* 需要在创建当前类对象是进行约束使用。
*
* @author Anonymous
*
* @param <T> 遵从接口和接口一致的泛型
*/
class TypeB<T> implements A<T> {
@Override
public void test(T t) {
System.out.println("自由模式");
}
}
/**
* 规矩模式,遵从接口时,接口使用泛型对应的具体数据类型已经明确
* 在类内使用接口中缺省属性为public abstract方法时,泛型已确认
*
* @author Anonymous
*/
class TypeC<T> implements A<String> {
@Override
public void test(String t) {
System.out.println("规矩模式");
}
}
public class Demo3 {
public static void main(String[] args) {
/*
* 自由模式
*/
TypeB<Integer> typeB = new TypeB<Integer>();
typeB.test(10);
TypeB<Character> typeB2 = new TypeB<Character>();
typeB2.test('A');
TypeC typeC = new TypeC();
typeC.test("比较无聊,但是很规矩");
}
}
匿名内部类
Anonymous Inner Type
没有名字的类~~
/*
* 匿名内部类演示
*/
interface A {
void test();
}
/**
* 非abstract修饰TypeA遵从接口A,要求强制实现接口A中的test方法
* @author Anonymous
* */
class TypeA implements A {
@Override
public void test() {
System.out.println("TypeA遵从A接口,实现A接口中的test方法");
}
}
public class Demo1 {
public static void main(String[] args) {
TypeA typeA = new TypeA();
typeA.test();
/*
* A接口的引用a, new 调用A接口的构造方法【注意】这里不是创建A接口对象
* Anonymous Inner Type提示是完成了一个要求是【遵从】接口A必须实现的方法
* * 大括号{}里面的内容和一个普通类遵从接口A的效果是一模一样的。
* 大括号里面的内容可以认为是【类的本体】
* 但是大括号之前没有类名,这就是匿名内部类。
* * new关键字在内存堆区申请了空间,创建了一个隐含遵从接口A的匿名内部类对象
* 并且把该对象的空间首地址,赋值给接口A的引用数据类型变量,还有一个知识点
* 接口的引用指向遵从接口的类对象,这就是多态!!!
* * low!!!
*/
A a = new A() {
@Override
public void test() {
System.out.println("匿名内部类的对象赋值给接口的引用");
}
};
a.test();
// 匿名内部类的匿名对象直接调用实现的方法 little low!!!
new A() {
@Override
public void test() {
System.out.println("匿名内部类的匿名对象直接调用方法");
}
}.test();
// 匿名内部类的匿名对象直接作为方法的参数 终极奥义!!!
testInterface(new A() {
@Override
public void test() {
System.out.println("匿名内部类的匿名对象直接作为方法的参数");
}
});
}
/**
* 需要接口A的实现类对象作为方法的参数
* * @param a 接口A的实现类对象
*/
public static void testInterface(A a) {
a.test();
}
}
集合
为什么要用集合
开发中会使用大量相同数据类型的情况。
如果使用数组来解决问题
- 数组能够使用的方法非常少,功能方法需要程序员自己完成。
- 数据类型单一化,不支持多种情况。
- 数组容量不可以更改。
集合为解决问题而生:
- 方法多种多样,基本功能完善
- 数据类型支持多样化,但是又不失数据类型一致要求
- 容量可以变,并且不用开发者操心
集合架构
Java中集合的【总接口】Collection< E>。
Java中所有和集合有关的内容,都是Collection< E>接口的子接口或者实现类interface Collection< E>
interface List<E> List接口,有序可重复
class ArrayList<E>【重点】可变长数组结构。原码实现,了解其中的特征,性能....
class LinkedList<E>【重点】双向链表结构
class Vector<E>【远古时代】JDK1.0 线程安全的
ArrayList<E>,如果不考虑线程安全问题,建议使用ArrayList<E>
interface Set<E> Set接口,无序不可重复
HashSet<E> 底层存储数据的方式是采用哈希表方式
TreeSet<E> 底层存储数据的方式一个平衡二叉树方式
Collection接口下的常用方法
增:
boolean add(E e);
存入元素到当前集合对象中,这里要求的数据类型是E类型,也就是泛型对于
的具体数据类型 。
boolean addAll(Collection<? extends E> c);
? extends E 泛型的上限要求存入的集合c中,存储的元素要么是E类型,要么是E类的子类。
删:
void clear();清空整个集合
boolean remove(Object obj);删除集合中的指定元素
boolean removeAll(Collection<?> c);删除两个集合的交集
boolean retainAll(Collection<?> c);保留两个集合的交集
查:
int size();返回集合中有效元素个数
boolean isEmpty();判断当前集合是否为空
boolean contains(Object obj);判断指定元素在当前集合中是否存在
boolean containsAll(Collection<?> c);判断集合c是不是当前集合的子集合
import java.util.ArrayList;
import java.util.Collection;
public class Demo1 {
public static void main(String[] args) {
/*
* 因为Collection<E>使用一个接口,接口没有自己的类对象
* 这里使用Collection接口的实现类来完成演示过程 ArrayList<E>
*/
Collection<String> c = new ArrayList<String>();
c.add("82年的拉菲");
c.add("82年的雪碧");
c.add("82年的可乐");
c.add("82年的老雪");
System.out.println(c);
Collection<String> c1 = new ArrayList<String>();
c1.add("百威");
c1.add("福佳白");
c1.add("精酿啤酒");
c1.add("修道院啤酒");
c.addAll(c1);
System.out.println(c);
c.remove("82年的雪碧");
System.out.println(c);
//c.removeAll(c1);
//System.out.println(c);
//c.retainAll(c1);
//System.out.println(c);
System.out.println("size:" + c.size());
System.out.println(c.isEmpty());
System.out.println(c.contains("百威"));
System.out.println(c.contains("哈尔滨"));
System.out.println(c.containsAll(c1));
c1.add("野格");
System.out.println(c.containsAll(c1));
}
}
迭代器
通过集合对象获取对应的Iterator<E>迭代器
Iterator<E> iterator();
常用方法:
boolean hasNext();判断当前Iterator是否可以继续运行。
E next();获取Iterator当前指向元素,并且指向下一个元素。
void remove();删除
【注意】
- remove方法有且只能删除通过next方法获取的元素
- remove方法如果想要使用,必须紧挨着next方法
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("烤羊排");
c.add("油焖大虾");
c.add("土豆牛肉");
c.add("黄焖鸡米饭");
c.add("麻辣香锅");
c.add("孜然肉片");
c.add("酸汤肥牛");
System.out.println(c);
/*
* 获取当前集合对应的Iterator迭代器对象
*/
Iterator<String> iterator = c.iterator();
/*
System.out.println("当前Iterator是否可以继续运行:" +iterator.hasNext());
System.out.println("获取当Iterator指向元:" + iterator.next());
System.out.println("获取当Iterator指向元:" + iterator.next());
iterator.remove();
System.out.println(c);
iterator.remove();
System.out.println(c);
*/
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
System.out.println(c.isEmpty());
}
}
Iterator使用注意问题
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> c = new ArrayList<String>();
c.add("烤羊排");
c.add("油焖大虾");
c.add("土豆牛肉");
c.add("黄焖鸡米饭");
c.add("麻辣香锅");
c.add("孜然肉片");
c.add("酸汤肥牛");
Iterator<String> iterator = c.iterator();
/*
* ConcurrentModificationException
* Iterator在创建的过程中,会对整个集合所有元素打招呼,记录每一个元素位置。
* Iterator在执行next方法过程中,会按照初始条件一个一个遍历
* 当前集合通过remove方法,删除已经被Iterator记录的元素时,
* 是有可能导致Iterator一脸懵逼!!!元素不见了!!!
*
* 这里就会发生冲突!
*
* 这里因为集合中元素,对于集合本身和当前Iterator而言是一个共享资源
* 不管是哪一方操作元素,都存在影响对方操作的情况。【共享资源冲突问题】
*
* ArrayList存储元素不是连续的吗,土豆牛肉删除了,他的位置不是会被后面的元素顶上来吗
*/
while (iterator.hasNext()) {
System.out.println(iterator.next());
// 这里通过集合删除土豆牛肉元素
// 后期代码中会出现很多相同名字方法,这里一定要注意!!!
// 调用当前方法的是哪一个
c.remove("酸汤肥牛");
}
}
}
来源:CSDN
作者:左耳不闻天下事
链接:https://blog.csdn.net/weixin_42036089/article/details/104682552