java基础11
异常
- 如果在主函数上抛出异常,表示将这个异常抛给了Java虚拟机,JVM默认打印异常的栈轨迹
- 异常对方法的重载没有影响
- 在try执行完前,无论出现了异常与否,finally都会执行一次。
- 在try-catch出现的情况下,JVM在编译的时候认为try不一定能够执行成功,就意味着try中不一定能够正 常返回结果
- 异常是Java中一套用于问题的反馈和处理的机制
- Throwable — 异常的顶级父类
- Error — 错误表示合理(语法上还是逻辑上都是成立的)的应用程序中出现了严重的问题,而且这个问题不应该试图捕获 —意味着在程序中,错误一旦出现不能处理 —
VirtualMachineError : StackOverflowError, OutOfMemoryError - Exception — 表示合理的应用程序想要捕获的问题,也因此可以处理。处理方式:要么捕获要么抛出
- 编译时异常:在编译时期就已经出现,要求必须处理。
- CloneNotSupportedException UnsupportedEncodingException ParseException
- 运行时异常:在编译时期不出现到运行的时候才出现。可以处理可以不处理 — RuntimeException
ArithmeticException ArrayIndexOutOfBoundsException
NullPointerException ClassCastException
StringIndexOutOfBoundsException NumberFormatException - 自定义异常:写一个类继承Exception或者是其子类。如果是继承了RuntimeException及其子类,那么这个时候定义的是一个运行时异常;如果继承的是其他的Exception,那么定义的就是编译时异常
- 异常的捕获方式
A. 如果多个异常的处理方式各不一样,可以使用多个catch分别捕获分别处理
B. 如果所有异常的处理方式都一样,可以捕获这些异常的父类进行统一的处理
C. 如果多个异常进行了分组,那么同一组的异常之间用 | 隔开进行分组处理 — 从JDK1.7开始
package cn.tedu.exception;
public class ExceptionDemo6 {
public static void main(String[] args) {
System.out.println(m());
}
public static int m() {
int i = 4;
// 方法在栈内存中执行
// 方法在执行过程中的计算和存储是分成了2块
// 在代码执行过程中,会出现计算和存储分别进行的现象
// 代码是从上到下来依次编译运行的
try {
// 先运行return i++;
// 在计算区域将i的值从存储区域取出来进行计算
// 在计算的时候由于++在后,所以将i的值4作为数据参与下一步运算
// 下一步运算 return 4;
// 在执行return 4准备返回的时候需要检查是否有后续操作执行:i的自增,finally
// 先让i进行自增为5
// 然后继续执行下一个后续操作是finally
// 后续操作全部完成最后在执行刚才被挂起的return 4;
return i++;
} finally {
// 在finally中对i再次自增为6
i++;
System.out.println("finally:" + i);
}
}
}
public static Person m() {
Person p = new Person();
try {
p.setName("如花");
p.setAge(80);
// p是对象,所以return的是p的地址
return p;
} finally {
p.setName("周星星");
p.setAge(18);
}
}
package cn.tedu.exception;
public class ExceptionDemo2 {
public static void main(String[] args) /* throws FileNotExistException */ {
try {
String msg = readTxt("E:\\a.jpg");
System.out.println(msg);
} catch (FileNotExistException e) {
System.out.println(e.getMessage());
System.out.println("在处理这个问题~~~");
} catch (FileFormatException e) {
System.out.println(e.getMessage());
} catch (NullPointerException e) {
System.out.println("处理空指针~~~");
}
// try {
// String msg = readTxt("E:\\a.jpg");
// System.out.println(msg);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// }
// 一定是先子类异常后父类异常
try {
String msg = readTxt("E:\\a.jpg");
System.out.println(msg);
} catch (FileNotExistException | FileFormatException e) {
// System.out.println(e.getMessage());
// 打印栈轨迹
e.printStackTrace();
} catch (NullPointerException e) {
System.out.println("处理空指针~~~");
} catch (Exception e) {
}
System.out.println("running~~~");
}
public static String readTxt(String path) throws FileNotExistException, FileFormatException, NullPointerException {
if (path == null)
throw new NullPointerException("亲,路径不能并且空~~~");
// 可能路径不存在
if (path.startsWith("H:\\"))
throw new FileNotExistException("亲,这个文件不存在哦~~~");
// 文件格式不一样
if (!path.endsWith(".txt"))
throw new FileFormatException("亲,需要一个TXT文件~~~");
return "读取成功~~~";
}
}
@SuppressWarnings("serial")
class FileFormatException extends Exception {
public FileFormatException() {
}
public FileFormatException(String msg) {
super(msg);
}
}
@SuppressWarnings("serial")
class FileNotExistException extends Exception {
private String msg;
public FileNotExistException() {
}
public FileNotExistException(String msg) {
this.msg = msg;
}
public String getMessage() {
return msg;
}
}
public static void main(String[] args) throws CloneNotSupportedException, ParseException {
// try {
// System.out.println(1 / 0);
// } catch (ArithmeticException e) {
// System.out.println("在处理这个算术异常~~~");
// }
// int[] arr = new int[3];
// System.out.println(arr[5]);
// String str = null;
// System.out.println(str.length());
// Object o = new Object();
// String str = (String) o;
// Integer in = new Integer("abc");
// new ExceptionDemo().clone();
// "abc".getBytes("abc");
// new SimpleDateFormat("yyyy-MM-dd").parse("2012-03-05");
}
集合
- 集合是一个存储多个同一类型的数据的容器 — 大小是不固定的
- - 泛型,表示集合中的元素的数据类型
- List - 列表
保证元素的存入顺序,列表中的元素是可以重复的。可以通过下标操作指定位置上的元素
ArrayList - 顺序表
底层是基于数组来存储数据。内存空间是连续的。默认初始容量是10,每次扩容默认增加一半,基于了右移。增删操作相对复杂,查询操作相对简单。是一个线程不安全的列表。
LinkedList - 链表
基于节点(Node)来实现的。利用节点来存储数据以及维系链表之间每一个节点的关系。内存空间不连续。增删操作相对简单,查询操作相对复杂。是一个线程不安全的列表
LinkedList:基于节点,内存空间是不连续的。相对增删简单但是查询复杂。是一个线程不安全的列表
考虑:如果在增删和查询的次数相差不多的情况下,使用ArrayList还是LinkedList? — LinkedList
因为ArrayList要保证一段连续的存储空间,而LinkedList不用,节点都是散开分布的,所以存储性能好。
LinkedList的增删是基于二分查询的,跟中间比较,小的话从头开始查找、大的话从尾开始查找。
- Vector - 向量
最早的列表,依靠数组来存储数据,内存空间一定是连续的,初始默认是10,每次扩容默认增加一倍。是一个线程安全的列表 - Stack - 栈
继承了Vector。遵循后进先出/先进后出的原则。最先放入栈中的元素 — 栈底元素,最后放入栈中的元素 — 栈顶元素。将元素放入栈中 — 入栈/压栈,将元素从栈中取出 — 出栈/弹栈 - Set - 散列集合
包含的元素不重复,保证元素的唯一性
HashSet:散列集合,不包含重复的元素,不保证元素的存入顺序。底层基于HashMap来进行数据的存储。 -> 基于了数组+链表结构(链表栈结构)。默认初始容量是16,默认加载因子是0.75f,默认增加一倍。
TreeSet:要求存入的元素对应的类必须实现Comparable的接口,然后利用接口中compareTo方法进行自然排序;如果需要对某一个TreeSet单独指定排序规则,则需要传入一个Comparator对象
- 加载因子过小,会导致频繁发生rehash操作而降低效率,同时还造成空间资源的浪费
加载因子过大,会导致每一个桶中的链的长度过长,从而降低增删效率。
从JDK1.8开始,对HashSet的存储做了调优:如果桶中的链的长度大于了8,会将这个链式结构扭转成一个二叉树结构 - TreeSet — 会对元素进行整体的自然排序,需要元素对应的类实现Comparable接口
Comparable —类在实现这个接口之后对这个类的所有的对象进行整体排序
Comparator — 用于给某个对象单独指定规则
总结:如果需要给某个TreeSet对象单独指定比较规则,则使用Comparator;如果不指定比较规则,则使用Comparable进行整体的自然排序 - Collections - 操作集合的工具类
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
// 集合中存储的元素类型是String
Collection<String> c = new ArrayList<String>();
// 添加元素
c.add("gwe");
c.add("hred");
c.add("hrd");
c.add("ater");
// 将集合转化为数组
// Object[] os = c.toArray();
// String[] os = (String[]) c.toArray();
// for (Object o : os) {
// System.out.println(((String) o).length());
// }
// String[] strs = c.toArray(new String[0]);
// for (String str : strs) {
// System.out.println(str.length());
// }
// 获取集合中的元素个数
// System.out.println(c.size());
// 清空集合
// c.clear();
// 判断集合是否为空
// System.out.println(c.isEmpty());
// 判断元素是否存在
// System.out.println(c.contains("hrd"));
// System.out.println(c.contains("aft"));
// 从集合中移除指定的元素
// c.remove("gwe");
// 如果元素不存在,会直接跳过
// c.remove("age");
// System.out.println(c);
}
}
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
// 保证元素的存入顺序
list.add("bdd");
list.add("abd");
list.add("egd");
list.add("eds");
list.add("abd");
list.add(null);
// 截取子列表
System.out.println(list.subList(1, 3));
// 获取指定的元素在列表中第一次出现的位置
// System.out.println(list.indexOf("abd"));
// List<String> list2 = new ArrayList<String>();
// list2.add(new String("bdd"));
// list2.add("abd");
// list2.add("egd");
// list2.add("eds");
// 比较两个列表的时候是逐位比较是否一致
// System.out.println(list.equals(list2));
// for(int i = 0; i < list.size(); i++)
// System.out.println(list.get(i));
// 获取指定下标上的元素
// System.out.println(list.get(1));
// 向列表的指定的下标插入指定的元素
// list.add(1,"fec");
// list.add(4, "fec");
// list.add(6, "fec");
// 移除指定下标上的元素
// list.remove(0);
// list.remove(5);
// 替换指定位置上的元素
// list.remove(2);
// list.add(2, "abc");
// list.set(2, "abc");
System.out.println(list);
}
}
右移一位和除二的区别
https://blog.youkuaiyun.com/FlushHip/article/details/82495034
手写ArrayList
package cn.tedu.collection;
import java.util.Arrays;
public class ListExer {
public static void main(String[] args) {
ArrList list = new ArrList(1);
list.add("abc");
list.add("abc");
list.add("def");
// list.add(0, "hijk");
list.remove(0);
System.out.println(list);
}
}
class ArrList {
private String[] data;
// 记录元素的个数
private int size = 0;
public ArrList() {
data = new String[10];
}
public ArrList(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException();
data = new String[initialCapacity];
}
// 添加元素
public void add(String str) {
// 判断是否需要扩容
if (size >= data.length)
this.grow();
data[size++] = str;
}
// 扩容
private void grow() {
if (data.length <= 1)
data = Arrays.copyOf(data, data.length + 1);
else
data = Arrays.copyOf(data, data.length + (data.length >> 1));
}
// 插入元素
public void add(int index, String str) {
// 判断下标越界
if (index > size)
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
// 判断扩容
if (size >= data.length)
this.grow();
// 挪动元素空出对应的位置
// for (int i = size - 1; i >= index; i--) {
// data[i + 1] = data[i];
// }
System.arraycopy(data, index, data, index + 1, size - index);
// // 插入元素
data[index] = str;
size++;
}
private void out(int index) {
// 判断下标越界
if (index >= size)
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
// 根据下标移除指定的元素
public void remove(int index) {
this.out(index);
// for(int i = index; i < size - 1; i++){
// data[i] = data[i + 1];
// }
System.arraycopy(data, index + 1, data, index, size - (index + 1));
size--;
}
// 移除指定的元素
public void remove(String str) {
// 首先先找到这个元素出现的位置
int index = indexOf(str);
if (index != -1)
this.remove(index);
}
// 找这个元素第一次出现的位置
public int indexOf(String str) {
for (int i = 0; i < size; i++) {
if (data[i] == str || data[i] != null && data[i].equals(str))
return i;
}
return -1;
}
public void clear() {
data = new String[10];
size = 0;
}
public boolean contains(String str) {
return indexOf(str) != -1;
}
public String get(int index) {
this.out(index);
return data[index];
}
public boolean isEmpty() {
return size == 0;
}
public void set(int index, String str) {
this.out(index);
data[index] = str;
}
public int size() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(data[i]).append(", ");
}
String str = sb.toString();
if (size > 0)
str = str.substring(0, str.length() - 2);
return str += "]";
}
}
手写LinkedList
目录,再往上一拉
package cn.tedu.collection;
public class ListExer2 {
public static void main(String[] args) {
LinkList list = new LinkList();
list.add("a");
list.add("b");
list.add("c");
// list.add(0,"d");
// list.add(1, "e");
// list.remove(0);
// list.remove(2);
list.remove(1);
System.out.println(list);
}
}
class LinkList {
private int size = 0; // 节点个数
private Node first; // 第一个节点
private Node last; // 最后一个节点
public LinkList() {
}
public void add(String str) {
// 创建节点存储数据
Node node = new Node(null, str, null);
// 列表此时为空
if (size == 0) {
// 如果列表为空,则头结点指向新节点
this.first = node;
} else {
// 原来的尾节点的下一位置为新节点
this.last.next = node;
// 新节点的上一位是原来的尾节点
= this.last;
}
// 新的节点变成了尾节点
this.last = node;
size++;
}
public void add(int index, String str) {
// 判断下标是否越界
if (index > size)
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
// 在尾部追加
if (index == size) {
this.add(str);
return;
}
Node node = new Node(null, str, null);
// 插入的头部
if (index == 0) {
node.next = this.first;
this.first.prev = node;
this.first = node;
} else {
Node no = this.getNode(index);
// 原节点的上一个节点的下一位变成新的节点
no.prev.next = node;
// 新的节点的上一位是原节点的上一位
node.prev = no.prev;
// 新节点的下一位是原来的节点
node.next = no;
// 原节点的上一位是新的节点
no.prev = node;
}
size++;
}
private Node getNode(int index) {
// 获取指定位置的节点
Node no = this.first;
for (int i = 0; i < index; i++) {
no = no.next;
}
return no;
}
private void out(int index) {
// 判断下标越界
if (index >= size)
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
public void remove(int index) {
this.out(index);
// 头部
if (index == 0) {
this.first = this.first.next;
this.first.prev = null;
} /* 尾部 */ else if (index == size - 1) {
this.last = this.last.prev;
this.last.next = null;
} else {
Node node = this.getNode(index);
// 原节点的上一个节点的下一位变成原节点的下一位
node.prev.next = node.next;
// 原节点的下一个节点的上一位变成原节点的上一位
node.next.prev = node.prev;
}
size--;
}
public int indexOf(String str) {
Node node = this.first;
for (int i = 0; i < size; i++) {
String data = node.data;
if (data == str || data != null && data.equals(str))
return i;
node = node.next;
}
return -1;
}
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node node = this.first;
for (int i = 0; i < size; i++) {
sb.append(node.data).append(", ");
node = node.next;
}
String str = sb.toString();
if (size > 0)
str = str.substring(0, str.length() - 2);
return str += "]";
}
// 利用节点存储数据
private class Node {
Node prev; // 上一个节点
String data; // 元素
Node next; // 下一个节点
public Node(Node prev, String data, Node next) {
super();
this.prev = prev;
this.data = data;
this.next = next;
}
}
}
set集合排序
package cn.tedu.collection;
import java.util.Comparator;
import java.util.TreeSet;
public class SetDemo2 {
public static void main(String[] args) {
// TreeSet<String> set = new TreeSet<String>();
// 会根据自然顺序(往往是升序)进行排序
// set.add("d");
// set.add("g");
// set.add("a");
// set.add("r");
// set.add("c");
// System.out.println(set);
TreeSet<Student> set = new TreeSet<Student>();
// 按照分数进行降序排序
// TreeSet<Student> set = new TreeSet<Student>(new Comparator<Student>()
// {
//
// @Override
// public int compare(Student o1, Student o2) {
// return o2.getScore() - o1.getScore();
// }
// });
// 要求set中的元素所对应的类必须实现Comparable
set.add(new Student("01金毛狮王", 20, 59));
set.add(new Student("03白眉鹰王", 50, 18));
set.add(new Student("02紫衫龙王", 49, 89));
set.add(new Student("05青翼蝠王", 38, 60));
for (Student s : set) {
System.out.println(s);
}
}
}
class Student implements Comparable<Student> {
private String name;
private int age;
private int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
}
@Override
// 排序规则就写在compareTo中
// 在排序的时候会根据返回值的正负来确定元素的排序顺序
// 如果返回了一个正数,那么this就会排在o之后
// 如果返回了一个负数,那么this就会排在o之前
public int compareTo(Student o) {
// return this.age - o.age; //升序
// return o.age - this.age; //降序
return name.compareTo(o.name);
}
}
Stack - 栈集合
目录,再往上一拉
Stack<String> s = new Stack<String>();
// 添加元素
s.push("d");
s.push("a");
s.push("e");
s.push("s");
s.push("h");
// 从栈顶到栈底依次查找,以1基数
System.out.println(s.search("a"));
// 判断栈是否为空
// System.out.println(s.empty());
// 查看栈顶元素
// 如果栈为空,则抛出空栈异常
// System.out.println(s.peek());
// 移除栈顶元素
// System.out.println(s.pop());
System.out.println(s);
练习:使用数组/节点(Node)完成一个Stack — empty peek pop push search
目录,再往上一拉
package cn.tedu.collection;
import java.util.Arrays;
import java.util.EmptyStackException;
public class StackExer {
public static void main(String[] args) {
// ArrStack as = new ArrStack();
// as.push("a");
// as.push("b");
// as.push("c");
// as.pop();
LinkStack ls = new LinkStack();
ls.push("a");
ls.push("b");
ls.push("c");
System.out.println(ls);
}
}
class LinkStack {
private int size = 0;
private Node first;
public boolean empty() {
return size == 0;
}
public void push(String str) {
Node node = new Node(null, str);
// if (size != 0) {
// node.next = this.first;
// }
this.first = node;
size++;
}
public String peek() {
if (size == 0)
throw new EmptyStackException();
return this.first.data;
}
public String pop() {
String str = this.peek();
this.first = this.first.next;
size--;
return str;
}
public int search(String str) {
Node node = this.first;
for (int i = 0; i < size; i++) {
if (node.data == str || str != null && str.equals(node.data))
return i + 1;
node = node.next;
}
return -1;
}
@Override
public String toString() {
return "LinkStack [size=" + size + ", first=" + first + "]";
}
private class Node {
Node next;
String data;
public Node(Node next, String data) {
super();
this.next = next;
this.data = data;
}
}
}
class ArrStack {
private String[] data = new String[10];
private int size = 0;
public String peek() {
// 判断栈中是否有元素
if (size == 0)
throw new EmptyStackException();
return data[size - 1];
}
public String pop() {
String str = this.peek();
size--;
return str;
}
public void push(String str) {
// 判断是否需要扩容
if (size >= data.length)
data = Arrays.copyOf(data, data.length * 2);
data[size++] = str;
}
public int search(String str) {
// for(int i = size - 1, j = 1; i >= 0; i--, j++){
//
// if(str == data[i] || str != null && str.equals(data[i]))
// return j;
//
// }
for (int i = size - 1; i >= 0; i--) {
if (str == data[i] || str != null && str.equals(data[i]))
return size - i;
}
return -1;
}
}
JDK8 新特性
目录,再往上一拉
package exer1;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abe");
list.add("egs");
list.add("dgse");
list.add("3sdf");
list.add("sdh8");
list.add("dbks");
list.add("vn3hdso");
// 流式结构,但不是流,可以批量的操作集合中的数据
// JDK1.8的新特性之一
Stream<String> stream = list.stream();
System.out.println(stream.allMatch(str -> str.matches(".*\\d.*")));
// 判断集合中是否包含以字母a开头的字符串
System.out.println(stream.anyMatch(str -> str.startsWith("a")));
stream.filter(new Predicate<String>() {
@Override
public boolean test(String t) {
return t.matches(".*\\d.*");
}
}).forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
stream.filter(str -> str.matches(".*\\d.*"))
.map(str -> str.replaceAll("\\d", ""))
.forEach(str -> System.out.println(str));
// for (String str : list) {
// if (str.matches(".*\\d.*")) {
// str = str.replaceAll("\\d", "");
// System.out.println(str);
// }
// }
}
}
Vector
目录,再往上一拉
package cn.tedu.collection;
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
// Vector<String> v = new Vector<String>();
// 表示向量的初始容量是10,每次扩容增加5个
// Vector<String> v = new Vector<String>(10, 5);
// System.out.println(v.capacity());
//
// for (int i = 0; i < 21; i++) {
// v.add("a");
// }
//
// System.out.println(v.capacity());
Vector<String> v = new Vector<String>();
v.add("a");
v.add("d");
v.add("t");
v.add("h");
// 获取迭代器
Enumeration<String> e = v.elements();
// 判断后续是否还有元素
while (e.hasMoreElements()) {
// 挪动指针获取这一个元素
String str = e.nextElement();
System.out.println(str);
// 无法清空集合
// v.remove(str);
}
System.out.println(v);
}
}
迭代器
- 迭代器是通过指针的挪动来依次获取集合中的每一个元素。
- Enumeration - 最早期的迭代器
- Iterator -通过迭代器自带的remove方法来移除当前在迭代的元素, it.remove();
实质是:一个集合实现迭代器后(产生一个迭代器对象),迭代器会复制一份集合,然后对这份集合中每一个元素进行标记(true/false),然后去和原集合对比,确定每一个元素是否该删除。因此在迭代过程中不允许直接增删原集合,否则会产生ConcurrentModificationException 当前认证(标记)异常。 - Collection中的iterator方法是从Iterable中继承过来的,实现了Iterable接口的类产生的对象可以被增强for循环进行遍历。 — 增强for循环也是JDK1.5的特性之一。增强for循环本质上是一个迭代遍历,简化了迭代操作!
- 迭代器:Iterator - 通过状态标记和指针的挪动来遍历元素 - 在迭代过程中不允许直接增删原集合。Iterable接口提供了获取迭代器的方法,实现了这个接口的类所产生的对象可以被增强for循环遍历
List<String> list = new ArrayList<String>();
list.add("abe");
list.add("egs");
list.add("dgse");
list.add("dase");
list.add("3sdf");
list.add("sdh8");
list.add("dbks");
list.add("vn3hdso");
// 通过iterator方法获取到一个Iterator对象
Iterator<String> it = list.iterator();
// 判断是否有下一个元素
while(it.hasNext()){
// 挪动指针获取这个元素
String str = it.next();
System.out.println(str);
// 通过迭代器自带的remove方法来移除当前在迭代的元素
// it.remove();
list.remove(str);
}
System.out.println(list);
泛型
参数化类型 - ParameterizedType — JDK1.5的特性之一
用具体类型来替代泛型的过程 — 泛型的擦除 — 编译期
? extends 类/接口 表示传入这个类/接口或者是其子类/子接口对象 — 上限
? super 类/接口 表示传入这个类/接口及其父类/父接口的对象 — 下限
? 表示泛型的通配符
package cn.tedu.type;
import java.util.ArrayList;
//import java.util.Iterator;
import java.util.List;
public class TypeDemo1 {
public static void main(String[] args) {
// 在早期没有泛型的限制的时候,集合中存储元素是Object类型
// List list = new ArrayList();
// List<String> list = new ArrayList();
// List list = new ArrayList<String>();
// List<String> list = new ArrayList<String>();
// 从JDK1.7开始,后边实现类的泛型可以省略不写
// 会根据程序的上文来进行类型的自动推导
List<String> list = new ArrayList<>();
list.add("a");
// list.add(5);
// list.add(3.5);
// list.add(new Object());
// Iterator it = list.iterator();
// while (it.hasNext()) {
// Object o = it.next();
//
// if (o instanceof String) {
// String str = (String) o;
// } else if (o instanceof Integer) {
// Integer in = (Integer) o;
// }
// }
}
// public static void m(List list){}
}
package cn.tedu.type;
public class TypeDemo2 {
public static void main(String[] args) {
Demo<String, Integer> demo = new Demo<>();
demo.set("abc");
System.out.println(demo.get());
}
}
// <T> 表示定义了一个泛型叫T
// 泛型的命名只要符合标识符的命名规则即可
// 习惯上泛型一般只使用一个大写字母进行命名
// T type
// E element
// K key
// V value
// R result/return
class Demo<T, E> {
// 泛型定义的变量不能直接实例化
private T t;
E e;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
package cn.tedu.type;
public class TypeDemo3 {
public static void main(String[] args) {
A a = new A();
a.m(5);
a.m(true);
a.m("def");
}
}
class A {
// 表示给当前的方法定义了一个单独的泛型
public <T> void m(T t) {
System.out.println(t.getClass());
}
}
package cn.tedu.type;
import java.util.ArrayList;
import java.util.List;
public class TypeDemo4 {
public static void main(String[] args) {
List<Integer> ins = new ArrayList<>();
ins.add(3);
ins.add(4);
ins.add(6);
ins.add(7);
ins.add(0);
List<Double> dos = new ArrayList<>();
dos.add(3.6);
dos.add(4.2);
dos.add(6.8);
dos.add(7.7);
dos.add(0.2);
// 泛型不存在向下兼容
print(ins);
print(dos);
// List<String> strs = new ArrayList<>();
// print(strs);
}
// 写一个新的方法来遍历元素类型是数字的列表
// 元素类型是Number或者是其子类
// ? extends 类/接口 表示传入这个类/接口或者是其子类/子接口对象
// 所能传入的元素的最大类型限定为Number
// 规定了泛型的上限
public static void print(List<? extends Number> list) {
// 不能再添加任何元素除非是null
list.add(null);
for (Number n : list) {
System.out.println(n);
}
}
// 泛型的下限
// 传入元素类型是String及其父类的列表
// ? super 类/接口 表示传入这个类/接口及其父类/父接口的对象
// 表示传入的最小类型是String
public static void m(List<? super String> list){
list.add("abc");
for (Object o : list) {
System.out.println(o);
}
}
}
package cn.tedu.type;
import java.util.ArrayList;
import java.util.List;
public class TypeDemo5 {
public static void main(String[] args) {
List<Integer> ins = new ArrayList<>();
ins.add(7);
ins.add(3);
ins.add(6);
ins.add(0);
ins.add(4);
sort(ins);
}
// 可以接收任意类型,但是在排序(操作)期间不允许发生增删操作
public static void sort(List<?> list){
list.add(null);
}
}
Map<K, V> - 映射
- 映射:用于存储键值对的容器。每一个键对应一个值,键是唯一的。
- 一个键对应一个值。键是唯一的,值可以重复。
- 每一个键和它所对应的值构成了键值对。— 一个Map是由多个键值对来组成。
- 将每一个键值对看作一个对象,抽取出来一个代表键值对的接口
- Entry,内部会有一个内部类实现这个Entry接口。Entry是Map中的内部接口 — 一个Map是由多个Entry对象来组成的
- Map不是集合,但是Map是Java集合框架的成员。
Java集合框架(Java Collections Framework)包含:集合、数组、映射以及操作它们的工具类 — Arrays、Collections、Interator、Comparable、Comparator - 遍历映射
方式一:先获取映射中所有的键组成的集合,然后通过键获取对应的值
方式二:将所有的键值对放入集合中,然后遍历集合获取这个键值对的数据 - HashMap:基于哈希码存储,然后允许键和值为null。默认初始容量是16,默认加载因子是0.75f,每次默认增加一倍。自定义初始容量x,这个x介于[2n,2n+1],那么初始容量一定是2n+1 — 底层保证HashMap的容量永远是2n的形式。本身是一个异步式线程不安全的映射
- Hashtable:基于哈希码存储,然后不允许键和值为null。默认初始容量是11,默认加载因子是0.75f,每次默认增加一倍,然后再+1 — 11 -> 23。本身是一个同步式线程安全的映射。 ConcurrentHashMap — 异步式线程安全的映射
Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("d", 4);
map.put("h", 7);
map.put("z", 7);
map.put("o", 3);
map.put("t", 3);
// 如果键相同则对应的值覆盖
map.put("o", 0);
map.put("e", null);
// 获取键值对的个数
System.out.println(map.size());
// 移除键值对
// map.remove("o");
// 判断键是否存在
// System.out.println(map.containsKey("f"));
// 判断值是否存在
// System.out.println(map.containsValue(4));
// 根据指定的键获取对应的值
// System.out.println(map.get("o"));
// 如果键不存在则返回null
// System.out.println(map.get("y"));Map<String, Integer> map = new HashMap<>();
map.put("d", 4);
map.put("h", 7);
map.put("z", 7);
map.put("o", 3);
map.put("t", 3);
// 遍历映射
// 方式一:先获取映射中所有的键组成的集合,然后通过键获取对应的值
// keySet表示将映射中的所有的键放入一个Set集合中
// Set<String> set = map.keySet();
// for (String key : set) {
// System.out.println(key + "=" + map.get(key));
// }
// 方式二:将所有的键值对放入集合中,然后遍历集合获取这个键值对的数据
// entrySet表示将映射中的所有的键值对放入一个Set集合中
Set<Map.Entry<String, Integer>> set = map.entrySet();
// 遍历集合,然后从键值对中获取键和值
for (Map.Entry<String, Integer> entry : set) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
System.out.println(map);
输入一个字符串,统计其中每一个字符出现的次数
目录,再往上一拉
package cn.tedu.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 输入一个字符串,统计其中每一个字符出现的次数
*/
public class MapExer {
@SuppressWarnings("resource")
public static void main(String[] args) {
// 获取字符串
Scanner s = new Scanner(System.in);
String str = s.next();
// 定义一个映射,键存储字符,值存储个数
Map<Character, Integer> map = new HashMap<>();
// 遍历字符串,然后统计每一个字符出现的次数
for (int i = 0; i < str.length(); i++) {
// 获取对应位置的字符
char c = str.charAt(i);
// 判断这个字符在映射是否出现过
if (map.containsKey(c))
map.put(c, map.get(c) + 1);
else
map.put(c, 1);
}
// 遍历映射
for (Map.Entry<Character, Integer> entry : map.entrySet())
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
断言
- 对结果进行预测。
- assert 断言条件 : 错误信息;
- 在Java中,断言不是默认开启的,需要利用参数手动指定开启 -> -ea -> -enableassertion
Scanner s = new Scanner(System.in);
int age = s.nextInt();
// 进行断言
// 如果断言成功,则继续往下执行
// 如果断言失败,则抛出错误AssertionError
assert age > 0 : "预测年龄应该是大于0的数值,实际上是" + age;
System.out.println(age);
Scanner s = new Scanner(System.in);
int age = s.nextInt();
// 进行断言
// 如果断言成功,则继续往下执行
// 如果断言失败,则抛出错误AssertionError
assert age > 0;
System.out.println(age);
扩展:native修饰方法— 本地方法,没有方法体但不是抽象方法,方法体是在JVM中用C语言完成的,在本地方法栈中执行
目录,再往上一拉
Scanner s = new Scanner(System.in);
// 用空白字符进行分隔
int i = s.nextInt();
double d = s.nextDouble();
String str = s.next();
// 用回车换行进行分隔
// 以上次结束作为本次的开始
String str2 = s.nextLine();
// System.out.println(i);
// System.out.println(d);
// System.out.println(str);
System.out.println(str2.equals(""));