JavaSE——05集合及常用类库

目录

05 集合及常用类库

01、集合的定义

02、集合的继承结构

2.1 超级父接口Collection

2.2 超级父接口Map

2.3 总结

03、Collection接口

3.1 Collection存放的元素类型

3.2 Collection的常用方法

04、集合遍历/迭代

4.1 如何遍历集合

4.2 迭代器是通用的

05、深入Collection的contains方法

06、String类

6.1 String字符串的存储原理

2.2 String类常用的构造方法

2.3 String常用的方法

07、StringBuffer

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

7.2 优化程序引入StringBuffer

7.3 如何优化StringBuffer的性能

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

7.5 StringBuffer和StringBuilder的区别

08、List接口

09、ArrayList集合

9.1 Arrays工具类的使用

9.2 初始化容量

9.3 扩容

9.4 关于数组

10、位运算

11、LinkedList集合

11.1 链表的优缺点

11.2 Java实现单链表

11.3 关于LinkedList

12、Vector集合

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

13、泛型

13.1 关于泛型

13.2 泛型的具体使用

13.3 ArrayList<>()

13.4 自定义泛型

14、foreach【增强for循环】

14.1 语法格式

14.2 增强for(foreach)

14.3 集合使用foreach

15、Map接口

15.1 关于Map接口

15.2 Map接口中常用的方法

15.3 遍历Map集合

16、HashMap集合

16.1 关于HashMap集合

16.2 HashMap集合的key部分特点

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

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

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

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

16.7 euquals和hashCode的重写

17、Hashtable集合

18、Properties类

19、TreeSet集合

19.1 关于TreeSet集合

19.2 排序方式:实现Comparable接口

19.3 二叉树数据结构

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

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

20、Collections工具类

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


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,2,3);

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(7,10));//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,思路更加清晰

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖虎不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值