JavaSE——05集合及常用类库

141 阅读20分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

05 集合及常用类库


01、集合的定义

1)集合实际上是一个容器,可以容纳其他类型的数据。集合不能直接存放基本数据类型,另外集合也不能直接存储java对象,而存储的是java对象的内存地址(引用)

2)为什么集合在开发中使用较多?

集合是一个容器,是一个载体,可以一次容纳多个对象,实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询将10个数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来

list.add(100);//自动装箱

注意:

集合再java中本身就是一个容器,是一个对象,集合在任何时候存储的但是"引用"

在这里插入图片描述​编辑

3)java中每一个不同的集合,底层对应不同的数据结构,往不同的集合中存储元素,等于将数据放到了不同的数据结构(数据存储结构)当中。其中:数组、二叉树、链表、哈希表都是常见的数据结构

4)new ArrayList(); 创建一个集合,底层是数组

new LinkedList();创建一个集合对,底层是链表

new TreeSet(); 创建一个集合对象,底层是二叉树

5)集合类和集合接口都在在java.utill包下


02、集合的继承结构

在java中集合分为两大类:

一类是单个方式存储元素,这一类集合中超级父接口:java.utill.Collection;

一类是以键值对的方式存储元素,这一类元素中超级父类接口是:java.utill.Map;

2.1 超级父接口Collection

在这里插入图片描述​编辑

2.2 超级父接口Map

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dD9uIId3-1657728924925)(C:\Users\86177\Pictures\Saved Pictures\微信图片_20220117200816.png)]

2.3 总结

ArrayList:底层是数组

LinkedList:底层是双向链表

Vector:底层是数组,线程安全的,效率较低,使用较少

HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了

TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到HashMap集合key部分了

HashMap:底层是哈希表

Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少

Properties:是线程安全的,并且key和value只能存储字符串String

TreeMap:底层是二叉树,可以自动按照大小顺序排序

List集合存储元素的特点:

有序:存进去和取出来的顺序相同,每一个元素都有下标

可重复:存进去1,还可以存储1

Set集合存储元素的特点

无序:存进去和取出来的顺序不一定相同,元素没有下标

不可重复:存进去1,不可以再存储1

SortedSet集合存储元素特点
无序不可重复的,

元素可排序:可以按照大小顺序排列

Map集合的特点

Map集合的key,就是一个Set集合

往Set集合中放数据,实际上放到了Map集合的key部分


03、Collection接口

3.1 Collection存放的元素类型

没有使用“泛型”之前Collection可以存储Object的所有子类型

使用“泛型”之后,Collection只能存储某个具体的类型

集合中不能直接存储基本数据类型,也不能存储java对象,存储的是java对象的内存地址

3.2 Collection的常用方法

1)boolean add(Object e)向集合中添加元素

//多态机制,接口是抽象的,无法实例化
Collection c = new ArrayList();
//测试Collection接口中的常用方法
c.add(1200);//自动装箱,实际上是放进去一个对象的内存地址。Integer c = new Integer(1200);
c.add(3.14);//自动装箱
c.add(new Object());
c.add(new Student());
c.dd(true);//自动装箱
System.out.println("集合中的元素个数是:" + c.size());//5

2)int size()获取集合中的元素的个数

3)void clear()清理集合

4)boolean contains(Object o)判断当前集合中是否包含元素o,包含返回ture,不包含返回false;

c.add("绿巨人");
System.out.println(c.contains("绿巨人"));

5)boolean remove(Object o)删除集合中的某个元素

c.remove("绿巨人");

6)boolean isEmpty()判断该集合中元素中的个数是否为0

c.clear();
System.out.println(c.isEmpty());//true

7)*Object[ ] toArray()集合转换成数组

c.add(1200);
c.add(3.14);
c.add("12345");
//转换成数组
Object[] objs = c.toArray();
for(int i = 0 ; i<objs.length ; i++){
    //遍历数组
	object o = objs[i];
	System.out.println(o);//调用toString方法
}


04、集合遍历/迭代

4.1 如何遍历集合

迭代方法是Collection通用的一种方法,在Map集合中不能用。在所有的Collection以及子类当中使用

//创建集合对象
Collection c = new ArrayList();
//添加元素
c.add("abc");
c.add("def");
c.add(100);
c.add(new Object());
//对集合Collection进行遍历/迭代
//第一步:获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();//调用iterator()拿到迭代器,迭代器是个对象,其中对象有两个方法
//第二步:通过以上获取的迭代器对象开始迭代/遍历集合
/*
	以下两个方法是迭代器对象Iterator中的方法:
	boolean hasNext()如果仍有元素可以迭代,则返回ture。
	Object next() 返回迭代的下一个元素。
*/



while(it.hasNext()){
    Object obj = it.next();
    System.out.println(obj);
}

在这里插入图片描述​编辑

Collection c = new ArrayList();
//添加元素
c.add(1);//自动装箱成Integer类型
c.add(2);
c.add(3);
c.add(4);
//迭代集合
Iterator it = c.Iterator();
while(it.hasNext()){
    //存进去什么类型,取出来还是什么类型
 	Object obj = it.next();
    if(obj instanceof Integer){
        System.out.println("Integer类型");//这里调用toString方法转换成了字符串
    }
}

4.2 迭代器是通用的

HashSet集合:无序不可重复

无序:存进去和取出的顺序不一定相同;

不可重复:存了100不可能再存100;

Collection c2 = new HashSet();
//添加元素
c2.add(100);//自动装箱成Integer类型
c2.add(2);
c2.add(33);
c2.add(43245);
c2.add(100);
//迭代集合
Iterator it2 = c2.Iterator();
while(it2.hasNext()){
    System.out.println(it2.next());//这里调用toString方法转换成了字符串
}

