Java基础Day16

mac2022-06-30  107

day16

泛型 1.1 泛型的概念和使用 泛型: 广泛的类型.定义一个类,类中的方法的参数或者是返回值类型,不能确定,可以使用泛型来进行定义

举例: ArrayList中的add方法,可以存储任意的引用数据类型,因此方法的参数不确定,使用泛型表示 ArrayList arr = new ArrayList(); arr.add();

泛型的定义方式: 类比 ArrayList <泛型的声明> : E----->Elements 元素,就表示广泛的类型 泛型的声明规范 : 可以使用一个符合规范的大写字母来进行表示 通常,使用E,T----Type,也可以使用W,Q,S…

泛型的使用: 创建类对象时,给出具体的泛型的类型 ArrayList arr1 = new ArrayList();

1.2 泛型的好处和使用注意事项 泛型的好处:

泛型提高代码的安全性,将运行时会发生的转型错误,提前到代码的编译环节. 带有泛型的集合,集合中能存储的数据类型就已经确定,保证集合中元素类型一致,不会发生转型的异常泛型可以省略掉强制类型转换过程.存储数据时,已经确定数据类型,获取数据时,直接获取到指定类型

代码 package com.zgjy.fanxing;

import java.util.ArrayList; import java.util.Iterator;

public class FanXingDemo1 {

public static void main(String[] args) { //getArr(); getFanXingArr(); } // 没有泛型的集合定义,的问题:

// 1. 可以在集合中存储多个数据类型,在进行集合遍历时,需要通过强制类型转换得到集合中的元素类型 // 2. 因为集合中存储的存储的类型不一致,强制类型转换时报错 public static void getArr() { ArrayList arr = new ArrayList(); arr.add(“a”);// String arr.add(“b”); arr.add(“c”); arr.add(12);// Integer

for(int i = 0 ; i < arr.size() ; i ++) { Object obj = arr.get(i); String s = (String)obj ; System.out.println(s); } } // 有泛型的集合定义: 1. 确定集合的数据类型,提高代码的安全性 2. 获取元素不需要强制类型转换,比较方便 public static void getFanXingArr() { // 定义ArrayList加上泛型 // 表示: String类型就是arr这个对象的泛型,属于一个已知的类型 // 使得集合ArrayList中只能存储String类型 ArrayList<String> arr = new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("hello"); //arr.add(1234); Iterator<String> it = arr.iterator(); while(it.hasNext()) { String s = it.next(); System.out.println(s); } }

}

泛型使用的注意事项:

定义的泛型类型,前后必须要保持一致泛型的推测: 从JDK1.7版本开始,后面的泛型类型可以省略,默认与前面泛型保持一致, 甚至,可以将 都省略掉

代码 // 泛型的注意事项 public static void fanXingAttion() { // 1. 定义的泛型类型,前后必须要保持一致 // ArrayList arr = new ArrayList(); 错误代码 ArrayList arr = new ArrayList(); // ArrayList arr3 = new ArrayList(); 错误代码

// 2. 泛型的推测: 从JDK1.7版本开始,后面的泛型类型可以省略,默认与前面泛型保持一致 // 甚至,可以将<Integer> 都省略掉 ArrayList<String> arr1 = new ArrayList<>(); ArrayList<String> arr2 = new ArrayList(); }

1.3 带有泛型的类 泛型类: 类上是带有泛型的 泛型类的定义方式: 修饰符 class 类名<泛型类型1,泛型类型2…>{

}

泛型类的使用:

泛型类型在定义时,定义规则: 是一个符合标识符规范的大写字母,泛型类型可以定义多个泛型定义时机: 在创建类对象时,指定泛型的具体类型泛型声明具体类型之后,就是一个已知的类型,可以在整个类中使用

代码 package com.zgjy.fanxing;

import java.util.ArrayList;

// 定义一个带有泛型的类 // 2. 泛型T在整个类中都可以使用 public class FanXingClass { // 成员变量 ArrayList private ArrayList arr = new ArrayList();

// 向成员变量所表示的集合中添加元素,在方法中使用了类上的泛型类型T public T addElements(T t) { arr.add(t); return t; } public static void main(String[] args) { // 1. 创建类对象时,指定泛型T的具体类型 FanXingClass<String> class1 = new FanXingClass<>(); String s = class1.addElements("abc"); String s1 = class1.addElements("world"); System.out.println(s);// abc System.out.println(s1);// world System.out.println(class1.arr);// [abc, world] }

}

1.4带有泛型的方法 泛型的方法: 方法上面带有泛型 泛型方法的定义: 修饰符 <泛型类型1,泛型类型2,…> 返回值类型 方法名(参数列表){

}

泛型方法的使用:

