一、JDK与JRE
1. JDK是开发工具包,包含三部分:
- jvm虚拟机:Java程序运行的地方
- 核心类库:Java已经写好的
- 开发工具:Javac、Java、jdb……
2. JRE是Java运行环境,包含三部分:
- jvm
- 核心类库
- 运行工具
3. JDK、JRE、JVM三者关系
JDK包含JRE、JRE包含JVM
二、算术运算符
1. 隐式转换小结
- 取值范围:byte<short<int<long<float<doule
- byte、short、char会自提升为int,再计算
三、面向对象
1. 面向对象的介绍:
- 面向:拿、找
- 对象:能干活的东西
- 面向对象:拿能干活的东西做对应的事
2. 构造器
- 创建对象的时候, 虚拟机自动调用构造器,其作用是初始化成员变量
- 创建对象是 new 的事,而不是 构造器 的事
- 分为有参和无参构造器且构成重载
- 推荐把无参构造器和带有所有成员变量的构造器都写出来
3. ptg
- 标准JavaBean插件
4. 对象的内存图
(1)创建对象虚拟机至少做了以下7步:
- 加载class文件
- 申明局部变量
- 在堆空间中开辟一个空间
- 默认初始化
- 显示初始化
- 构造器初始化
- 将堆内存中的地址赋值给左边的局部变量
(2)一个对象的内存图
public class Student{
String name;
int age;
public void study(){
sout("好好学习");
}
}
public class SudentTest{
public static void mian(String[] args){
Student s = new Student();
sout(s)
s.name = "阿龙";
s.age = 21;
s.study()
}
}
5. StringBuilder介绍
- StringBuilder 是线程不安全的,效率比较高,是可变的
- 应用场景主要:字符串拼接 和 字符串反转
- 添加方法是 append()
- 默认容量为 16 字节
- 第一次扩容为 16*2+2=34,超过34以后以实际字符长度为容量,最大容量为 Int 最大值
6. StringJoiner介绍
- StringJoiner 是可变的,JDk8出现
- 添加属性是 add()
- length() 求的是所有的长度包括开始符号、结束符号、间隔符号
- StringJoiner stringJoiner = new StringJoiner(",") 这是以 "," 为间隔
- StringJoiner stringJoiner = new StringJoiner(",", "[", "]"); 这是以"," 间隔 、"[" 开始、"]" 结束
7. 封装的介绍
- 对象代表什么,就得封装对应的数据,并提供数据对应的行为
8. 继承的介绍
- 可以把子类中重复的代码抽取到父类中,子类可以直接使用,从而减少代码的冗余,提高代码的复用性
- 只支持单继承,不支持多继承,但支持多层继承
9. 多态的介绍
- 多态:同类型的对象,表现出的不同形态
- 成员属性:编译时看左边,运行时也看左边
-
成员方法:编译时看左边 ,运行时看右边
class Fu{ String name = "父亲"; int age = 40; public void work(){ sout("父亲在工作"); } }
class Zi {
String name = "儿子"
int age = 21;
@Override
public void work(){
sout("儿子在工作");
}
}
class Test{
public static void main(String[] args){
Fu f = new Zi();
sout(f.name);//父亲
f.work();//儿子在工作
}
}
- 多态的前提
- 需要有继承/实现关系
- 有父类引用指向子类对象 Fu f = new Zi()
- 有方法重写
- 多态的优势
- 在多态形态下,右边对象可以实现解耦合,便于扩展与维护
- 定义方法时,使用父类作为参数,可以接收所有子类对象,体现多态的扩展性与便利
- 多态的弊端
- 使用Fu f = new Zi()创建对象时,子类扩展的方法不能调用
- 解决方案:Zi z = (Zi) f
10. final介绍
- final是最终类,不能被继承
- final是最终方法,不能被重写
- final叫做常量,只能被赋值一次,不能被修改;修饰基本类型常量,值不能被修改;修饰引用数据类型,地址值不能被修改,其内容可以被修改
11. 代码块
- 局部代码块:提前结束变量的生命周期(已淘汰)
- 构造代码块:抽取构造方法中重复的代码(不够灵活)
- 静态代码块:随着类的加载而加载,只能被加载一次;可以做数据的初始化
12. 抽象类和抽象方法的介绍
- 抽象类的作用:抽取共性时,无法确定方法体,就把方法定义为抽象的。强制子类按照某种格式重写。抽象方法所在的类必须是抽象类
- 抽象类不能实例化
- 抽象类中不一定有抽象方法,有抽象方法的一定是抽象类
- 可以有构造方法
- 抽象类的子类:要么重写抽象类的所有抽象方法,要么是抽象类
13. 接口(interface)的介绍
- 接口就是一种规则
- 接口不能实例化
- 接口成员的特点:
- 成员变量:只能是常量,默认修饰符:public static final
- 构造方法:没有
- 成员方法:只能是抽象方法,默认修饰符:public abstract
- JDk7以前:接口中只能定义抽象方法
- JDk8的新特性:接口中可以定义有方法体的方法
- JDK9的新特性:接口中可以定义私有方法
14. 接口和类的关系
- 类和类的关系:是继承关系,只允许单继承,不允许多继承,但可以多层继承
- 类和接口的关系:是实现关系,可以单实现,也可以多实现,还可以继承一个类的同时实现多个接口
- 接口和接口的关系:是继承关系,可以单继承,也可以多继承,但底层的实现类需要重写该体系的所有抽象方法
15.适配器的介绍
- 当一个接口中抽象方法过多时,但我只要使用其中一部分的时候,就可以采用适配器的设计模式
- 书写步骤:
- 编写中间类XXXAdapter,实现对应的接口。对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重新需要的方法,为了避免其他类创建适配器的对象,中间的适配器类用abstrac进行修饰
16. 内部类的介绍
-
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
-
外部类不能直接访问内部类的成员,必须创建对象
public class Car {
private String carName;//车名 //内部类 class Engine{ String engineName;//引擎名字 public void show(){ suot(carName);直接调用 } } public void show(){ sout(engineName);错误不能直接调用 //必须需要创建对象 Engine e = new Engine(); sout(e.engineName);//通过对象来调用 }
}
-
成员内部类创建对象
public class Outer { private int a = 10; class Inner { private int a = 20; public void show(){ int a = 30; System.out.println(a);//30 System.out.println(this.a);//20 System.out.println(Outer.this.a);//10 } } public Inner getInstance(){ return new Inner(); } }
创建对象
Inner私有化时,创建对象 Outer o = new Outer(); Object oi = o.getInstance(); //Inner没有私有化时,创建对象 Outer.Inner oi = new Outer().new Inner();
-
静态内部类:创建对象的方式 Outer.Inner oi = new Outer.Inner()
-
局部内部类
- 将内部类定义在方法里面就叫做局部内部类,类型于方法里面的局部变量
- 外界是无法直接使用的,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
- 匿名内部类
- 创建 new 类名/接口名() { 重写方法 };
- 格式的细节:包含了继承或者实现,方法的重写,创建对象;整体就是一个类的子类对象或者接口的实现类对象
- 使用场景: (1) 当方法的参数是接口或者类时。 (2) 以接口为例,可以传递这个接口的实现类对象。 (3) 如果实现类只使用一次,就可以用匿名内部类简化代码。
17. BigInteger介绍
-
BigInteger bigInteger = new BigInteger(4,new Random());
sout(bigInteger) // 生成[0,2^4-1]的随机整数
-
BigInteger bigInteger = new BigInteger("1213213");参数里面只能是整数类型的字符串
- BigInteger bigInteger = new BigInteger("12323",进制);
- BigInteger bigInteger2 = BigInteger.valueOf(13213);范围和long的范围一样,内部有优化;对象一旦创建,内部数据就不会发生改变
-
常用方法:
- 加法:add(),eg: bi1.add(bi2)
- 减法:substract(),eg: bi1.substract(bi2)
- 乘法:multiply,eg:bi1.multiply(bi2)
- 商:divide(),eg:bi1.divide(bi2)
-
商和取余:public BigInteger[] divideAndRemainder(),eg: BigInteger[] arr = bi1.divideAndRemainder(bi2),
sout(arr[0]) //商 sout(arr[1]) //余数
-
较大/小值:max()/min()
18. BigDecimal介绍
- 用于精确计算小数
- 常用方法:
- 加法:add(),eg: bd1.add(bd2)
- 减法:substract(),eg: bd1.substract(bd2)
- 乘法:multiply,eg:bd1.multiply(bd2)
- 商:divide(),eg:bd1.divide(bd2)
19. 正则表达式(pattern)
- 方法:matches()
-
作用:
- 校验字符串是否满足要求
- 在一段文本中查找满足要求的内容
-
字符类(只能匹配一个字符):
- [abc] 只能是a,b或者c
- [^abc] 除了a,b,c之外的任何字符
- [a-zA-Z] a到z A到Z,包括(范围)
- [a-d[m-p]] a到d,或者m到p
- [a-z&&[def]] a-z和def的交集。为d,e,f
- [a-z&&[^bc]] a-z和非bc的交集(等同于[ad-z])
- [a-z&&[^mp]] a-z和非mp的交集(等同于[a-lq-z])
-
预定义字符(只能匹配一个字符)
- . 任意字符
- \d 表示任意一个数字[0-9]
- \D 非数字[^0-9]
- \s 一个空白字符[\t\n\x0B\f\r]
- \S 非空字符
- \w [a-zA-Z_0-9]英文、数字、下划线
- \W [^\w]
- (?i) 大小写都可以
-
数量词
- X? X,一次或者0次
- X* X,0次或者多次
- X+ X,一次或者多次
- X{n} X,正好n次
- X{n+,}X,至少n次
- X{n,m} X,至少n但不超过m次
-
爬虫
String str = "Java8十大慈善擦山东撒旦撒Java11"; //获取正则表达式的对象 Pattern pattern = Pattern.compile("Java\\d{1,2}"); //获取文本选择器的对象 Matcher m = pattern.matcher(str); while (m.find()){ String s = m.group(); System.out.println(s);//Java8 Java11 }
-
捕获分组
- 可以获取每组中的内容,反复使用
- 每组内部重复使用:\组号
-
每组外部重复使用:$组号
//捕获分组 //判断一个字符串的开始字符与结束字符是否一致? //举例:a123a 17891 a123b(false) String regex1 = "(.).+\\1"; // "\\1"把第一组数据在用一次 System.out.println("a123a".matches(regex1)); System.out.println("a123b".matches(regex1)); /* * 判断一个字符串的开始部分与结束部分是否一致?开始部分内部每个字符也需要一致 * 举例: aaa123aaa bbb34bb &&abc&& * .表示第一个字符任意 * \\2 拿出第二组数据再用一次 * *作用于\\2,表示出现0次或者多次 * 分组会以左括号为基准 eg:((.)\\2+).+\\1 (.)为第一组 * */ System.out.println("_________________"); String regex2 = "((.)\\2+).+\\1"; System.out.println("aaa1223aaa".matches(regex2)); System.out.println("aaa123bbb".matches(regex2)); String str = "我要学学编编编程程程"; final String s = str.replaceAll("(.)\\1+", "$1"); System.out.println(s);
结果:
true false _________________ true false 我要学编程
-
非捕获分组:(:?)(?=)(?!)都是非捕获分组
20. SimpleDateFormat介绍
-
日期格式化
默认格式
//默认格式 SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat(); final String format1 = simpleDateFormat1.format(new Date(0L)); System.out.println(format1);//70-1-1 上午8:00
格式化
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时:mm分:ss秒"); final String format = simpleDateFormat.format(new Date(0L)); System.out.println(format);//1970年01月01日 08时:00分:00秒
-
日期解析
//日期解析 String str = "2023-11-11 11:11:11"; SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date; try { date = simpleDateFormat2.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } final String format2 = simpleDateFormat2.format(date); System.out.println(format2);//2023-11-11 11:11:11
21. 排序算法
-
冒泡排序
public class BubbleSort { public static void main(String[] args) { int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } private static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { //n-1趟排序 boolean flag = false; for (int j = 0; j < arr.length - 1 - i; j++) { //每一趟进行n-1 - i次排序 if (arr[j] > arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; flag = true; } } if (!flag) return; } } }
-
选择排序
public class SelectSort { public static void main(String[] args) { int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; selectSort(arr); System.out.println(Arrays.toString(arr)); } private static void selectSort(int[] arr) { //思想:,每次都要拿第一个元素和后面元素比较 for (int i = 0; i < arr.length - 1; i++) {//比较的趟数 for (int j = i + 1; j < arr.length; j++) { //j 不能 < arr.length - 1,这样arr[arr.length-1]元素不能被比较 if (arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } }
-
插入排序
-
public class InsertSort { public static void main(String[] args) { int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; insertSort(arr); System.out.println(Arrays.toString(arr)); }
private static void insertSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int end = i - 1;//已经有序的最大下标 int insert = arr[i];//需要插入的元素 while (end >= 0 && insert < arr[end]){ arr[end+1] = arr[end]; end--; } //找到待插入元素的位置 arr[end+1] = insert; } }
-
快速排序
public class QuickSort1 { public static void main(String[] args) { int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; quickSort1(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } private static void quickSort1(int[] arr, int i, int j) { int start = i; int end = j; //递归出口 if (start > end) return; //基准数 int baseNumber = arr[i]; while (start != end) { //必须先从后往前找,否则就会排序错误 //end从后往前找 while (true) { if (start >= end || arr[end] < baseNumber) break; end--; } //从外前往后找 while (true) { if (start >= end || arr[start] > baseNumber) break; start++; } //end和start都找到对数以后,交换两个位置上的元素 int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } //找打基准数的位置,归位 int temp = arr[i]; arr[i] = arr[end]; arr[end] = temp; //把基准数的右边继续进行排序 quickSort1(arr,start+1,j); //把基准数的左边继续进行排序 quickSort1(arr,i,end-1); } }
四、集合
1. Arrays介绍
- Arrays.copyOf():int[] copyOf = Arrays.copyOf(被拷贝的数组, 拷贝数组)
- Arrays.toString():把数组转换为字符串[数组元素]
- Arrays.fill():Arrays.fill(填充数组,填充值)
- Arrays.sort():给数组排序采用快排,默认升序,需要降序需要重写sort()方法
-
Arrays.sort()的重写,底层采用插入+二分查找实现,o1-o2实现升序,o2-o1降序
Integer[] arr1= {6, 3, 2, 6, 8, 1}; Arrays.sort(arr1,new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); System.out.println(Arrays.toString(arr1));//[8, 6, 6, 3, 2, 1]
2. Collection介绍
- 是可以重复的、有序的,是所有单列集合的祖宗接口,它的功能是全部单列集合都可以继承的。不能创建对象,只能通过实现类创建对象,Collection coll = new ArrayList<>()
-
方法
- coll.add():添加元素,添加成功返回True,一定添加成功
- coll.remove():删除元素,删除成功返回True,删除失败返回False
- cool.clear():清空集合
- coll.contains():查看是否包含某个元素,包含返回True,不包含返回False,特别注意:contains方法底层是通过equals方法判断的,如果是自定义对象,一定要在JavaBean中重写equals方法
- coll.size():获取集合大小
-
Collection的遍历
-
通过迭代器遍历:迭代器的遍历是不依赖索引的,索引不存在索引越界,只会发生NoSuchElementException(没有这个元素异常)。迭代器遍历完以后,指针是不会复位的,想要再次遍历只能重新获取一个新的迭代器对象
public static void main(String[] args) { Collection coll = new ArrayList<>(); //添加元素 coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); //创建迭代器对象 Iterator<String> iterator = coll.iterator(); while (iterator.hasNext()){ //iterator.hasNext() 判断当前位置是否有元素,有返回True String s = iterator.next();//有两层意思:1.返回当前元素 2. 移动指针 System.out.println(s); }
-
3. ArrayList介绍
- ArrayList list = new ArrayList<>(),ArrayList大小是可变的,自动扩容; "< >"表示泛型只能为引用数据类型
- 增加:add() 返回类型为boolean
- 删除:remove("") 返回类型为boolean,remove(index)删除指定下标,返回值为被删除的元素
- 修改:set(index,"替换") 返回值为被修改的元素
- 查:get(index) 返回值为查找的元素
- ArrayList 大小size()
- 可以使用size()和get()实现遍历
4.红黑树
-
红黑树的数据结构规则:
- 每一个节点只能是红色或者黑色
- 根节点必须是黑色
- 如果一个节点没有父节点或者子节点,该节点相应的指针属性值为Nil,这些Nil视为叶子节点,每个叶子节点(Nil)是黑色的
- 如果某一个节点是红色的,那么它的子节点必须是黑色的(不能出现开两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点
-
添加的节点默认是红色的,添加效率更高
- 红黑树的增删改查效率比较
5.set系列集合
- 无序:存取顺序不一致
- 不重复:可以去重
- 无索引:没有带索引的方法,所有不能使用普通的for循环遍历,也不能通过索引来获取元素
-
set的实现类:
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TressSet:可排序、不重复、无索引
-
常用方法:
public class Set1 { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("张三"); set.add("张三");//返回false 不允许重复添加 set.add("王五"); set.add("李四"); System.out.println(set);//[李四, 张三, 王五] 存取是无序的 /*//迭代器遍历 Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }*/ //增强for循环遍历 /*for (String s : set) { System.out.println(s); }*/ //Lambda表达式 /*set.forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } });*/ set.forEach(s -> System.out.println(s) ); } }
6.HashSet介绍
-
HashSet底层原理:
- HashSet底层采用哈希表存取
- 哈希表是一种对于增删改查数据性能较好的数据结构
- 添加是的位置:int index = (数组长度-1)& 哈希值
-
哈希表的组成:
- JDK8以前:数组+链表
- JDK8开始:数组+链表+红黑树
-
哈希值
- 根据hashCode方法计算出来的的int类型的整数
- 该方法被定义在Object中,所有对象都可以调用,默认使用地址值来计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
-
对象的哈希值特点
- 如果没有重写hashCode方法,不同对象计算出来的哈希值是不同的
- 如果已经重写了hashCode方法,不同的对象只要属性值相同,计算出来的哈希值就是一样的
-
在小部分的情况下,不同属性值或者不同的地址值计算出来的哈希值也可能是一样的(哈希碰撞)
public class HashSet1 { public static void main(String[] args) { Student student1 = new Student("张三",20); Student student2 = new Student("张三",20); Student student3 = new Student("李四",22); /* //没有重写hashCode方法,不同对象计算出来的哈希值是不同的 System.out.println(student1.hashCode());//460141958 System.out.println(student2.hashCode());//1163157884 */ //重写hashCode方法以后,属性值相同计算出来的哈希值就是一样的 System.out.println(student1.hashCode());//24022540 System.out.println(student2.hashCode());//24022540 //哈希碰撞 System.out.println("abc".hashCode());//96354 System.out.println("acD".hashCode());//96354 } }
-
HashSet的去重
public class HasHSetTest { public static void main(String[] args) { Student student1 = new Student("张三",20); Student student2 = new Student("李四",21); Student student3 = new Student("王五",22); Student student4 = new Student("张三",20); HashSet<Student> hashSet = new HashSet<>(); System.out.println(hashSet.add(student1)); System.out.println(hashSet.add(student2)); System.out.println(hashSet.add(student3)); System.out.println(hashSet.add(student4));//false 实现去重 // 输出和存入时候顺序不一样 System.out.println(hashSet);//[Student{name = 张三, age = 20}, Student{name = 王五, age = 22}, Student{name = 李四, age = 21}] } }
7. linkedHashSet介绍
- 有序、不重复、无索引
- 这里的有序指的是保证存取的元素一致
-
原理:底层的数据结构依旧是哈希表,只是每一个元素又额外多了一个双链表的机制记录存储的顺序
public class LinkedHashSetTest { public static void main(String[] args) { Student student1 = new Student("张三",20); Student student2 = new Student("李四",21); Student student3 = new Student("王五",22); Student student4 = new Student("张三",20); LinkedHashSet<Student> linkedHashSet = new LinkedHashSet<>(); //添加元素 System.out.println(linkedHashSet.add(student1)); System.out.println(linkedHashSet.add(student2)); System.out.println(linkedHashSet.add(student3)); System.out.println(linkedHashSet.add(student4));//false 也能实现去重 // 输出的顺序和添加的一样 System.out.println(linkedHashSet);//[Student{name = 张三, age = 20}, Student{name = 李四, age = 21}, Student{name = 王五, age = 22}] } }
8. TreeSet
- 可排序、不重复、无索引
- 底层是红黑树
-
有序:
public class TreeSet1 { public static void main(String[] args) { TreeSet<Integer> treeSet = new TreeSet<>(); treeSet.add(2); treeSet.add(3); treeSet.add(4); treeSet.add(1); System.out.println(treeSet);//[1, 2, 3, 4] } }
-
对于数值类型:Integer和Double都是默认小到大排序
-
对于字符和字符串类型都是按照ASCLL表中升序排序
//让Student类实现Comparable接口并重写compareTo方法,去实现默认排序 public int compareTo(Student o) { //按照年龄的升序排序 return this.age - o.age; }
public class TreeSet1 {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<>();
Student student1 = new Student("Zhangsan",22);
Student student2 = new Student("Lisi",20);
Student student3 = new Student("Wangwu",21);
treeSet.add(student1);
treeSet.add(student2);
treeSet.add(student3);
System.out.println(treeSet);//[Student{name = Lisi, age = 20}, Student{name = Wangwu, age = 21}, Student{name = Zhangsan, age = 22}]
}
}
-
自定义Comparator比较器
public class TreeSet1 { public static void main(String[] args) {
Student student1 = new Student("Zhangsan",22,99); Student student2 = new Student("Lisi",22,98); Student student3 = new Student("Wangwu",20,80); TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int i = o1.getAge() - o2.getAge(); i = i==0? o1.getSum() - o2.getSum() : i; return i; } }); treeSet.add(student1); treeSet.add(student2); treeSet.add(student3); System.out.println(treeSet); //[Student{name = Wangwu, age = 20,sum =80}, Student{name = Lisi, age = 22,sum =98}, Student{name = Zhangsan, age = 22,sum =99}] } }