注意:集合结构发生改变,迭代器必须重新获取

迭代过程中不能调用remove方法等改变集合的结构;如果需要在迭代过程中必须使用迭代器的remove方法删除元素

Collection c2 = new HashSet();
//添加元素
c2.add(100);//自动装箱成Integer类型
c2.add(2);
c2.add(33);
c2.add(43245);
c2.add(100);
//迭代集合
Iterator it2 = c2.Iterator();
while(it2.hasNext()){
    //删除的一定是迭代器指向的当前元素
    it2.remove();//如果需要在迭代过程中删除元素,必须使用迭代器的remove方法
    //调用迭代器删除元素会更新迭代器,而使用集合删元素结构发生改变需要重新获取迭代器
    System.out.println(it2.next());//这里调用toString方法转换成了字符串
}

Collection c = new ArrayList();
//此时获取的迭代器,指向的是那是集合中没有元素状态下的迭代器
Iterator it = c.iterator();
//添加元素
c.add(1);
c.add(2);
//此时再添加元素,无法迭代,必须重新获取迭代器


05、深入Collection的contains方法

contains方法是判断集合中是否包含某个元素的方法

底层调用了equals方法进行比较,并且String重写了equals方法,如果不重写equals方法则会比较内存地址

放在集合中类型一定要重写equals方法,八大基本类型的包装类都重写了equals方法

public class asdf{
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
        //向集合中存储元素
        String s1 = new String("abc");
        ((ArrayList) c).add(s1);
        String s2 = new String("def");
        ((ArrayList) c).add(s2);
        //新建的对象String
        //集合中是否包含x?包含返回ture
        String x =new String("abc");
        System.out.println(c.contains(x));//true
        //contains底层调用了equals方法,与字符串常量池无关,与开辟在堆区的内存地址也无关
    }
}

在这里插入图片描述​编辑

import java.util.ArrayList;
import java.util.Collection;

public class asdf{
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
        //向集合中存储元素
        String s1 = new String("abc");
        ((ArrayList) c).add(s1);
        String s2 = new String("abc");
        //((ArrayList) c).add(s2);
        c.remove(s2);//remove调用了equals方法
        //这里s2和s1是相同的,所以删s2就是删s1
        //如果s2也创建出来了,那么删s2就是删s2,
        //所以只是删除集合中找到的第一个元素,就算后面还有相同的,也不会删除
        System.out.println(c.size());
    }
}

06、String类

6.1 String字符串的存储原理

关于Java JDK中内置的一个类:java.alng.String

1)String表示字符串类型,属于引用数据类型,不属于基本数据类型

2)在java中随随便便用双引号括起来的都是String对象

3)java中规定,双引号括起来的字符串是不可变的

4)JDK当中双引号括起来的字符串存储在方法区的"字符串常量池"当中的【字符串使用过于频繁,提高效率】

5)String类当中已经重写了equals()方法,直接调用即可

public class Text{
	public static void main(String[] args){
		//下面程序一共创建了三个对象,字符串常量池当中"xyz"
		堆内存当中两个对象,因为new两次
		String x = new String("xyz");
		String y = new String("xyz");
		System.out.println(x == y);//false
		
		String s1 = "hello";
		String s2 = "hello";
		System.out.println(s1 == s2);//true
		
		//所以直接直接赋值和创建对象的字符串比较结果是不同的,所以要通过equals()方法进行比较
		System.out.println("hello".quals(s1));//建议采用这种方法,避免了空指针异常
		System.out.println(s1.equals("hello"));//存在空指针异常的风险
		
		//i保存的是100
		int i = 100;
		//s保存的是字符串对象在字符串常量池当中的内存地址
		String s = "abc";
	}
}

在这里插入图片描述​编辑

2.2 String类常用的构造方法

1)String s = new String(" ");

2)String s = " " ; (最常用的方法)

3)String(byte数组); 【将byte数组全部转换为字符串】

byte[] bytes = {97,98,99};//97是a,98是b,99是c
String s2 = new String(bytes);
System.out.println(s2);//abc

4)String(byte数组,起始下标,长度); 【将byte数组一部分转换为字符串】

byte[] bytes = {97,98,99};//97是a,98是b,99是c
String s3 = new String(bytes,1,2);
System.out.println(s3);//bc

5)String(char数组); 【将char数组全部转换成字符串】

Char[] chars = {'我''是''中''国''人'};
String s4 = new String(chars);

s4的输出结果为"我是中国人"

6)String(char数组,起始下标,长度); 【将char数组一部分转换为字符串】

Char[] chars = {'我''是''中''国''人'};
String s5 = new String(chars,23);

s5的输出结果为"中国人"

2.3 String常用的方法

2.3.1 【取出某个元素】

char charAt(int index)

char c = "中国人".charAt(1);
System.out.println(c);//国

2.3.2 【字符串比较】

int compareTo(String anotherString)

逐位比较: 前后一致(0)、前小后大(-1)、前大后小(1)

int result = "abc".compareTo("abc");
System.out.println(result);//0

2.3.3 【判断是否包含子字符串】

boolean contains(CharSequence s)

System.out.println("HelloWorld.java".contains(".java"));//true

2.3.4 【判断是否以子字符串开始】

boolean endWith(String suffix)

System.out.println("text.java".endWith("text"));//true

2.3.5 【判断是否以子字符串结尾】

boolean startWith(String suffix)

System.out.println("HelloWorld.java".startWith(".java"));//true

2.3.6 【比较是否相等】

boolean equals(Object anObject)

equals()方法只能看出是否相等,

compareTo()方法可以看出谁大谁小

2.3.7 【判断两个字符串是否相等,忽略大小写】

boolean equalsIgnoreCase(String anotherString)

