javaSE(七)
1.对象数组
类的有参构造构造出对象,将对象存储在数组中。
案例代码:
Student[] arr = new Student[5];
arr[0] = new Student("张一", 11);
arr[1] = new Student("张二", 12);
arr[2] = new Student("张三", 13);
arr[3] = new Student("张四", 14);
arr[4] = new Student("张五", 15);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
对应的内存图:
2.集合体系
数组和集合的区别
* 区别1 :
* 数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
* 集合只能存储引用数据类型(对象)集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象
* 区别2:
* 数组长度是固定的,不能自动增长
* 集合的长度的是可变的,可以根据元素的增加而增长
因为JDK1.0Vector就出现了,JDK1.2才出现Collection,所以命名不符合新的命名规则。
3.Collection集合的基本功能
案例代码:
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
public class Demo2_Collection {
/**
* boolean add(E e)
* boolean remove(Object o)
* void clear()
* boolean contains(Object o)
* boolean isEmpty()
* int size()
* 注意:要了解详细信息,请使用 -Xlint:unchecked重新编译.
* add方法如果是List集合一直都返回true,因为List集合中是可以存储重复元素的
* 如果是Set集合当存储重复元素的时候,就会返回false
* ArrayList的父类的父类重写toString方法,所以在打印对象的引用的时候,输出的结果不是Object类中toString的结果
*/
public static void main(String[] args) {
// 接口不能具体实现类,只能父类引用指向子类对象
Collection c = new ArrayList();
boolean add = c.add("a");
boolean add2 = c.add(true);
boolean add3 = c.add(100);
boolean add4 = c.add("abc");
boolean add5 = c.add(new Student("张三", 23));
System.out.println(c);
c.remove("trueaaaa");
System.out.println(c);
System.out.println(c.contains(true));
System.out.println(c.isEmpty());
System.out.println(c.size());
c.clear();
System.out.println(c.size());
}
需要说明的问题有:
remove不存在的内容,并不会报错
创建实例必须使用父类引用指向子类对象
在这里没有使用泛型会出现黄色波浪线,可以cmd+1将这个标注在方法上,将其上移至类上,适用于类中所有方法。
4.集合遍历
案例代码:
public static void demo2() {
Collection c = new ArrayList();
c.add(new Student("张三", 18));
c.add(new Student("李四", 19));
c.add(new Student("王五", 21));
c.add(new Student("刘流", 34));
Object[] array = c.toArray();
for (int i = 0; i < c.size(); i++) {
// System.out.println(array[i]);
Student s = (Student) array[i];
System.out.println(s.getName() + "..." + s.getAge());
}
}
public static void demo1() {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
Object[] array = c.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
集合遍历需要先调用toArray方法,将集合转成数组,再来实现数组的遍历。
遍历的时候,数组中的数据类型是Object,多态的弊端:无法直接调用子类特有的方法,所以,如果集合中是对象,但是想调用对象的方法,需要向下强制转型成具体的类。
5.Collection类带All的功能
案例代码:
public static void main(String[] args) {
//demo1();
//demo2();
//demo3();
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");
Collection c2 = new ArrayList();
c2.add("a");
c2.add("b");
c2.add("c");
c2.add("d");
c2.add("e");
c2.add("f");
//取交集,如果调用的集合改变就返回true,如果调用的集合不变就返回false
boolean b = c1.retainAll(c2); //取交集
System.out.println(b);
System.out.println(c1);
}
public static void demo3() {
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");
Collection c2 = new ArrayList();
c2.add("a");
c2.add("b");
c2.add("z");
boolean b = c1.containsAll(c2); //判断调用的集合是否包含传入的集合
System.out.println(b);
}
public static void demo2() {
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");
Collection c2 = new ArrayList();
c2.add("a");
c2.add("b");
c2.add("z");
boolean b = c1.removeAll(c2); //删除的是交集
System.out.println(b);
System.out.println(c1);
}
public static void demo1() {
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");
Collection c2 = new ArrayList(); //alt + shift + r改名
c2.add("a");
c2.add("b");
c2.add("c");
c2.add("d");
//c1.addAll(c2); //将c2中的每一个元素添加到c1中
c1.add(c2); //将c2看成一个对象添加到c1中
System.out.println(c1);
}
说明:
addAll是添加集合的元素,add是添加集合本身
removeAll是删除交集
containsAll判断调用的集合是否包含参数集合
retainAll是留下交集,此处需要注意,如果参数集合大于等于调用集合,那么调用集合不变,返回false,如果参数集合小于调用集合,调用集合发生变化,返回true。
6.迭代器
案例代码:
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("张三", 18));
c.add(new Student("李四", 19));
c.add(new Student("王五", 21));
c.add(new Student("刘流", 34));
Iterator iterator = c.iterator();
while (iterator.hasNext()) {
// System.out.println(iterator.next());
Student s = (Student) iterator.next();
System.out.println(s.getName() + "..." + s.getAge());
}
}
底层实现:
Itr类,cursor是指针,调用一次next方法,cursor会加一。hasnext判定标准是cursor != size,当cursor等于size的时候,已经遍历完毕,自然没有next,所以是false。
7.List集合特有的功能
案例代码:
public static void demo4() {
ArrayList list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
// 返回的值是替换出来的对应索引的值
Object set = list.set(0, 1);
System.out.println(set);
System.out.println(list);
}
public static void demo3() {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// Iterator iterator = list.iterator();
// while (iterator.hasNext()) {
// System.out.println(iterator.next());
// }
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
public static void demo2() {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add("厉害啊");
// 默认是索引
Object remove = list.remove(1);
// remove的是object,返回的是boolean,remove的是index,返回的是object
System.out.println(remove);
System.out.println(list);
list.remove("厉害啊");
System.out.println(list);
}
public static void demo1() {
ArrayList list = new ArrayList();
list.add("a");
list.add("b");
System.out.println(list);
list.add(2, "c");
System.out.println(list);
}
void add(int index,E element)
E remove(int index)
E get(int index)
E set(int index,E element)
add方法,需要注意索引值只能小于等于list集合的长度,不然会报索引越界。
remove方法,如果参数列表中是索引,那么返回值是对应索引位置的值,如果参数列表中是object,返回的是布尔类型。
get方法,获取的是对应索引的object,可用于遍历,arrayList也可以使用迭代器进行遍历。
set方法,返回值是被以前索引对应的值。
8.并发修改异常
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo3_List {
/**
** A:案例演示
* 需求:我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
*/
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("c");
list.add("e");
// demo1(list);
ListIterator iterator = list.listIterator();
while (iterator.hasNext()) {
if ("world".equals((String) iterator.next())) {
iterator.add("javaee");
}
}
System.out.println(list);
}
public static void demo1(List list) {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String s = (String) iterator.next();
if ("world".equals(s)) {
list.add("javaee");
}
}
}
}
调用集合的add方法在遍历的同时进行修改操作,会报出如下错误:
Exception in thread "main" java.util.ConcurrentModificationException
说明:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
此时list的add方法不能进行操作,所以需要使用List类特有的迭代器ListIterator中的add方法进行操作。
案例代码:
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo4_ListIterator {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("c");
list.add("e");
ListIterator iterator = list.listIterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
System.out.println("");
while (iterator.hasPrevious()) {
System.out.print(iterator.previous());
}
}
}
ListIterator还有从后向前遍历的方法,不过需要在从前往后以后才可以,因为索引的位置需要先到最后面,才能倒着遍历。
9.Vector类
需要了解的方法:
addElement()
elements() 返回值属于Enumeration
Enumeration的方法,需要了解hasMoreElements()和nextElemennt()
案例代码:
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Demo5_Vector {
public static void main(String[] args) {
Vector v = new Vector();
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
// 获取枚举
Enumeration elements = v.elements();
while (elements.hasMoreElements()) {
Object element = elements.nextElement();
System.out.println(element);
}
}
}
10.List子类的特点及选择
数组有序,
Vector和ArrayList的区别
Vector线程安全,效率低(但是ArrayList同样可以达到线程安全的效果,所以Vector被放弃了)
ArrayList线程不安全,效率高。
他们都是数组实现的
ArrayList和LinkedList的区别
ArrayList底层是数组,查询修改快,增加删除慢
LinkedList底层是链表,查询修改慢,增加删除块
他们都是线程不安全的
List有三个子类,一般选择哪个?
查询和修改多用ArrayList
增加和删除多用LinkedList
都多,建议使用ArrayList
Vector已经放弃了
11.去除List中的重复元素
案例代码:
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("a");
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("c");
list.add("c");
list.add("c");
ArrayList newList = getSingle(list);
System.out.println(newList);
}
/*
* 创建新集合将重复元素去掉
* 1,明确返回值类型,返回ArrayList
* 2,明确参数列表ArrayList
*
* 分析:
* 1,创建新集合
* 2,根据传入的集合(老集合)获取迭代器
* 3,遍历老集合
* 4,通过新集合判断是否包含老集合中的元素,如果包含就不添加,如果不包含就添加
*/
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList<>(); //1,创建新集合
Iterator it = list.iterator(); //2,根据传入的集合(老集合)获取迭代器
while(it.hasNext()) { //3,遍历老集合
Object obj = it.next(); //记录住每一个元素
if(!newList.contains(obj)) { //如果新集合中不包含老集合中的元素
newList.add(obj); //将该元素添加
}
}
return newList;
}
用新的List来装老List里面的东西,有就不装,没有就装。
去掉List中重合的自定义对象:
public static void main(String[] args) {
ArrayList list = new ArrayList(); //创建集合对象
list.add(new Person("张三", 23));
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("李四", 24));
list.add(new Person("李四", 24));
list.add(new Person("李四", 24));
//ArrayList newList = getSingle(list); //调用方法去除重复
//System.out.println(newList);
list.remove(new Person("张三", 23));
System.out.println(list);
}
/*
* 创建新集合将重复元素去掉
* 1,明确返回值类型,返回ArrayList
* 2,明确参数列表ArrayList
*
* 分析:
* 1,创建新集合
* 2,根据传入的集合(老集合)获取迭代器
* 3,遍历老集合
* 4,通过新集合判断是否包含老集合中的元素,如果包含就不添加,如果不包含就添加
*/
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList<>(); //1,创建新集合
Iterator it = list.iterator(); //2,根据传入的集合(老集合)获取迭代器
while(it.hasNext()) { //3,遍历老集合
Object obj = it.next(); //记录住每一个元素
if(!newList.contains(obj)) { //如果新集合中不包含老集合中的元素
newList.add(obj); //将该元素添加
}
}
return newList;
}
这里需要知道:
remove和contains底层都是依赖的equals方法,需要在javaBean中重写equals方法才可以,不然原equals根据地址值判断,操作无法达到理想效果。
12.LinkedList的特有方法
public void addFirst(E e)及addLast(E e)
public E getFirst()及getLast()
public E removeFirst()及public E removeLast()
public E get(int index);
13.栈和队列 数组和链表
14.LinkedList模拟栈结构(先进后出)
案例代码:
新建一个类Stack:
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Stack {
private LinkedList list = new LinkedList();
/*
* 模拟进栈方法
*/
public void in(Object object) {
list.addLast(object);
}
/*
* 模拟出栈方法
*/
public Object out() {
Object removeLast = list.removeLast();
return removeLast;
}
/*
* 模拟判断是否为空方法
*/
public boolean isEmpty() {
return list.isEmpty();
}
}
再调用封装好的方法实现功能:
Stack stack = new Stack();
stack.in("a");
stack.in("b");
stack.in("c");
stack.in("d");
while (!stack.isEmpty()) {
System.out.println(stack.out());
}
15.泛型
泛型好处
1.提高了安全性(运行时期的错误转换到编译期)
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
没有用泛型限定能够输入的类型,在遍历过程中如果输出的时候进行强转,会报错无法强转。 2. 省去强转的麻烦
<>中必须是引用数据类型
后面的泛型可以省去不写()
案例代码:
public static void main(String[] args) {
ArrayList<Person> arrayList = new ArrayList<>();
arrayList.add(new Person("张三", 18));
arrayList.add(new Person("张四", 19));
arrayList.add(new Person("张五", 20));
arrayList.add(new Person("张六", 21));
ListIterator<Person> iterator = arrayList.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getAge() + iterator.next().getName());
}
while (iterator.hasPrevious()) {
Person next = iterator.previous();
System.out.println(next.getName() + next.getAge());
}
}
输出结果如下:18张四
20张六
张六21
张五20
张四19
张三18
如果不把对象返回,而是不断调用方法,指针索引会向后继续走。调用的方法其实不是一个同对象的。而且,一段调用结束,需要用previous让指针反向走才可以读取到数据。
16.泛型类,泛型方法和泛型接口
public class Tool<Q> {
private Q q;
public Q getObj() {
return q;
}
public void setObj(Q q) {
this.q = q;
}
public <T> void show(T t) {
System.out.println(t);
}
public static <W> void test(W w) {
System.out.println(w);
}
}
泛型类:public class 类名<泛型类型1,…>。
泛型方法: public <泛型类型> 返回类型 方法名(泛型类型 变量名)。泛型推荐和类的泛型一样,如果不一样,要在权限修饰符后面添加自己的泛型
泛型静态方法:泛型不能和泛型类的一样,因为static在创建的时候就加载了,不确定先后顺序,所以需要自己在static后面加泛型
案例代码:
interface inter<T> {
public void show(T t);
}
class Demo implements inter<String> {
@Override
public void show(String t) {
System.out.println(t);
}
}
// class Demo<T> implements inter<T> {
//
// @Override
// public void show(T t) {
// System.out.println(t);
// }
//
// }
泛型接口:public interface 接口名<泛型类型> 一般推荐实现类不带泛型,因为没必要
17.限定通配符
public static void main(String[] args) {
// 当右边的泛型是不确定的时候,左边可以指定为?
// ArrayList<?> arrayList = new ArrayList<>();
// 父类Person 子类Student
ArrayList<Person> list1 = new ArrayList<Person>();
list1.add(new Person("张三", 12));
list1.add(new Person("李四", 13));
list1.add(new Person("王五", 14));
ArrayList<Student> list2 = new ArrayList<Student>();
list2.add(new Student("赵六", 15));
list2.add(new Student("胡七", 15));
// list2.addAll(list1);
boolean addAll = list1.addAll(list2);
System.out.println(list1);
}
A:泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了(当右边的泛型不确定的时候,左边的泛型中就输入通配符)
B:? extends E
向下限定,E及其子类
C:? super E
向上限定,E及其父类
boolean |
addAll(Collection<? extends E> c) 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
在上面的案例代码中,可以发现。子类作为 泛型的list集合可以全被添加到父类作为泛型的集合中,但是父类作为泛型的不能被添加到子类作为泛型的list集合中,查询addAll方法,可以看到,参数列表是?extend E,也就是说,它支持向下限定。
18.增强for循环
作用:简化数组和Collection的遍历
原理:底层依赖是迭代器
简化为foreach
19.三种迭代能否删除
案例代码:
//1,普通for循环删除,索引要--
/*for(int i = 0; i < list.size(); i++) {
if("b".equals(list.get(i))) {
list.remove(i--); //通过索引删除元素
}
}*/
//2,迭代器删除
/*Iterator<String> it = list.iterator();
while(it.hasNext()) {
if("b".equals(it.next())) {
//list.remove("b"); //不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常
it.remove();
}
}*/
/*for(Iterator<String> it2 = list.iterator(); it2.hasNext();) {
if("b".equals(it2.next())) {
//list.remove("b"); //不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常
it2.remove();
}
}*/
//3,增强for循环,增强for循环不能删除,只能遍历
for (String string : list) {
if("b".equals(string)) {
list.remove("b");
}
}
System.out.println(list);
}
特别说明for循环:
ArrayList<String> list = new ArrayList<>();
list.add("10.1举国欢庆!");
list.add("10.2举国欢庆!");
list.add("10.3举国欢庆!");
for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
list.remove(i--);
}
在这个循环中,如果是list.remove(i)输出结果是10.2举国欢庆!
因为在删除掉第一条以后,索引下移到了1的位置,而以前1位置对应的元素因为前面空缺,所以上移到了以前0的位置,以前2位置的元素恰好到了1位置,即正好被删除。所以需要i--回查。
20.静态导入
import static java.util.Arrays.toString;
import java.util.Arrays
前者是静态导入,后者是导入
静态导入调用直接方法即可
导入类的话需要类调用方法
导入的方法必须是静态的,因为存在同名等问题,所以开发中一般不用。
21.可变参数
可变参数基本上是以数组格式传入的。
基本格式为:修饰符 返回值类型 方法名(数据类型… 变量名){}
public static void print(int ... arr)
案例代码:
public static void main(String[] args) {
int[] arr = {11,22,33,44,55};
//print(arr);
print(11,22,33,44,55);
System.out.println("---------------");
//print();
}
/*public static void print(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}*/
public static void print(int ... arr) { //可变参数其实是一个数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
功能:定义数组的时候不知道定义多少个参数的时候使用。比一般数组参数强大的地方在于他可以直接接受元素形式的数组。
在接受元素形式的数组时,元素对着前面的参数进行分配,剩下的元素全部给可变参数,如果可变参数在最前面,那么,后面的参数都取不到值。
22.数组转集合asList
案例代码:
public static void main(String[] args) {
//demo1();
//demo2();
//集合转数组,加泛型的
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
String[] arr = list.toArray(new String[10]); //当集合转换数组时,数组长度如果是小于等于集合的size时,转换后的数组长度等于集合的size
//如果数组的长度大于了size,分配的数组长度就和你指定的长度一样
for (String string : arr) {
System.out.println(string);
}
}
public static void demo2() {
//int[] arr = {11,22,33,44,55};
//List<int[]> list = Arrays.asList(arr); 基本数据类型的数组转换成集合,会将整个数组当作一个对象转换
//System.out.println(list);
Integer[] arr = {11,22,33,44,55}; //将数组转换成集合,数组必须是引用数据类型
List<Integer> list = Arrays.asList(arr);
System.out.println(list);
}
public static void demo1() {
String[] arr = {"a","b","c"};
List<String> list = Arrays.asList(arr); //将数组转换成集合
//list.add("d"); //不能添加
System.out.println(list);
}
数组转集合:必须是引用数据类型,基本数据类型只能输出地址值。
在数组转成集合的时候,不能进行修改操作,会报出如下错误:
Exception in thread "main" java.lang.UnsupportedOperationException
集合转换成数组:如果数组长度小于等于集合的元素个数,就会自动补全全部转换。如果数组长度大于集合元素个数,就会给一部分赋值,其余为null。
23.嵌套集合
案例代码:
ArrayList<ArrayList<Person>> arrayList = new ArrayList<>();
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三", 15));
list.add(new Person("李四", 16));
list.add(new Person("王五", 17));
ArrayList<Person> list2 = new ArrayList<>();
list2.add(new Person("赵六", 18));
list2.add(new Person("胡七", 19));
list2.add(new Person("李八", 20));
arrayList.add(list);
arrayList.add(list2);
for (ArrayList<Person> clazz : arrayList) {
for (Person person : clazz) {
System.out.println(person);
}
System.out.println("--------");
}
嵌套并遍历输出