Java基础深化和提高-------容器篇

一、泛型(Generics)

 为了能够更好的学习容器,我们首先要先来学习一个概念:泛型。
泛型基本概念,泛型是JDK5.0以后增加的新特性。
泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入。我们可以把“泛型”理解为数据类型的一个占位符(类似:形式参数),即告诉编译器,在调用泛型时必须传入实际类型.

参数化类型,白话说就是:
把类型当作是参数一样传递。
<数据类型> 只能是引用类型。

泛型的好处

在不使用泛型的情况下,我们可以使用Object类型来实现任意的参数类型,但是在使用时需要我们强制进行类型转换。这就要求程序员明确知道实际类型,不然可能引起类型转换错误;但是,在编译期我们无法识别这种错误,只能在运行期发现这种错误。使用泛型的好处就是可以在编译期就识别出这种错误,有了更好的安全性;同时,所有类型转换由编译器完成,在程序员看来都是自动转换的,提高了代码的可读性。

总结一下,就是使用泛型主要是两个好处:
1、代码可读性更好【不用强制转换】
2、程序更加安全【只要编译时期没有警告,运行时期就不会出现ClassCastException异常】

 类型擦除

编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。
泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。
泛型主要是方便了程序员的代码编写,以及更好的安全性检测。

 实时效果反馈
1.泛型是在JDK哪个版本中增加的新特性?
A  JDK5.0
B JDK6.0
C JDK7.0
D JDK8.0

2.泛型的本质是什么?
A 数据的参数化
B 对象的参数化
C 数据类型的参数化
D 基本类型的参数化

答案
1=>A 2=>C

泛型类

 泛型标记
定义泛型时,一般采用几个标记:E、T、K、V、N、?。他们约定
俗称的含义如下:

泛型标记 对应单词 说明
EElement在容器中使用,表示容器中的元素
TType表示普通的JAVA类
KKey表示键,例如:Map中的键Key
VValue表示值
NNumber表示数值类型
?  表示不确定的JAVA类型

泛型类的使用

 语法结构

public class 类名<泛型标识符号> {  }

public class 类名<泛型标识符号,泛型标识符号> {  }

 示例

public class Generic<T> {
  private T  flag;
  public void setFlag(T flag){
    this.flag = flag;
 }
  public T getFlag(){
    return this.flag;
 }
}
public class Test {
  public static void main(String[] args) {

    //创建对象时,指定泛型具体类型。
    Generic<String> generic = new Generic<>();

    generic.setFlag("admin");

    String flag = generic.getFlag();
    System.out.println(flag);
   
    //创建对象时,指定泛型具体类型。
    Generic<Integer> generic1 = new Generic<>();

    generic1.setFlag(100);

    Integer flag1 = generic1.getFlag();
    System.out.println(flag1);
 }
}

实时效果反馈

1.如下哪个选项是正确定义泛型类的语法
A public class <泛型标识符号> 类名
B public <泛型标识符号> class 类名
C <泛型标识符号> public class 类名
D public class 类名<泛型标识符号>

答案
1=>D

 泛型接口

 泛型接口和泛型类的声明方式一致。

泛型接口的使用

语法结构

public interface 接口名<泛型标识符号> {   }
public interface 接口名<泛型标识符号,泛型标识符号>{   }

 示例

public interface IGeneric<T> {
  T getName(T name);
}



//在实现接口时传递具体数据类型
public class IgenericImpl implements
Igeneric<String> {
  @Override
  public String getName(String name) {
    return name;
 }
}


//在实现接口时仍然使用泛型作为数据类型
public class IGenericImpl2<T> implements
IGeneric<T>{
  @Override
  public T getName(T name) {
    return name;
 }
}

public class Test {
  public static void main(String[] args) {
    IGeneric<String> igeneric= new  IGenericImpl();
    String name = igeneric.getName("old");
    System.out.println(name);
    IGeneric<String> igeneric1 = new IGenericImpl2<>();
    String name1 = igeneric1.getName("it");
    System.out.println(name1);
 }
}