System.out.println("ABC".equalsIgnoreCase(".Abc"));//ture

2.3.8 【转换成字节数组】

byte [] getBytes()

byte[] bytes = "abcdef".getBytes();
for(int i=0;i<bytes.length;i++){
	System.out.println(bytes[i]);//
}

2.3.9 【子字符串在当前字符串第一次出现处的索引(下标)】

int indexOf(String str)

System.out.println("oraclejavac++htmlc#phpython".indexOf("java"));//6

2.3.10 【子字符串在当前字符串最后一次出现处的索引(下标)】

int lastIndexOf(String str)

System.out.println("oraclejavac++htmlc#phpjavapython".indexOf("java"));//

2.3.11 【判断是否为空】

boolean isEmpty()

底层调用的是length()方法

String s = "";
System.out.println(s.isEmpty);//ture

2.3.12 【判断字符串长度】

int length()

判断数组长度是length属性,判断字符串长度是length()方法

System.out.println("abc".length());

2.3.13 【替换】

String replace(CharSequence target,CharSequence replacement)

String的父接口就是:CharSequence

String newString = "http://www.baidu.com".replace("http://","heetps://");
System.out.println(newString);//"https://www.baidu.com"

2.3.14 【拆分字符串】

String[ ] split(String regex)

String[] ymd = "1980-10-11".split("-");//以"-"分隔符进行拆分
for(int i=0;i<ymd.length;i++){
	System.out.println(ymd[i]);
}//1980	10	11

2.3.15 【截取字符串】

String substring(int beginIndex)

参数是起始下标

System.out.println("http://www.baidu.com".substring(7));//www.baidu.com

2.3.16 【截取字符串(有终止下标)】

String substring(int beginIndex,int endIndex)

System.out.println("http://www.baidu.com".substring(710));//www

左闭右开,起始下标【包含】,终止下标【不包含】

2.3.17 【将字符串转换成char数组】

char[] toCharArray()

char[] chars = "我是中国人".toCharArray();
for(int i = 0;i<chars.length;i++){
	System.out.println(chars[i]);
}//我	是	中	国	人

2.3.18 【转换成小写】

String toLowerCase()

System.out.println("ASDFG".toLowerCase());//asdfg

2.3.19 【转换成大写】

String toUpperCase()

System.out.println("asdfg".toLowerCase());//ASDFG

2.3.20 【去除字符串前后空白】

String trim()

System.out.println("          asd   wd    ".trim());//asd   wd

2.3.21 【将非字符串转换成字符串】

valueOf

1)String中唯一的静态方法,不需要new对象

System.out.println(String.valueOf(100));//100(字符串)

2)参数是一个对象的时候,会调用该对象的toString()方法,但没有重写,输出的是对象的内存地址

System.out.println(String.valOf(new Customer());//是对象的内存地址

System.out.println()这方法在输出的时候都是先转成字符串再输出


07、StringBuffer

7.1 在字符串拼接时的资源浪费

若采用下列代码,则会占有大量的内存区空间,造成内存空间的浪费

String s = "abc";
s += "hello";

就以上两行代码,就导致在方法区字符串内存池当中创建了三个对象:

“abc”,“hello”,“abchello”

7.2 优化程序引入StringBuffer

public class String1 {
    public static void main(String1[] args) {
        //创建一个初始化容量为16个byte[]数组(字符串缓冲区对象)
        StringBuffer stringBuffer = new StringBUffer();
        //拼接字符串,以后拼接字符串统一调用append()方法
        stringBuffer.append("a");
        stringBuffer.append("b");
        stringBuffer.append("d");
        stringBuffer.append(3.14);
        //append方法底层在进行追加的时候,如果byte数组满了,会自动扩容
        stringBuffer.append(100L);
        System.out.println(stringBuffer.toString());
        //System.out.println(stringBuffer);
    }
}

7.3 如何优化StringBuffer的性能

1)在创建StringBuffer的时候尽可能给定一个初始化容量

2)最好减少底层数组的扩容次数。预估计一下,给一个合适的较大的初始化容量,提高程序的执行效率

StringBuffer sb = new StringBUffer(100);

7.4 优化大量字符串的拼接的程序

建议使用JDK自带的:java.lang.StringBuffer和java.lang.StringBuilder

7.5 StringBuffer和StringBuilder的区别

StringBuffer方法都有:sychronized关键字修饰。表示StringBuffer在多线程环境下运行是安全的

StringBuilder方法都没有:sychronized关键字修饰。表示StringBuilder在多线程环境下运行是不安全的

08、List接口

1)List集合存储元素特点:有序可重复

有序:List集合中的元素有下标,从0开始,以1递增;

可重复:存储一个1,还可以再存储1;

2)List接口中特有的常用方法:

添加元素到指定位置 void add(int index,E element)

返回列表中指定位置的元素 Object get(int index)

返回列表中第一个出现的指定元素的索引,如果不包含返回-1 int intdexOf(Object c)

返回列表中最后出现的指定元素的索引,如果不包含返回-1 int lastIndexOf(Object c)

移除列表中指定位置的元素(可选操作) Object move(Object o)

用指定元素替换列表中指定位置的元素(可选操作) Object set(int index,E element)

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

