文章目录
一、语法
1、包
2、权限修饰符
3、final
变量有几种:
- 局部变量。
- 成员变量:1、静态成员变量2、实例成员变量
4、常量
(1)常量概述和基本作用
(2)常量做信息标志和分类
public static void move(int flag){
// 控制玛丽移动
switch (flag) {
case UP:
System.out.println("玛丽往上飞了一下~~");
break;
case DOWN:
System.out.println("玛丽往下蹲一下~~");
break;
case LEFT:
System.out.println("玛丽往左跑~~");
break;
case RIGHT:
System.out.println("玛丽往→跑~~");
break;
}
}
public class ConstantDemo2 {
public static final int UP = 1; // 上
public static final int DOWN = 2; // 上
public static final int LEFT = 3; // 左
public static final int RIGHT = 4; // 右
}
- 硬编码:死板,一次性,将可变变量用一个固定数值表示,这种方式在编码的过程中会导致变量很难修改。因此采用软编码
- 软编码:通过一个标记取代变量名称,而这个标记的值是可以不断变化的。但标记名称却是不变的,从而实现了“以不变应万变“。
- 硬编码和软编码的区别是:
软编码可以在运行时确定,修改;而硬编码是不能够改变的。
5、枚举
(1)枚举的概述
public enum Orientation {
UP, DOWN, LEFT, RIGHT;
}
public static void move(Orientation o){
// 控制玛丽移动
switch (o) {
case UP:
System.out.println("玛丽往上飞了一下~~");
break;
case DOWN:
System.out.println("玛丽往下蹲一下~~");
break;
case LEFT:
System.out.println("玛丽往左跑~~");
break;
case RIGHT:
System.out.println("玛丽往→跑~~");
break;
}
}
(2)枚举的使用场景演示
(3)抽象类的特征和注意事项
(4)抽象类的应用知识:模板方法模式
public abstract class Animal {
public void dog(){
System.out.print("狗很可爱,");
like();
}
public abstract void like();
}
class animallike extends Animal{
@Override
public void like() {
System.out.println("但小明更喜欢小猫");
}
}
class animallike2 extends Animal{
@Override
public void like() {
System.out.println("但小红更喜欢仓鼠");
}
}
public static void main(String[] args) {
animallike a1=new animallike();
a1.dog();
animallike2 a2=new animallike2();
a2.dog();
}
6、接口
(1)接口的概述和特点
(2)接口的基本使用:被实现
public interface Animal {
void run();
void eat();
}
public interface People {
String name1="小明";
void walk();
}
public static void main(String[] args) {
demo d=new demo("狗");
d.run();
d.eat();
d.walk();
}
}
class demo implements Animal,People{
private String name;
public demo(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name+"跑的快");
}
@Override
public void eat() {
System.out.println(name+"吃骨头");
}
@Override
public void walk() {
System.out.println(name1+"牵着"+name+"散步");
}
}
(3)接口与接口的关系:多继承
(4)接口新增方法
public static void main(String[] args) {
exam e=new exam();
e.goes();
}
class exam implements text3{
}
public interface text3 {
private void go(){
System.out.println("随便测试一下");
}
default void goes(){
System.out.println("学习接口");
go();
}
}
(5)使用接口的注意事项
7、多态
(1)多态的概述、多态的形式
(2)多态的优势
public static void main(String[] args) {
text3 e=new exam();
e.go();
System.out.println(e.name);
}
}
class exam extends text3{
public String name ="张三";
}
public abstract class text3 {
public void go(){
System.out.println("随便测试一下");
}
String name ="李四";
}
(3)多态下引用数据类型的类型转换
text3 e=new exam();
e.go();
System.out.println(e.name);
exam a= (exam) e;
System.out.println(a.name);
_______________________________________
if(a instanceof Tortoise){
Tortoise t = (Tortoise) a;
t.layEggs();
}else if(a instanceof Dog){
Dog d1 = (Dog) a;
d1.lookDoor();
}
(5)多态的案例
// 定义USB接口:接入、接出
// 定义两个这个接口的实现类分别为:鼠标、键盘
// 实现类里需要重写接口里的全部抽象方法
public interface USB {
void connect();
void unconnect();
}
// 实现类里需要定义一些独有的方法
public class KeyBoard implements USB {
private String name;
@Override
public void connect() {
System.out.println(name+"成功连接电脑");
}
public void type(){
System.out.println(name+"打出了:学习Java");
}
@Override
public void unconnect() {
System.out.println(name+"成功从电脑拔出");
}
public KeyBoard(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Mouse implements USB {
private String name;
@Override
public void connect() {
System.out.println(name+"成功连接电脑");
}
public void click(){
System.out.println(name+"双击了一下");
}
@Override
public void unconnect() {
System.out.println(name+"成功从电脑拔出");
}
public Mouse(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 创建一个电脑对象(面对对象程序设计)
public class Computer {
private String name;
public Computer(String name) {
this.name = name;
}
public void start(){
System.out.println(name+"开机了");
}
public void installusb(USB usb){
usb.connect();
if(usb instanceof KeyBoard){
((KeyBoard) usb).type();
}
else if(usb instanceof Mouse){
((Mouse) usb).click();
}
usb.unconnect();
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
8、内部类
内部类概述
内部类的分类
- 静态内部类
- 成员内部类
- 局部内部类
- 匿名内部类
(1)静态内部类
(2)成员内部类
(3)匿名内部类
public static void main(String[] args){
swimming s=new swimming() {
@Override
public void swim() {
System.out.println("swim");
}
};
go(s);
}
public static void go(swimming s){
System.out.println("start");
s.swim();
System.out.println("over");
}
}
interface swimming{
void swim();
}
二、常用API
1、Object
(1)toString方法
public class Student {
String name;
char sex;
int age;
public Student(String name, char sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
Student s=new Student("张三",'男',18);
System.out.println(s.toString());
// 默认toString可以不写
System.out.println(s);
}
(2)equals方法
@Override
public boolean equals(Object o){
if(o instanceof Student ){
Student s=(Student) o;
return this.age==s.age&&this.name.equals(s.name)&&this.sex==s.sex;
}
else{
return false;
}
}
2、Objects方法
使用Objects的equals 方法在进行对象的比较更安全
3、StringBulider
StringBuilder sb1 = new StringBuilder();
// 支持链式编程
sb1.append("a").append("b").append("c").append("我爱你中国");
System.out.println(sb1);
// 注意:StringBuilder只是拼接字符串的手段:效率好。
// 最终的目的还是要恢复成String类型。
StringBuilder sb2 = new StringBuilder();
sb2.append("123").append("456");
// 恢复成String类型
String rs = sb2.toString();
- 定义字符串使用String
- 拼接、修改等操作字符串使用StringBuilder
4、Math类
- 包含执行基本数字运算的方法,Math类没有提供公开的构造器
- 如何使用类中的成员:看类的成员是否都是静态的,如果是,通过类名就可以直接调用
public static void main(String[] args) {
// 1.取绝对值:返回正数
System.out.println(Math.abs(10)); // 10
System.out.println(Math.abs(-10.3)); // 10.3
// 2.向上取整: 5
System.out.println(Math.ceil(4.00000001)); // 5.0
System.out.println(Math.ceil(4.0)); // 4.0
// 3.向下取整:4
System.out.println(Math.floor(4.99999999)); // 4.0
System.out.println(Math.floor(4.0)); // 4.0
// 4.求指数次方
System.out.println(Math.pow(2 , 3)); // 2^3 = 8.0
// 5.四舍五入 10
System.out.println(Math.round(4.49999)); // 4
System.out.println(Math.round(4.500001)); // 5
System.out.println(Math.random()); // 0.0 - 1.0 (包前不包后)
// 拓展: 3 - 9 之间的随机数 (0 - 6) + 3
// [0 - 6] + 3
int data = (int)(Math.random() * 7) + 3;
System.out.println(data);
}
5、System类
System也是一个工具类,代表了当前系统,提供了一些与系统相关的方法
常用方法:
public static void main(String[] args) {
System.out.println("程序开始。。。");
// System.exit(0); // JVM终止!
// 2、计算机认为时间有起源:返回1970-1-1 00:00:00 走到此刻的总的毫秒值:时间毫秒值。
long time = System.currentTimeMillis();
System.out.println(time);
long startTime = System.currentTimeMillis();
// 进行时间的计算:性能分析
for (int i = 0; i < 100000; i++) {
System.out.println("输出:" + i);
}
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime)/1000.0 + "s");
// 3、做数组拷贝(了解)
/*arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length)
参数一:被拷贝的数组
参数二:从哪个索引位置开始拷贝
参数三:复制的目标数组
参数四:粘贴位置
参数五:拷贝元素的个数*/
int[] arr1 = {10, 20, 30, 40, 50, 60, 70};
int[] arr2 = new int[6]; // [0, 0, 0, 0, 0, 0] ==> [0, 0, 40, 50, 60, 0]
System.arraycopy(arr1, 3, arr2, 2, 3);
System.out.println(Arrays.toString(arr2));
6、BigDecimal
作用:用于解决浮点型运算精度失真的问题
// 注意事项:BigDecimal是一定要精度运算的
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
//参数一:除数 参数二:保留小数位数 参数三:舍入模式
BigDecimal c11 = a11.divide(b11, 2, RoundingMode.HALF_UP); // 3.3333333333
System.out.println(c11);
7、日期和时间
(1)Date
Date类概述:
Date类代表当前所在系统的日期时间信息
Date的构造器:public Date(),创建一个Date对象,代表的是系统当前此刻日期时间
Date的常用方法:public long getTime(),返回从1970年一月一日走到此刻的总毫秒数
时间毫秒值—>日期对象
1、构造器:public Date(long time),把时间毫秒转换成Date日期对象
2、Date方法:public void setTime(long time),设置日期对象的时间为当前时间毫秒值对应的时间
案例:计算出当前时间1小时121秒后的时间
public static void main(String[] args) {
Date d=new Date();
System.out.println(d);
long time=System.currentTimeMillis();
time+=(60*60+121)*1000;
Date d2=new Date(time);
Date d3=new Date();
System.out.println(d2);
d3.setTime(time);
System.out.println(d3);
}
(2)SimpleDateFormat
作用:可以完成日期时间的格式化操作
格式化的时间形式常用的模式对应关系如下
年:y 月:M 日:d 时:H 分:m 秒:s
SimpleDateFormat解析字符串时间成为日期对象:2022-4-25 20:47:35—>Date日期对象
案例:计算出该日期往后走2天14小时9分06秒的时间是多少
public static void main(String[] args) throws ParseException {
// 1、把字符串时间拿到程序中来
String time="2022年4月25日 20:47:35";
// 2、把字符串时间解析成时间对象,格式必须一致
SimpleDateFormat d=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d1=d.parse(time);
// 3、往后走2天14小时9分06秒
long time2=d1.getTime()+(2L*24*60*60+14*60*60+9*60+6)*1000;
System.out.println(d.format(time2));
}
(3)Calendar
概述:
- Calendar代表了系统此刻日期对应的日历对象
- Calendar是一个抽象类,不能直接创建对象
Calendar日历类创建日历对象的方法
public static Calendar getInstance() (获取当前日历对象)
(4)JDK8新增日期类
三、
1、包装类
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量
包装类的特有功能:
- 包装类的变量的默认值可以是null,容错率高
- 可以把基本类型的数据转换成字符串类型(作用不大)
//可以用这个方法
Integer a=21;
String s=a+"1";
System.out.println(s);
- 可以把字符串类型的数值转换成真实的数据类型
String number = "23";
//转换成整数
int age = Integer.parseInt(number);
int age = Integer.valueOf(number);
System.out.println(age + 1);
2、正则表达式
正则表达式的概述
正则表达式可以用一些规定的字符来制定规则,并用来校验数据格式的合法性。
(1)正则表达式的使用
(2)正则表达式在字符串方法中的使用
String a="张三123456abcdefg李四";
String arr[]=a.split("\\w+");
for (int i = 0; i < arr.length ; i++) {
System.out.println(arr[i]);
}
String name=a.replaceAll("\\w+",",");
System.out.println(name);
(3)正则表达式支持爬取信息
3、Arrays类
(1)Arrays类概述
数组操作工具类,专门用于操作数组元素的。
// 1、返回数组内容的 toString(数组)
String rs = Arrays.toString(arr);
System.out.println(rs);
// 2、排序的API(默认自动对数组元素进行升序排序)
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 3、二分搜索技术(前提数组必须排好序才支持,否则出bug)
int index = Arrays.binarySearch(arr, 55);
System.out.println(index);
// 返回不存在元素的规律: - (应该插入的位置索引 + 1)
int index2 = Arrays.binarySearch(arr, 22);
System.out.println(index2);
// 注意:数组如果么有排好序,可能会找不到存在的元素,从而出现bug!!
int[] arr2 = {12, 36, 34, 25 , 13, 24, 234, 100};
System.out.println(Arrays.binarySearch(arr2 , 36));
(2)Arrays类对于Comparator比较器的支持
如果认为左边数据 大于 右边数据 返回正整数
如果认为左边数据 小于 右边数据 返回负整数
如果认为左边数据 等于 右边数据 返回0
//降序排序(自定义比较器对象,只能支持引用类型的排序!!)
Integer[] ages1 = {34, 12, 42, 23};
/**
参数一:被排序的数组 必须是引用类型的元素
参数二:匿名内部类对象,代表了一个比较器对象。
*/
Arrays.sort(ages1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// 指定比较规则。
// if(o1 > o2){
// return 1;
// }else if(o1 < o2){
// return -1;
// }
// return 0;
// return o1 - o2; // 默认升序
return o2 - o1; // 降序
}
});
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 自己指定比较规则
// return o1.getAge() - o2.getAge(); // 按照年龄升序排序!
// return o2.getAge() - o1.getAge(); // 按照年龄降序排序!!
// return Double.compare(o1.getHeight(), o2.getHeight()); // 比较浮点型可以这样写 升序
return Double.compare(o2.getHeight(), o1.getHeight()); // 比较浮点型可以这样写 降序
}
});
4、常见算法
(1)选择排序
选择排序的思想:每轮选择当前位置,开始找出后面的较小值与该位置交换
选择排序的关键:1、确定总共需要选择几轮: 数组的长度-1.
2、控制每轮从以前位置为基准,与后面元素选择几次。
public static void paixu(int[] arr){
for (int i = 0; i < arr.length-1 ; i++) {
for (int j =i+1; j < arr.length ; j++) {
if(arr[i]>arr[j]){
int t=0;
t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
}
}
System.out.println(Arrays.toString(arr));
}
(2)二分查找
public static int chazhao(int[] arr,int number){
int left=1;
int right= arr.length;
while(left<=right){
int mid=(left+right)/2;
if(arr[mid]>number){
right=mid-1;
}
else if(arr[mid]<number){
left=mid+1;
}
else{
return mid;
}
}
return -1;
}
5、Lambda表达式
Lambda概述
Lambda表达式是JDK 8开始后的一种新语法形式。
作用:简化匿名内部类的代码写法。
注意:Lambda表达式只能简化函数式接口的匿名内部类的写法形式
什么是函数式接口?
首先必须是接口、其次接口中有且仅有一个抽象方法的形式
public static void main(String[] args){
run(()->{
System.out.println("游泳中");
});
swim s=()->{
System.out.println("在游泳中");
};
run(s);
}
public static void run(swim s){
System.out.println("开始");
s.swimming();
System.out.println("结束");
}
}
interface swim{
public abstract void swimming();
}
Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
6、集合类
(1)集合的体系特点
集合:1、Collection单列 2、Map双列
Collection单列集合,每个元素(数据)只包含一个值
Map双列集合,每个元素包含两个值(键值对)
List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
(2)Collection集合常用API
public static void main(String[] args) {
// HashSet:添加的元素是无序,不重复,无索引。
Collection<String> c = new ArrayList<>();
// 1.添加元素, 添加成功返回true。
c.add("HTML");
System.out.println(c.add("HTML"));
// 2.清空集合的元素。
c.clear();
System.out.println(c);
// 3.判断集合是否为空 是空返回true,反之。
// System.out.println(c.isEmpty());
// 4.获取集合的大小。
System.out.println(c.size());
// 5.判断集合中是否包含某个元素。
System.out.println(c.contains("Java")); // true
// 6.删除某个元素:如果有多个重复元素默认删除前面的第一个!
System.out.println(c.remove("Java")); // true
// 7.把集合转换成数组 [HTML, HTML, MySQL, Java, 黑马]
Object[] arrs = c.toArray();
System.out.println("数组:" + Arrays.toString(arrs));
//8、
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("赵敏");
c2.add("殷素素");
// addAll把c2集合的元素全部倒入到c1中去。
c1.addAll(c2);
System.out.println(c1);
System.out.println(c2);
}
(3)Collection集合的遍历方式
方式一:迭代器遍历概述:迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
// 1、得到当前集合的迭代器对象。
Iterator<String> it = lists.iterator();
// 2、定义while循环
while (it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
方式二:foreach/增强for循环
增强for循环:既可以遍历集合也可以遍历数组
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
for (String ele : lists) {
System.out.println(ele);
}
方式三:lambda表达式
arr.forEach (integer-> System.out.println(integer));
7、常见数据结构简述
(1)数据结构概述、栈、队列
数据结构概述
数据结构是计算机底层存储、组织数据的方式。是指数据相互之间是以什么方式排列在一起的。
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
常见的数据结构:
栈
队列
数组
链表
二叉树
二叉查找树
平衡二叉树
红黑树
…
栈数据结构的执行特点:后进先出,先进后出
队列数据结构的执行特点:先进先出,后进后出
(2)数组
数组是一种查询快,增删慢的模型
- 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
- 删除效率低:要将原始数据删除,同时后面每个数据前移。
- 添加效率极低:添加位置后的每个数据后移,再添加元素。
(3)链表
链表的特点:
- 链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址。
- 链表查询慢。无论查询哪个数据都要从头开始找
- 链表增删相对快
链表的种类
单向链表
双向链表
(4)二叉树、 二叉查找树
二叉查找树又称二叉排序树或者二叉搜索树。
特点:
1,每一个节点上最多有两个子节点
2,左子树上所有节点的值都小于根节点的值
3,右子树上所有节点的值都大于根节点的值
(5)平衡二叉树
二叉树查找存在的问题:出现瘸子现象,导致查询的性能与单链表一样,查询速度变慢!
平衡二叉树是在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
平衡二叉树的要求:任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树。
平衡二叉树在添加元素后可能导致不平衡
基本策略是进行左旋,或者右旋保证平衡
(6)红黑树
红黑树概述:
- 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
- 1972年出现,当时被称之为平衡二叉B树。1978年被修改为如今的"红黑树"。
- 每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。
红黑规则:
1、每一个节点或是红色的,或者是黑色的,根节点必须是黑色。
2、如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。
3、对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
4、如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的;
添加节点:添加的节点的颜色,可以是红色的,也可以是黑色的。
默认用红色效率高。
8、List系列集合
(1)List集合特有API
List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
(2)List集合的遍历方式小结
List集合的遍历方式有几种?
1、迭代器
2、增强for循环
3、Lambda表达式
4、for循环(因为List集合存在索引)
/** (1)for循环。 */
for (int i = 0; i < lists.size(); i++) {
String ele = lists.get(i);
System.out.println(ele);
}
(3)ArrayList集合的底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
(4)LinkedList集合的底层原理
LinkedList的特点:
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
// LinkedList可以完成队列结构,和栈结构 (双链表)
// 1、做一个队列:
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
System.out.println(queue);
// 出队
// System.out.println(queue.getFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
// 2、做一个栈
LinkedList<String> stack = new LinkedList<>();
// 入栈 压栈 (push)
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
System.out.println(stack);
// 出栈 弹栈 pop
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
(5)集合的并发修改异常问题
从集合中的一批元素中找出某些数据并删除
哪些遍历存在问题?
迭代器遍历集合且直接用集合删除元素的时候可能出现。
增强for循环遍历集合且直接用集合删除元素的时候可能出现。
哪种遍历且删除元素不出问题?
迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
// a、迭代器遍历删除
Iterator<String> it = list.iterator();
while (it.hasNext()){
String ele = it.next();
if("Java".equals(ele)){
// 删除Java
// list.remove(ele); // 集合删除会出毛病
it.remove(); // 删除迭代器所在位置的元素值(没毛病)
}
使用for循环遍历并删除元素不会存在这个问题。但需注意从最后一个开始删或者删后i-1
9、泛型
(1)泛型的概述和优势
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。
(2)自定义泛型类
定义类时同时定义了泛型的类就是泛型类。
泛型类的格式:修饰符 class 类名<泛型变量>{ }
范例:public class MyArrayList<T> { }
此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等。
作用:编译阶段可以指定数据类型,类似于集合的作用。
泛型类的原理: 把出现泛型变量的地方全部替换成传输的真实数据类型
需求:模拟ArrayList定义一个MyArrayList ,关注泛型设计
MyArrayList<String> list = new MyArrayList<>();
list.add("Java");
list.add("MySQL");
System.out.println(list);
MyArrayList<Integer> list2 = new MyArrayList<>();
list2.add(23);
System.out.println(list2);
public class MyArrayList<E> {
public void add(E e){
lists.add(e);
}
}
(3)自定义泛型方法
泛型方法的概述:定义方法时同时定义了泛型的方法就是泛型方法。
泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
范例: public <T> void show(T t) { }
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性
泛型方法的原理:
把出现泛型变量的地方全部替换成传输的真实数据类型。
泛型方法的作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性
(4)自定义泛型接口
泛型接口的概述:使用了泛型定义的接口就是泛型接口。
泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}
范例: public interface Data<E>{}
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
泛型接口的原理:实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作
public interface Data<E> {
void add(E e);
void update(E e);
}
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public void update(Teacher teacher) {
}
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public void update(Student student) {
}
(5)泛型通配符、上下限
通配符:?
? 可以在“使用泛型”的时候代表一切类型。
E T K V 是在定义泛型的时候使用的。
泛型的上下限:
? extends Car: ?必须是Car或者其子类 泛型上限
? super Car : ?必须是Car或者其父类 泛型下限
//所有车比赛
public static void go(ArrayList<? extends Car> cars){
}
四、Set系列集合、Map集合体系
1、Set系列集合
(1)Set系列集系概述
Set系列集合特点:
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点:
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。
Set集合的功能上基本上与Collection的API一致
(2)HashSet元素无序的底层原理:哈希表
HashSet底层原理
HashSet集合底层采取哈希表存储的数据。
哈希表是一种对于增删改查数据性能都较好的结构。
哈希表的组成
JDK8之前的,底层使用数组+链表组成
JDK8开始后,底层采用数组+链表+红黑树组成。
哈希值
Object类的API
public int hashCode():返回对象的哈希值
是JDK根据对象的地址,按照某种规则算出来的int类型的数值
Object类的API
public int hashCode():返回对象的哈希值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。
HashSet1.7版本原理解析:数组 + 链表 +(结合哈希算法)
1、创建一个默认长度16的数组,数组名table
2、根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
3、判断当前位置是否为null,如果是null直接存入
4、如果位置不为null,表示有元素,则调用equals方法比较
5、如果一样,则不存,如果不一样,则存入数组,
JDK 7新元素占老元素位置,指向老元素
JDK 8中新元素挂在老元素下面
结论:哈希表是一种对于增删改查数据性能都较好的结构。
JDK1.8版本开始HashSet原理解析
底层结构:哈希表(数组、链表、红黑树的结合体)
当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。
当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
(3)HashSet元素去重复的底层原理
Set集合去重复原因:先判断哈希值算出来的存储位置是否相同 再判断equals
如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法。
(4)实现类:LinkedHashSet
LinkedHashSet集合概述和特点
有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
(5)实现类:TreeSet
TreeSet集合概述和特点
不重复、无索引、可排序
可排序:按照元素的大小默认升序(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet无法直接排序。
自定义排序规则
TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则
方式一
让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
public class Apple implements Comparable<Apple>{
@Override
public int compareTo(Apple o) {
// 按照重量进行比较的
return this.weight - o.weight ; // 去重重量重复的元素
// return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素
}
}
方式二
TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
// return o2.getWeight() - o1.getWeight(); // 降序
// 注意:浮点型建议直接使用Double.compare进行比较
// return Double.compare(o1.getPrice() , o2.getPrice()); // 升序
}
});
两种方式中,关于返回值的规则:
如果认为第一个元素大于第二个元素返回正整数即可。
如果认为第一个元素小于第二个元素返回负整数即可。
如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
总结
2、可变参数
可变参数用在形参中可以接收多个数据。
可变参数的格式:数据类型…参数名称
可变参数的作用:传输参数非常灵活,方便。可以不传输参数,可以传输1个或者多个,也可以传输一个数组
可变参数在方法内部本质上就是一个数组。
可变参数的注意事项:
1.一个形参列表中可变参数只能有一个
2.可变参数必须放在形参列表的最后面
sum(); // 1、不传参数
sum(10); // 2、可以传输一个参数
sum(10, 20, 30); // 3、可以传输多个参数
sum(new int[]{10, 20, 30, 40, 50}); // 4、可以传输一个数组
public static void sum( int...nums){
// 注意:可变参数在方法内部其实就是一个数组。 nums
System.out.println("元素个数:" + nums.length);
}
3、集合工具类Collections
java.utils.Collections:是集合工具类
作用:Collections并不属于集合,是用来操作集合的工具类。
Collections排序相关API
使用范围:只能对于List集合的排序。
排序方式1:public static void sort(List list) 将集合中元素按照默认规则排序。
排序方式2:public static void sort(List list,Comparator<? super T> c) 将集合中元素按照指定规则排序
- 方式一:类自定义比较规则
- 方式二:sort方法自带比较器对象 Collections.sort(apples, ( o1, o2) -> Double.compare(o1.getPrice() , o2.getPrice()) );
4、Map集合体系
(1)Map集合的概述
Map集合是一种双列集合,每个元素包含两个数据。
Map集合的每个元素的格式:key=value(键值对元素)。
Map集合也被称为“键值对集合”。
Map集合整体格式:
Collection集合的格式: [元素1,元素2,元素3…]
Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , …}
Map集合非常适合做类购物车这样的业务场景
(2)Map集合体系特点
Map集合实现类特点
HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
TreeMap:元素按照建是排序,不重复,无索引的,值不做要求。
(3)Map集合常用API
// 11.合并其他Map集合。(拓展)
Map<String , Integer> map1 = new HashMap<>();
map1.put("java1", 1);
map1.put("java2", 100);
Map<String , Integer> map2 = new HashMap<>();
map2.put("java2", 1);
map2.put("java3", 100);
map1.putAll(map2); // 把集合map2的元素拷贝一份到map1中去
System.out.println(map1);//java1:1,java2:1,java3:100
System.out.println(map2);//java2:1,java3:100
// 1.添加元素: 无序,不重复,无索引。
Map<String , Integer> maps = new HashMap<>();
maps.put("iphoneX",10);
maps.put("iphoneX",100);// Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!
System.out.println(maps);
// 2.清空集合
// maps.clear();
// 3.判断集合是否为空,为空返回true ,反之!
System.out.println(maps.isEmpty());
// 4.根据键获取对应值:public V get(Object key)
Integer key = maps.get("huawei");
System.out.println(maps.get("生活用品")); // 10
System.out.println(maps.get("生活用品2")); // null
// 5.根据键删除整个元素。(删除键会返回键的值)
System.out.println(maps.remove("iphoneX"));
// 6.判断是否包含某个键 ,包含返回true ,反之
System.out.println(maps.containsKey("娃娃")); // true
System.out.println(maps.containsKey("娃娃2")); // false
// 7.判断是否包含某个值。
System.out.println(maps.containsValue(100)); //
System.out.println(maps.containsValue(10)); //
System.out.println(maps.containsValue(22)); //
// {huawei=100, 手表=10, 生活用品=10, 娃娃=20}
// 8.获取全部键的集合:public Set<K> keySet()
Set<String> keys = maps.keySet();
// 9.获取全部值的集合:Collection<V> values();
Collection<Integer> values = maps.values();
// 10.集合的大小
System.out.println(maps.size()); // 4
(4)Map集合的遍历方式
方式一:键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大。
方式三:JDK 1.8开始之后的新技术:Lambda表达式。
先获取Map集合的全部键的Set集合。
Map集合的遍历方式一:键找值
先获取Map集合的全部键的Set集合。
遍历键的Set集合,然后通过键提取对应值。
//第一步:先拿到集合的全部键。
Set<String> keys = maps.keySet();
// 2、第二步:遍历每个键,根据键提取值
for (String key : keys) {
int value = maps.get(key);
System.out.println(key + "===>" + value);
}
键找值涉及到的API:
Map集合的遍历方式二:键值对
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
遍历Set集合,然后提取键以及提取值。
键值对涉及到的API:
// 1、把Map集合转换成Set集合
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
// 2、开始遍历
for(Map.Entry<String, Integer> entry : entries){
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key + "====>" + value);
}
Map集合的遍历方式三:lambda表达式
maps.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String key, Integer value) {
System.out.println(key + "--->" + value);
}
});
(5)Map集合案例-统计投票人数
Map<Character,Integer> m=new HashMap<>();
StringBuilder sb=new StringBuilder();
String[] sight={"A","B","C","D"};
Random r=new Random();
for (int i = 0; i <80 ; i++) {
sb.append(sight[r.nextInt(sight.length)]);
}
System.out.println(sb.toString());
for (int i = 0; i <sb.length() ; i++) {
char c=sb.charAt(i);
if(m.containsKey(c)){
m.put(c,m.get(c)+1);
}else{
m.put(c,1);
}
}
System.out.println(m);
(6)Map集合的实现类HashMap
HashMap的特点
HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
依赖hashCode方法和equals方法保证键的唯一。
如果键要存储的是自定义对象,需要重写hashCode和equals方法。
(7)Map集合的实现类LinkedHashMap
LinkedHashMap集合概述和特点:
由键决定:有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
(8)Map集合的实现类TreeMap
TreeMap集合概述和特点
由键决定特性:不重复、无索引、可排序
可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap跟TreeSet一样底层原理是一样的。
TreeMap集合自定义排序规则有2种
1、类实现Comparable接口,重写比较规则。
2、集合自定义Comparator比较器对象,重写比较规则。