方法上的泛型,可以在方法内部当做已知类型使用非静态的方法,如果没有定义方法的泛型,可以使用类中的泛型静态方法,只能在方法上自己定义泛型. 如果静态方法上没有定义泛型,也不能使用类上的泛型(类上的泛型,在创建类对象时确定具体类型,静态优先于对象存在,因此静态不能使用类上面的泛型)

代码 package com.zgjy.fanxing; import java.util.ArrayList; public class FanXingMethod {

public static void main(String[] args) { FanXingMethod.getArr("a"); FanXingMethod.getArr("b"); } // 静态方法不能使用类上的泛型E,原因: 静态中不能使用非静态的类型 public static <T> void getArr(T t) { ArrayList<T> list = new ArrayList(); list.add(t); System.out.println(list); }

}

1.5带有泛型的接口 典型案例: Collection Iterator 泛型接口的定义: 修饰符 interface 接口名<泛型类型1,泛型类型2…>{

}

泛型接口的使用:

接口中定义的泛型类型,可以作为已知类型在接口中使用接口的实现类 实现类不带有泛型,要求现实时,接口中的泛型具体类型需要制定好 class impl implements MyInterface{

} 2) 实现类带有泛型的类,接口中不需要指定泛型

class impl implements MyInterface{

}

代码 package com.zgjy.fanxing; // 定义一个带有泛型的接口 // 1. 没有泛型的实现类 2. 带有泛型的实现类 public interface FanXingInterface { public abstract void getT(T t); }

// 没有泛型的实现类 class ShiXian1 implements FanXingInterface{

@Override public void getT(String t) { // TODO Auto-generated method stub }

}

// 带有泛型的实现类 class ShiXian2 implements FanXingInterface{

@Override public void getT(T t) { // TODO Auto-generated method stub }

}

1.6 泛型的通配符和泛型擦除(了解)

泛型的通配符: ? , 匹配任意类型的泛型 ArrayList<?> arr = new ArrayList(); ArrayList<?> arr1 = new ArrayList();addAll(Collection<? extends E> c) : 方法参数的集合,具有泛型,方法调用时,可以输入泛型E以及泛型的所有的子类Comparator<? super E> : 表示输入的泛型可有是E泛型或者是E的父类泛型

泛型擦除:

代码编写的时候,定义了泛型,使用了泛型,但是在编译的.class文件中,是没有泛型的,称这种现象为泛型的擦除,泛型就是为了在代码的编写环节提高代码的安全性

Set 集合 2.1 Set集合的介绍 Set接口是Collection接口的子接口, 来自JDK java.util.Set Set 集合的特点:

无序: 集合中元素的存入顺序和元素的取出顺序不一致

没有索引: 只能使用Collection中的方法进行元素的操作

不存储重复元素(元素唯一): 相同的元素在set集合中只能存储一个

List 集合特点:

有序: 集合中元素的存入顺序与元素的取出顺序保持一致有索引: 可以通过索引获取到集合中的元素, 0 ----- 集合长度(size())-1可存储重复元素 : 根据不同索引位置区分重复元素

Set集合是一个接口,需要通过实现类操作Set中方法, HashSet Set set = new HashSet();// 接口的多态性

代码 package com.zgjy.set; import java.util.ArrayList; import java.util.HashSet; import java.util.Set;