public class asdf{
    public static void main(String[] args) {
        //调用List特有的方法需要创建List引用
        List myList = new ArrayList();
        //添加元素
        myList.add("A");//默认都向集合末尾添加元素
        myList.add("B");
        myList.add("C");
        myList.add("D");
        myList.add("E");
        //在列表的指定位置插入元素(第一个参数是下标)
        myList.add(1,"KING");//这个方法使用的不多,因为效率太低
        //迭代
        Iterator it = myList.iterator();
        while(it.hasNext()){
            Object ect = it.next();
            System.out.println(ect);
        }
        //根据下标获取元素
        Object firstObj = myList.get(0);
        System.out.println(firstObj);
        //因为有下标,所以List集合有自己比较独特的遍历方式【通过下标】
        for(int i = 0;i<mylist.size();i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
    }
}

09、ArrayList集合

9.1 Arrays工具类的使用

判断两个数组是否相等。 boolean equals(int[] a,int[] b)

输出数组信息。 String toString(int[] a)

将指定值填充到数组之中。 void fill(int[] a,int val)

对数组进行排序。 void sort(int[l a)

对排序后的数组进行二分法检索指定的值。 int binarySearch(int[] a,int key)

9.2 初始化容量

1)默认初始化容量是10(JDK13新特性:底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为10)

注意:size方法测的是集合中元素的个数,不是集合的容量

2)集合底层是一个Object [ ] 数组

3)构造方法:

new ArrayList();
new ArrayList(20);

//指定初始化容量100
List myList2 = new ArrayList(100);
//创建一个HashSet集合
Collection c = new HashSet();
//添加元素到Set集合
c.add(100);
c.add(200);
c.add(900);
//通过这个构造方法就可以将HashSet集合转换成List集合
List myList3 = new ArrayList(c);
for(int i =0;i<myList3.size();i++){
    System.out.println(myList3.get(i));
}

9.3 扩容

1)默认初始化容量是10,容量满了之后如果再添加元素,自动扩容,扩容的大小是原来的1.5倍

2)ArrayList的底层是数组,尽可能少的扩容,因为数组扩容效率比较低。建议在使用ArrayList的是初始化容量给顶一个预估计的初始化容量,减少扩容

9.4 关于数组

优点:检索效率比较高;

缺点:随机增删元素的效率比较低;

向数组元素末尾添加元素,效率很高,不受影响;

10、位运算

// 5
// >> 1 二进制右移1位
// >> 2	二进制右移2位
// 10的二进制:00001010	【10】
// 10的二进制右移1位是:00000101	【5】

左移是乘以,右移是除以 2的n次方倍,n为移动的位数

11、LinkedList集合

11.1 链表的优缺点

链表的优点:空间存储内存地址不连续,随机增删元素效率较高(因为增删元素不涉及到大量的元素位移)

链表的缺点:查询效率较低,每一次查找某个元素的时候都需要从头节点开始往下遍历

ArrayList之所以检索效率高,不是单纯因为下标的原因,是因为底层数组发挥的作用

LinkedList集合照样有下标,但是检索某个元素的时候效率比较低,因为只能从头节点开始一个一个遍历

11.2 Java实现单链表

public class asdf{
    public static void main(String[] args) {
        Link link = new Link();
        link.add(100);
        link.add(200);
        link.add(300);
        System.out.println(link.size);
    }
}
class Node{
    //存储的数据
    Object data;
    //下一个结点的内存地址
    Node next;
    public Node(){

    }
    public Node(Object data,Node next){
        this.data = data;
        this.next = next;
    }
}

class Link {
    //头指针
    Node header = null;

    int size = 0;

    public int size(){
        return size;
    }

    //向链表中添加元素的方法
    public void add(Object data) {
        //创建一个新的节点对象
        //让之前单链表的末尾节点next指向新节点对象
        if(header == null) {
            //说明还没有节点,new一个新的节点对象,作为头节点对象
            header = new Node(data,null);
        }else{
            //说明头结点已经有了
            //找出当前末尾节点,让当前末尾节点的next是新节点
            Node currentLastNode = findLast(header);
            currentLastNode.next = new Node(data,null);
        }
        size++;

    }

    //专门查找末尾结点的方法
    private Node findLast(Node node) {
        if(node.next == null) {
            //如果节点的next是空则为末尾节点
            return node;
        }
        return findLast(node.next);//递归
    }

    //删除链表中的某个数据的方法
    public void remove(Object obj){

    }

    //修改链表中的某个数据元素的方法
    public void modify(Object newobj){

    }

    //查找链表中某个元素的方法
    public int find(Object obj){
        return 1;
    }
}

11.3 关于LinkedList

1)LinkedList集合没有初始化容量

2)最初这个链表中没有任何元素。first和last引用都是null

3)不管是LinkedList还是ArrayList,以后写代码不需要关系是哪个集合,我们需要面向接口编程,调用的方法都是接口中的方法

List list2 = new ArrayList();//这样写表示底层用的是数组
List list2 = new LinkedList();//这样写表示底层用的是双向链表
List2.add("123");
List2.add("456");
List2.add("789");
//这些方法都是面向的是接口编程


12、Vector集合

12.1 关于Vector集合

1)底层是一个数组

2)初始化容量是10

3)超过10后自动扩容,每次扩容是原容量的2倍

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

public class java{
    public static void main(String[] args) {
        List l = new Vector();
        l.add("123");
        l.add("456");
        l.add("789");
        l.add("122");
        Iterator as = l.iterator();
        while(as.hasNext()){
            Object object = as.next();
            System.out.println(object);
        }
    }
}

4)Vector中的所有方法都是线程同步的,都带有synchronized关键字,是线程安全的。效率较低,使用较少

12.2 将不安全的ArrayList集合转换成线程安全

使用集合工具类 java.utill.Collections;

注意:java.utill.Collection 是集合接口

java.utill.Collections 是集合工具类

List myList = new ArrayList();//非线程安全的
//变成线程安全的
Collection.synchronizedList(myList);
//现在mylist就是线程安全的了

13、泛型

13.1 关于泛型

jdk1.5后的新特性

泛型这种语法机制只能在程序编译阶段起作用,只是给编译器参考的【运行阶段泛型意义不大】

优点:

1)集合中存储的元素类型统一了