实时效果反馈
1.如下哪个选项是正确定义泛型接口的语法
A public interface<泛型标识符号> 接口名
B public <泛型标识符号> interface 接口名
C <泛型标识符号> public interface 接口名
D public interface 接口名<泛型标识符号>

答案
1=>D

泛型方法 

 类上定义的泛型,在方法中也可以使用。但是,我们经常需要仅仅在某一个方法上使用泛型,这时候可以使用泛型方法。
调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型

 泛型方法的使用


 非静态方法

非静态方法可以使用泛型类中所定义的泛型,也可以将泛型定义在方法上。

 语法结构

//无返回值方法
public <泛型标识符号> void getName(泛型标识符号name) {  }

//有返回值方法
public <泛型标识符号> 泛型标识符号 getName(泛型标识符号 name) {  }

 示例

public class MethodGeneric {
  public <T> void setName(T name){
    System.out.println(name);
 }
  public <T> T getAge(T age){
    return age;
 }
}
public class Test2 {
  public static void main(String[] args) {

    MethodGeneric methodGeneric = new MethodGeneric();

    methodGeneric.setName("old");

    Integer age = methodGeneric.getAge(123);

    System.out.println(age);
 }

静态方法

静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型,所以必须要将泛型定义在方法上。

语法结构

//无返回值静态方法
public static <泛型标识符号> void setName(泛型标识符号 name){  }

//有返回值静态方法
public static <泛型标识符号> 泛型表示符号getName(泛型标识符号 name){  }

示例

public class MethodGeneric {
  public static <T> void setFlag(T flag){
    System.out.println(flag);
 }
  public static <T> T getFlag(T flag){
    return flag;
 }
}
public class Test4 {
  public static void main(String[] args) {

    MethodGeneric.setFlag("old");

    Integer flag1 =  MethodGeneric.getFlag(123123);

    System.out.println(flag1);
 }
}

实时效果反馈
1.如下哪个选项是正确定义泛型方法的语法
A <泛型标识符号> public void getName(泛型标识符号 name)
B public void <泛型标识符号> getName(泛型标识符号 name)
C public <泛型标识符号> void getName(泛型标识符号 name)
D public void getName <泛型标识符号>(泛型标识符号 name)

答案
1=>C

泛型方法与可变参数 

 在泛型方法中,泛型也可以定义可变参数类型。

语法结构

public <泛型标识符号> void showMsg(泛型标识符号... agrs){  }

示例

public class MethodGeneric {
  public <T> void method(T...args){

    for(T t:args){
      System.out.println(t);
     }

   }
}

public class Test5 {
  public static void main(String[] args) {
    MethodGeneric methodGeneric = new  MethodGeneric();

    String[] arr = new String[]{"a","b","c"};

    Integer[] arr2 = new Integer[]{1,2,3};

    methodGeneric.method(arr);

    methodGeneric.method(arr2);
  }
}

实时效果反馈
1.如下哪个选项是正确的在可变参数中使用泛型
A public <泛型标识符号> void showMsg(泛型标识符号... agrs)
B public void showMsg(<泛型标识符号>... agrs)
C public <泛型标识符号> void showMsg(<泛型标识符号>... agrs)

D public <泛型标识符号> void showMsg(Object... agrs)


答案
1=>A

 泛型中的通配符

 无界通配符
“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。

语法结构

public void showFlag(Generic<?> generic){  }

示例

public class Generic<T> {

  private T  flag;

  public void setFlag(T flag){
    this.flag = flag;
 }

  public T getFlag(){
    return this.flag;
  }
}


public class ShowMsg {
  public void showFlag(Generic<?> generic){
  
  System.out.println(generic.getFlag());
 }
}


public class Test3 {
  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new  Generic<>();
    generic.setFlag(20);
    showMsg.showFlag(generic);

    Generic<Number> generic1 = new  Generic<>();
    generic1.setFlag(50);
    showMsg.showFlag(generic1);

    Generic<String> generic2 = new Generic<>();
    generic2.setFlag("old");
    showMsg.showFlag(generic2);
 }
}

实时效果反馈
1.在泛型中,无界通配符使用什么符号来表示?
A !
B ?
C #
D *