public class SetDemo {

public static void main(String[] args) { // 创建一个Set集合 Set<String> set = new HashSet<>(); // 向set集合中添加元素 set.add("a"); set.add("cd"); set.add("dd"); // 元素的存取无序 System.out.println(set);// [dd, a, cd] set.add("cd"); set.add("hello"); set.add("a"); System.out.println("-----"+set);//[dd, a, cd, hello] ArrayList<String> list = new ArrayList<>(); // 向list集合中添加元素 list.add("a"); list.add("cd"); list.add("dd"); // 元素的存取无序 //System.out.println(list);// [dd, a, cd] list.add("cd"); list.add("hello"); list.add("a"); System.out.println("++++++++++"+list);//[dd, a, cd, hello] }

}

2.2 Set集合的遍历方式

Collection接口中,方法toArray() : 将一个集合转换成一个Object类型的数组,遍历数组回去到集合中的每一个元素Collection 接口中,方法toArray(T[] t) : 将一个集合中的元素放置到参数的T[]中,进行T[] 遍历,得到的每一个元素类型都是T类型,避免在数组循环时的强制类型转换迭代器遍历 : 通过集合中重写的iterator() 方法,获取到迭代器对象Iterator<集合中存储的数据类型>, 通过Iterator的方法, hasNext() 集合中是否有下一个元素, 如果有 next() 获取到这个元素 说明: 迭代器,不论集合中是否有索引,都可以使用迭代器 原理: 获取集合中的元素,元素是否带有索引迭代器不关注,只获取元素增强for : for循环的增强表示方式,forEach (重点掌握)

增强for的语法格式: for(数据类型 变量名 : 集合或者数组){ // 变量就是获取到集合或者是数组中的每一个元素

}

说明:

集合(List,Set)或者数组,表示的是将要被遍历的容器数据类型 : 表示集合或者数组中存储的数据类型变量名 : 每次循环获取到的容器中的元素的表示方式

注意: 增强for 对于集合或者是数组是否有索引无要求,将容器中的每一个元素获取到 增强for底层就是使用迭代器的原理来实现 增强for 也会发生并发修改异常(使用增强for不要向集合中添加元素)

代码 package com.zgjy.set; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class SetFor {

public static void main(String[] args) { Set<String> set = new HashSet(); set.add("a"); set.add("b"); set.add("c"); set.add("d"); //getSet1(set); //getSet2(set); // getSet3(set); getSet4(set); } // 1. Set集合的遍历: toArray()-----> Object[] public static void getSet1(Set<String> set) { Object[] obj = set.toArray(); // 遍历数组 for(int i = 0 ; i < obj.length ; i ++) { Object ob = obj[i]; // 多态的向下转型 String s = (String)ob; System.out.println(s);// a b c d } } // 2. Set集合的第二种遍历方式: toArray(T[] t) public static void getSet2(Set<String> set) { // 需要参数T[],先创建一个数组,因为set中时String类型的元素,创建String[], // 数组大小设置为set集合中的元素的大小 String[] ss = new String[set.size()]; String[] ss1 = set.toArray(ss); for( int i = 0 ; i < ss1.length ; i ++) { String s = ss1[i]; System.out.println("222---"+s);// } } // 3. 迭代器遍历方式 public static void getSet3(Set<String> set) { //1 . 获取到迭代器对象 Iterator<String> it= set.iterator(); // 2. 循环获取集合中的每一个元素 while(it.hasNext()) { String s = it.next(); System.out.println("===="+s);// a b c d } } // 4. 增强for进行set集合的遍历 public static void getSet4(Set<String> set) { for(String s : set) {// 将集合中的每一个元素直接获取到,元素使用变量s表示 System.out.println("******"+s); } }

}

2.3 Set集合保证元素唯一性 2.3.1 Set集合存储JDK已经写好的数据类型 JDK已经预置好的引用数据类型,存储在set集合中可以有效去重复 JDK中的引用数据类型,都是重写过hashCode和 equals方法

代码 package com.zgjy.set;

import java.util.HashSet; import java.util.Set;

public class SetOnly {

public static void main(String[] args) { setDemo1(); } // Set集合存储JDK预置好的数据类型 public static void setDemo1() { Set<String> set = new HashSet<>(); set.add("a"); set.add("b"); set.add("a"); set.add("a"); set.add("c"); System.out.println(set); // [a, b, c] Set<Integer> set1 = new HashSet<>(); set1.add(1); set1.add(11); set1.add(2); set1.add(11); set1.add(1); System.out.println(set1);// [1, 2, 11] }

}

2.3.2 Set集合存储自定义类型 Set集合中存储的自定义的引用数据类型,要想做到根据对象中的成员变量的值进行去重复功能,在自定义类型中,重写hashCode 和 equals

关注Object类中,hashCode 和 equals 两个方法,因为这两个方法都是判断是否是同一个对象的方法 hashCode : 获取每一个对象的int 类型的数值,Object源代码中,每一个new对象都有一个不同的int 类型的返回值 equals : 用于比较两个对象是否相等,Object源码,比较两个对象的地址, 使用== 比较

自定义类型中,重写hashCode 和 equals 重写hashCode功能 : 将类中的成员变量的值获取到,转换成int类型的数值 重写equals 功能: 就是比较两个对象的成员变量值是否相等

发现重写hashCode 和 equals 方法后,自定义的数据类型对象,可以将相同成员变量的对象进行去重复操作

代码 package com.zgjy.set; public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } public Person() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return “Person [name=” + name + “, age=” + age + “]”; }

@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }

}

package com.zgjy.set; import java.util.HashSet; import java.util.Set; public class SetOnly { public static void main(String[] args) { setDemo2(); }

// Set集合中存储自定义的数据类型 public static void setDemo2() { // 要求: Person 对象存储在set集合时,不同的对象 name 和 age 的值都不相等 Set<Person> set = new HashSet<>(); set.add(new Person("QQ",12)); set.add(new Person("QQ",12)); set.add(new Person("QQ糖",12)); set.add(new Person("大美",25)); System.out.println(set); }

}

2.3.3 Set集合的保证元素唯一原理 Set集合底层数组 + 链表的结构,通过hashCode和equals两个方法进行元素唯一性的验证

2.4 LinkedHashSet HsahSet的一个子类,LinkedHashSet 进行元素存储,能够保证存入的顺序和元素取出的顺序保持一致,其他方法,与hashSet基本上一致

最新回复(0)