2)从集合中取出的元素类型是泛型指定的类型,不需要进行大量的"向下转型"

缺点:

1)导致集合中元素缺乏多样性

2)调用父类中特有的方法不需要向下转型,但调用子类中特有的方法还是需要向下转型

13.2 泛型的具体使用

public class java{
    public static void main(String[] args) {
        /*List l = new Vector();
        Cat cat = new Cat();
        Dog dog = new Dog();
        l.add(cat);
        l.add(dog);
        Iterator it = l.iterator();
        while(it.hasNext()){
            Object object = it.next();
            //                                 使用泛型前需要向下转型
            if(object instanceof Animal){
                Animal a =(Animal)object;
                a.move();
            }
        }*/
        
        //使用泛型后
        List <Animal> l = new Vector<Animal>();
        //指定list集合只能存储Animal,那么存储String就编译报错了
        //这样使用泛型之后,集合中元素的数据类型更加统一
        Cat cat = new Cat();
        Dog dog = new Dog();
        l.add(cat);
        l.add(dog);
        //获取迭代器
        //表示迭代器迭代的是Animal类型
        Iterator <Animal>it = l.iterator();
        while(it.hasNext()){
            //使用迭代之后返回的数据都是Animal类型的,不需要强制类型转换
            Animal a= it.next();
            a.move();
        }
        
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
class Cat extends Animal{
    public void move(){
        System.out.println("猫在吃鱼");
    }
}
class Dog extends Animal{
    public void move(){
        System.out.println("狗在吃肉");
    }
}

13.3 ArrayList<>()

ArrayList<这里的类型会自动推断>()

前提是jdk8之后才允许

List myList = new ArrayList <> ();

13.4 自定义泛型

<>尖括号里面的是一个标识符,随便写

一般是,

E是Element单词首字母

T是Type单词首字母

public class asdf<E>{
    public void Do(E o){
        System.out.println(o);
    }
    public static void main(String[] args) {
        asdf<String> gt = new asdf<>();
        gt.Do("ssd");
    }
}

14、foreach【增强for循环】

jdk5.0之后的新特性

14.1 语法格式

for(元素类型 变量名:数组或集合){
	System.out.println(变量名);
}

14.2 增强for(foreach)

int[] arr = {43,34,3241,5463,23,1};
for(int data:arr){
	System.out.println(data);
}

data代表数组中的每一个元素,可以改变

缺点:没有下标

14.3 集合使用foreach

//创建List集合
List<String>strList = new ArrayList<>();

//添加元素
strList.add("hello");
strList.add("world");
strList.add("kitty");

//遍历使用选代器方式
Iterator<String> it = strlist.iterator();
while(it.hasNext()){
	String s = it.next();
	System.out.println(s);
}

//使用下标方式(只针对于有下标的集合)
for(int i = B; i < strList.size(); i++){
	System.out.println(strList.get(i));
}

//使用foreach
for(String s:strlist){	// 因为泛型使用的是String关星,所以是:String s
	System.out.println(s);
}


15、Map接口

15.1 关于Map接口

1)MapCollection没有继承关系。

2)Map集合以key和value的方式存储数据:键值对

key和value都是引用数据类型。

key和value都是存储对象的内存地址。

key起到主导的地位,value是key的一个附属品

15.2 Map接口中常用的方法

V put(K key,V value) 向Map集合中添加键值对

V get(Object key) 通过key获取value

void clear() 清空Map集合

boolean containsKey(Object key) 判断Map中是否包含某个key

boolean containsValue(Object value) 判断Map中是否包含某个value

boolean isEmpty() 判断Map集合中元素个数是否为e

Set keySet() 获取Map集合所有的key(所有的键是一个set集合)

V remove(Object key) 通过key删除键值对

int size() 获取Map集合中键值对的个数。

Collection values() 获取Map集合中所有的value,返回一个Collection

Set<Map.Entry <K,V> >entrySet() 将Map集合转换成set集合

注意:关于将Map集合转换成set集合Set<Map.Entry<K,V>>entrySet()

假设现在有一个Map集合,如下所示:.

map1集合对象
key				value
1				   zhangsan
2					lisi
3					wangwu
4					zhaoliu

Set set = map1.entrySet();

set集合对象
1=zhangsan【转换成的这个Set集合,Set集合的类型是Map.Entry(一种类型的名字)】
2=lisi
3=wangwu
4=zhaoliu

Entry是Map中的静态内部类

// 创建Map集合对象
Map<Integer, String> map = new HashMap<>();
// 向Nap集合中添加键值对
map.put(1,"zhangsan");// 1在这里进行了自动装箱。
map.put(2,"lisi");
map.put(3,"wangwu");
map.put(4, "zhaoliu");
// 通过key获取value
String value = map.get(2);
System.out.println(value);
//获取键值对的数量
System.out.println("键值对的数量:"+map.size());|
// 通过key删除key-value
map.remove(key: 2);
System.out.println("键值对的数量:"+map.size());
// 判断是否包含某个key
//contoins方法屈层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
System.out.println(imap.containsKey(new Integer( value: 4))); // true
//判断是否包含某个value
System.out.println(map.containsValue(new String( original: "wangwu"))); // trud

//获取所有的value
Collection<String> values = map.value();
for(String s:values){
    System.out.println(s);
}
//清空Map集合
map.clear();
System.out.println("键值对的数量:"+map.size());

15.3 遍历Map集合

第一种方式:获取所有的key,通过遍历key,来遍历value

Map<Integer, String> map = new MashMap<>();
map.put(1,"zhangsan");
map.put(2,"1isi");
map.put(3,"wangwu");
map.put(4,"zhaoliu");
//迎历Nap集合
//获取所有的key,所有的key是一个5et集合
Set<Integer> keys = map.keySet();
//遍历key,遗过key获取val ue
//选代器可以
Iterator<Integer> it = keys.iterator();