答案
1=>B

 统配符的上下限定
 统配符的上限限定

 对通配符的上限的限定:<? extends 类型>  ?实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;

语法结构

public void showFlag(Generic<? extends Number> generic){ }

示例

public class ShowMsg {

  public void showFlag(Generic<? extends Number> generic){

        System.out.println(generic.getFlag());
  }
}


public class Test4 {

  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new  Generic<>();
    generic.setFlag(20);

    showMsg.showFlag(generic);
    Generic<Number> generic1 = new  Generic<>();

    generic1.setFlag(50);
    showMsg.showFlag(generic1);
 }
}

实时效果反馈
1.对通配符的上限的限定是指
A 实际类型只能是上限限定中所约定的类型;
B 实际类型只能是上限限定中所约定类型的子类型;

C 实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是上限限定中所约定的类型,也可以是约定类型的父类型;

答案
1=>C

 通配符的下限限定

 对通配符的下限的限定:<? super 类型>  ?实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

语法结构

public void showFlag(Generic<? super Integer> generic){ }

示例

public class ShowMsg {

  public void showFlag(Generic<? super Integer> generic){
  
     System.out.println(generic.getFlag());
   }
}


public class Test6 {

  public static void main(String[] args) {

    ShowMsg showMsg = new ShowMsg();
    Generic<Integer> generic = new Generic<>();
    generic.setFlag(20);
    showMsg.showFlag(generic);

    Generic<Number> generic1 = new Generic<>();
    generic1.setFlag(50);
    showMsg.showFlag(generic1);
 }
}

实时效果反馈

1.对通配符的下限的限定是指
A 实际类型只能是下限限定中所约定的类型;
B 实际类型只能是下限限定中所约定类型的子类型;
C 实际类型可以是下限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

答案

1=>D

泛型局限性和常见错误

 泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息。 类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。因此,使用泛型时,如下几种情况是错的:

 实时效果反馈 


1.如下哪个选项是错误的使用泛型?
A Generic
B Generic
C Generic
D Generic

答案
1=>D

二、容器介绍

容器简介

容器,是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等。

 程序中的“容器”也有类似的功能,用来容纳和管理数据。比如,如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理:

开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。 我们一般通过“容器”来容纳和管理数据。事实上,我们前面所学的数组就是一种容器,可以在其中
放置对象或基本类型数据。
数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从查询效率和类型检查的角度讲,数组是最好的。
数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。

 基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。

 实时效果反馈

1.Java中容器的作用是什么?
A 容纳数据
B 处理数据
C 生产数据
D 销毁数据

答案
1=>A

 容器的结构

结构图

 单例集合

 双例集合

 实时效果反馈

1.如下哪个接口不是容器接口?
A List
B Set
C Map
D Comparable

答案
1=>D

 单例集合


Collection接口介绍

Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。

 

 Collection接口中定义的方法

方法 说明
boolean add(Object element)增加元素到容器中
boolean remove(Object element)从容器中移除元素
boolean contains(Object element)容器中是否包含该元素
int size()容器中元素的数量
boolean isEmpty()容器是否为空
void clear()清空容器中所有元素
Iterator iterator()获得迭代器,用于遍历所有元素
boolean containsAll(Collection c)本容器是否包含c容器中的所有元素
boolean addAll(Collection c)将容器c中所有元素增加到本容器
boolean removeAll(Collection c)移除本容器和容器c中都包含的元素
boolean retainAll(Collection c)取本容器和容器c中都包含的元素,移除非交集元素
Object[] toArray()转化成Object数组

由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。

JDK8之后,Collection接口新增的方法(将在JDK新特性和函数式编程中介绍):

新增方法 说明
removeIf作用是删除容器中所有满足filter指定条件的元素
stream
parallelStream
stream和parallelStream 分别返回该容器的Stream视图表示,不同之处在于parallelStream()返回并行的Stream,Stream是Java函数式编程的核心类
spliterator可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,可以实现多线程操作提高效率

实时效果反馈 

1.如下哪个接口不是Collection接口的子接口?
A List
B Set
C Map
D Queue

答案
1=>C

更新中........ 


版权声明:本文为m0_58719994原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。