//迭代器遍历
while(it.hasNext())(
	// 取出其中一个key
	Integer key = it.next();
	// 通过key欢取value
	String value = map.get(key); 
	System.out.println(key + "=" + value);
}
    
    
//foreach循环遍历
for(Integer key:keys){
    System.out.println(key + "=" +map.get(key));
}

第二种方式:Set<Map.Entry<K,V>>entrySet() 【效率高】

//把Map集合直接全部转换成Set集合。
// Set集合中元素的类型是:Map.Entry
Set<Map.Entry<Integer,String>> set = map.entrySet();
//遍历set集合,每一次取出一个Node


//迭代器遍历
Iterator<Map.Entry<Integer,String>> it2 = set.iterator(>;
while(it2.hasNext()){
	Map.Entry<Integer,String> node = it2.next();
	Integer key = node.getkey();
	String value = node.getValue();
	System.out.println(key + "=" + value);
}
                                                       
// foreach
for(Map.Entry<Integer,String> node : set){
	System.out.println(node.getkey() + "---" + node.getValue());
}

16、HashMap集合

16.1 关于HashMap集合

1)HashMap集合底层是哈希表/散列表的数据结构。

2)哈希表是一个怎样的数据结构呢?

哈希表是一个数组和单向链表的结合体。
数组:在查询方面效率很高,随机增删方面效率很低。
单向链表:在随机增删方面效率较高,在查询方面效率很低。
哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

4)HashMap集合底层的源代码:

public class HashMap{
	//HashMap底层实际上就是一个数组。(一维数组)
	Node<K,V>[] table;
	//静态的内部类HashMap.Node
	static class Node<K,V> {
		final int hash;// 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法可以转换成数组的下标
		final K key;// 存储到Map集合中的那个key
		v value;//存储到Map集合中的那个value
		Node<K,V> next;//下一个节点的内存地址。
	}
}

哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)

5)在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构。

当红黑树上的节点数量小于6时,会重新把红思树变成单向链表数据结构。

【提高效率,二叉树的检索会再次缩小扫描范围】

6)对于哈希表数据结构来说:

如果o1和o2的hash值相同,一定是放到同一个单向链表上。

当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

7)HashMap允许key部分为空,但null值只能为一个


16.2 HashMap集合的key部分特点

无序,不可重复。

为什么无序?因为不一定挂到哪个单向链表上。

为什么不可重复?通过equals方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放到HashSet集合中了。

所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。


16.3 哈希表HashMap使用不当无法发挥性能

假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。

这种情况我们成为:散列分布不均匀。

1)什么是散列分布均匀?

假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。

假设将所有的hashCode()方法返回值都设定为不一样的值,这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。

所以散列分布均匀需要重写hashCode()方法时有一定的技巧。

2)为什么哈希表的随机增删,以及查询效率都很高?
增删是在链表上完成。查询也不需要都扫描,只需要部分扫描。

重点:HashMap集合的key,会先后调用两个方法,一个方法是hashCode(),一个方法是equals(),那么这两个方法都需要重写。


16.4 map.put(k,v)实现原理

第一步:先将k,v封装到Node对象当中。

第二步:底层会调用k的hashCode()方法得出hash值,

通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。

如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals,

如果所有的equals方法返回都是false,那么这个新节点将果被添加到链表的末尾。

如果其中有一个equals返回了会true,那么这个节点的value将会被覆盖。


16.5 v = map.get(k)实现原理

先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,

如果这个位置上什么也没有,返回null;

如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,

如果所有equals方法返false,那么get方法返回null,只要其中有一个节点的k和参数k equals的时候返回true,

那么此时这个节点的value就是我们要找的value,get方法最终返回这个要找的value。

//测试HashMap集合key部分的元素特点
//Integer是key。他的euquals和hashCode都重写了
import java.util.*;

public class java{
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1111,"zahngsan");
        map.put(2222,"wusan");
        map.put(3333,"lisi");
        map.put(1111,"sadasd");//key重复的时候value会自动覆盖
        System.out.println(map.size());
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历Map集合
        for(Map.Entry<Integer,String> entry : set){
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}

16.6 HashMap集合的初始化容量和扩容

HashMap集合的默认初始化容量是16,默认加载因子是0.75

这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容为原来的2倍。

重点:

HashMap 集合初始化容量必须是2的倍数,这也是官方推荐的,

这是因为达到散列均匀,为了提高HashMap集合的存取效率所必须的。

16.7 euquals和hashCode的重写

public class HashMapTest02 {
	public static void main(String[] args){
		Student s1 = new Student( name: "zhangsan");
		Student s2 = new Student( name: "zhangsan");
        
		//重写equals方法之前是false
		//System.out.println(s1.equals(s2)); // false
        
		//重写equals方法之后是true
		System.out.println(s1.equals(s2));//true(s1和s2表示相等)
        
		System.out.println("s1的hashCode=" + s1.hashCode());//284720968
		System.out.println("s2的hashCode="+s2.hashCode());//122883338
		//s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放的话,
		//按说只能放进去1个。(HashSet集合特点:无序不可重复)
		Set<Student> students = new HashSet<>();
		students.add(s1);
		students.add(s2);
		System.out.println(students.size());//这个结果按说应该是1.但是结果是2.显然不符合HashSet集合存储特点,所以要重写equals和HashCode方法
    }
}

1)注意

hashCode如果不重写,相同的对象内容信息通过哈希算法得到的哈希值不相同,会存到不同的数组下标下,不符合预期

所以需要重写hashCode,得到相同的哈希值,然后存到同一个数组下标下,进行equals比较,才会符合预期

如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。

equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。

所以hashCode()方法的返回值也应该相同。

2)如何重写?

直接使用IDEA工具生成,但是这两个方法需要同时生成。

3)终极结论:

放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

17、Hashtable集合

Hashtable不允许key和value部分为空,会出现空指针异常

Hashtable方法都带有synchronized:线程安全的。线程安全有其它的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。

HashMap和Hashtable底层都是哈希表

Hashtable的初始化容量为11,默认加载因子是0.75扩容为2倍再加一

18、Properties类

目前只需要掌properties属性类对象的相关方法即可。

Properties是一Map集合,继承Hashtable,Properties 的key和value 都是string类型。

Properties被称为属性类对象。

Properties是模程安全的。

public class PropertiesTeste1 {
	public static void main(String[] args){
		// 创建一小properties对象
		Properties pro = new Properties();
		//properties的存
		pro.setProperty("ur1", "jdbc:mysq1://localhost:3306/bjpovernode");
		pro.setProperty("driver","com.mysq1.jdbc.Driver");
		pro.setProperty("username","root");
		pro.setProperty("password", "123");
        
		// 通过key获取value
		String url = pro.getProperty("ur1");
		String driver = pro.getProperty("driver");
		String username = pro.getProperty("username");
		String password = pro.getProperty("password");
        
		System.out.println(url);
		System.out.println(driver);
		System.out.println(username);
		System.out.printIn(password);l
	}
}

19、TreeSet集合

19.1 关于TreeSet集合

1)TreeSet集合底层实际上是一个TreeMap

2)TreeMap集合底层是一个二叉树。

3)放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。

4)TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合。

19.2 排序方式:实现Comparable接口

无法对自定义类型进行排序,因为没有指定对象之间的比较原则。谁大谁小并没有说明啊。

程序运行的时候出现了这个异常:

java.long.ClossCastException:
	class com.bjpowernode.javase.collection.Person
	cannot be cast to class java.Lang.Comparable

出现这个异常的原因是:自定义类没有实现java.Lang.Comparable接口。

import java.util.TreeSet;
public class TreeSetTest04 {
	public static void main(String[] args){
		Customer c1 = new Customer( age: 32);
		Customer c2 = new Customer( age: 20);
		Customer c3 = new Customer( age: 30);
		Customer c4 = new Customer( age: 25);
        
		// 创建TreeSet集合
		TreeSet<Customer> customers = new TreeSet<>();
		//添加元素
		customers.add(c1);
		customers.add(c2);
		customers.add(c3);
		customers.add(c4);
    	//遍历
		for (Customer c : customers){
			System.out.println(c);
        }
    }
}
//放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
//并且实现compareTo方法equals可以不写。
class Customer implements Comparable<Customer>{
	int age;
	public Customer(int age){
		this.age = age;
    }
	//需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
	// k.compareTo(t.key)
	//拿着参数k和集合中的每一个k进行比较,返回值可能是>e<e=e
	//比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
@Override
	public int compareTo(Customer c) { // cl.compareTo(c2);
	// this是c1
	//c是c2
	//c1和c2比较的时候,就是this和c比较。
		/*int agel = this.age;
		int age2 = c.age;
		if(agel == age2){
		return 0;
		} else if(agel > age2){
			return 1;
		} else.{
			return -1;
		}*/
        //return this.age - c.age; // =0 >0 <0
		return c.age - this.age;
        
		//compareTo方法的返回值很重要:
		//返回e表示相同,value会覆盖。
		//返回>0,会继续在右子树上找。【10-9=1,1>0的说明左边这个数字比较大。所以在右子树上找。
		//返回<0,会继续在左子树上找。
        
	}
	public String toString(){
		return "Customer[age="+age+"]";
	}
}

19.3 二叉树数据结构

1)TreeSet/TreeMap是自平衡二叉树。遵循左小右大原则存放。

2)遍历二叉树的时候有三种方式:

存放是要依靠左小右大原则,所以这个存放的时候要进行比较。

前序遍历:根左右

中序遍历:左根右

后序遍历:左右根

注意
前中后说的是“根”的位置。根在前面是前序,根在中间是中序,根在后面是后序。

3)TreeSet集合/TreeMap集合采用的是:中序遍历方式。

​ Iterator迭代器采用的是中序遍历方式。左根右。

4) 100 200 50 60 80 120 140 130 135 180 666 .40 55

在这里插入图片描述​编辑

5)采用中序遍历取出:

40 50 55 60 80 100 120 130 135 140 180 200 666

存放的过程就是排序的过程。取出来就是自动按照大小顺序排列的。

19.4 排序方式:实现Comparator比较器接口

public class TreeSetTest06 {
	public static void main(String[] args) {
		//创建TreeSet集合的时候,需要使用这个比较器。
		//TreeSet<wuGui>wuGuis=new TreeSet<>();//这样不行,没有通过构造方法传递一个比较器进去。
        
		//给构造方法传递一个比较器。
		TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());
       
        
		wuGuis.add(new WuGui( age: 1000));
		wuGuis.add(new WuGui( age: 800));
		wuGuis.add(new WuGui( age: 810));
        
		for(WuGui wuGui : wuGuis){
			System.out.println(wuGui);
        }
    }
}

//乌龟
class WuGui {
	int age;
	public WuGui(int age){
		this.age = age;
    }
	@Override
	public String toString() {
		return"小乌龟["+
				"age=1" + age +
				']';
    }
}

//单独在这里编写一个比较器
//比较器实现java.util.Comparator接口。(Comparable是java.lang包下的。Comparator是java.util包下的。)
class WuGuiComparator implements Comparator<WuGui> {
	@Override
	public int compare(WuGui o1, WuGui o2) {
		//指定比较规则
		//按照年龄排序
		return o1.age - o2.age;
	}
}

19.5 Comparable接口和Comparator比较器接口的选择

放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:

第一种:放在集合中的元素实现java.lang.Comparable接口。

第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。

Comparable和Comparator怎么选择呢?

当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。

如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。

Comparator接口的设计符合OCP原则。

20、Collections工具类

java.util.Collection 集合接口

java.util.Collections集合工具类,方便集合的操作。

public class CollectionsTest {
	public static void main(String[] args){
        
		// ArrayList集合不是线程安全的。
		List<String> list = new ArrayList<>();
        
		//变成线程安全的
		Collections.synchronizedlist(list);
        
		//排序
		list.add("abf");
		list.add("abx");
		list.add("abc");
		list.add("abe");
		Collections.sort(list);
		for(String s : list){
			System.out.println(s);
        }
        
		List<WuGui> wuGuis = new ArrayList<>();
		wuGuis.add(new WuGui( age: 1000));
		wuGuis.add(new WuGui( age: 8000));
		Collections.sort(wuGuis);//对List集合中元素排序,需要保证List集合中的元素实现了Comparable接口
        for(WuGui wg : whGuis){
            System.out.println(wg);
        }
}

class WuGui implements Comparable<WuGui>{
	int age;
	public WuGui(int age){ this.age = age; }
    
	@Override
	public int compareTo(WuGui o){
		return this.age - o.age;
    }
    
    @Override
	public String toString() {
		return "WuGui2{" +
				"age=" + age +
				'}';
    }
}
    
	//对Set集合怎么排序呢?
	Set<String> set = new HashSet<>();
	set.add("king");
	set.add("kingsoft");
	set.add("king2");
	set.add("king1");
	//将set集合转换成List集合
	List<String> myList = new ArrayList<>(set);
	Collections.sort(myList);
	for(String s :myList){
		System.out.println(s);
		//Collections.sort(l正st集合,比较器对象);
	}
}


21、关于异常、接口、集合的练习【战争】

题目要求:

开放性题目,随意发挥;

建立一个武器集合,包括添加武器,遍历武器使得各武器能充分发挥功能,其中功能包括可移动、可攻击。最好不要面向具体编程,降低程序的耦合度,提高扩展力,再添加过程中若出现武器满的情况在控制台作出反馈,但不停止程序

测试类

package zhandou;

import java.util.ArrayList;
import java.util.List;

public class text{
 public static void main(String[] args) {
     List<Weapon> weaponList = new ArrayList<>();
     Weapon GaoShePao = new GaoShePao("C");
     Weapon TanKe = new TanKe("A");
     Weapon YunShuJi = new YunShuJi("B");
     Weapon ZhanDouJi = new ZhanDouJi("D");

     weaponList.add(GaoShePao);
     weaponList.add(TanKe);
     weaponList.add(YunShuJi);
     weaponList.add(ZhanDouJi);

     try {
         if (weaponList.size() > 4)
             throw new WeaponException("越界");
     } catch (WeaponException e) {
         System.out.println(e.getMessage());
     }
     for(Weapon data:weaponList){
         if (data instanceof Moveable) {
             Moveable m = (Moveable) data;
             m.move();
             System.out.println("$$$$$$$$$$$");
         }if (data instanceof Shootable) {
             Shootable b = (Shootable) data;
             b.shoot();
             System.out.println("############");
         }
     }

 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-or5jhvpP-1657728924927)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

武器越界异常

package zhandou;

public class WeaponException extends Exception{
 public WeaponException() {

 }

 public WeaponException(String message) {
     super(message);
 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZVjnXld-1657728924927)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

移动接口

package zhandou;

public interface Moveable {
 void move();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzTSax6k-1657728924928)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

射击接口

package zhandou;

public interface Shootable {
 void shoot();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5Yt80s1-1657728924928)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

父类:武器

package zhandou;

public class Weapon {

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fu89hCgY-1657728924928)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

子类: 战斗机

package zhandou;

public class ZhanDouJi extends Weapon implements Shootable,Moveable{
 private String name;

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public ZhanDouJi(String name) {
     this.name = name;
 }

 public ZhanDouJi() {
 }

 public void shoot() {
     System.out.println("战斗机射击!");
 }
 public void move(){
     System.out.println("战斗机起飞!");
 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R959DyDu-1657728924929)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

子类:高射炮

package zhandou;

public class GaoShePao extends Weapon implements Shootable{
 private String name;

 public GaoShePao(String name) {
     this.name = name;
 }

 public GaoShePao() {
 }

 public void setName(String name) {
     this.name = name;
 }

 public String getName() {
     return name;
 }

 public void shoot() {
     System.out.println("高射炮射击!");
 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tnwcI01r-1657728924929)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

子类:坦克

package zhandou;

public class TanKe extends Weapon implements Moveable,Shootable{
 private String name;

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public TanKe() {
 }

 public TanKe(String name) {
     this.name = name;
 }

 public void move() {
     System.out.println("坦克启动!");
 }
 public void shoot(){
     System.out.println("坦克射击!");
 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KOlj1dhu-1657728924929)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

子类:运输机

package zhandou;

public class YunShuJi extends Weapon implements Moveable{
 private String name;

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public YunShuJi() {
 }

 public YunShuJi(String name) {
     this.name = name;
 }

 public void move() {
     System.out.println("运输机起飞!");
 }
}

题后总结 :

1)抛出异常方面代码思路不够明确,其中若自定义集合中的异常,不知道如何写,只能出现自抛自抓的情况

2)父类型能转型称为接口类型,没有想到真的能实现,是在测试中发现的;

3)以后写程序要把接口、类、放在不同的Java class/interface,思路更加清晰