常识
java三大平台:JavaSE(基础)、JavaME(小型)、JavaEE(老大)
JVM(Java Virtual Machine),Java虚拟机
JRE(Java Runtime Environment),Java运行环境
JDK(Java Development Kit)称为Java开发工具
技巧
所有的技术都是为了解决问题出现的
创建包不能以数字开头可以用 a01 a02.
右键上方的.java文件 向右拆分可以同时看两个代码,比较方便
相对路径:要从项目名称开始
生成随机数
//生成随机数
Random r=new Random();
int number=r.nextInt(10);//0-9
System.out.println(number);
占位符%s
//占位符
System.out.printf("%s你好啊%s","张三","李四");
字符'1'-'0'就是数字1
System.out.print(arr[str1.charAt(i)-'0']);
loop:while break loop; 可以指定跳出哪一个循环
基础
数据类型
-
基本数据类型
-
引用数据类型
四类八种
注意!后缀
//4.定义long类型的变量
long d = 123456789123456789L;
System.out.println(d);
//5.定义float类型的变量
float e = 10.1F;
System.out.println(e);
当long也存不下的时候用 BigInteger
在使用float或者double类型的数据在进行数学运算的时候,很有可能会产生精度丢失问题
解决方法:BigDecimal
命名规则
小驼峰:变量名、方法名
大驼峰:类名
全小写:包名
ACSCII码表
权限修饰符
编写代码时,如果没有特殊的考虑,建议这样使用权限:
-
成员变量使用
private
,隐藏细节。 -
构造方法使用
public
,方便创建对象。 -
成员方法使用
public
,方便调用方法。
面向对象
对象内存图
成员方法调用过程
标准对象创建要求
ptg:下载ptg插件 右键Ptg To JavaBean 可一键生成标准JavaBean
后期可以用Lombok注解简化代码。
快捷键
alt+insert:插入一些构建类的常用方法
鼠标滚轮竖着选数字,能全部一起修改 或者 alt+左键
ctrl+shift+u:把字符串全部变大写 在写常量(全部大写)的时候用到
this:区分局部变量和成员变量 允许重名 就近原则
ctrl+alt+m:创建方法(method)
ctrl+D:复制上一行
psvm sout fori:快捷生成代码
格式化代码:Ctrl+Alt+L
ctrl+alt+左键:回到刚刚看的代码
ctrl+alt+v:自动生成左边的变量名
ctrl+alt+t:选择用什么包围
shift+alt+f10:编译代码
ctrl+o:查看可重写的方法
alt+insert:可以重写equall和hashcode方法
局部注释:ctrl+shift+/
ctrl+H:按住一个接口之后可以查看所有实现这个接口的类
创建测试类:alt+enter
ctrl+shif+end:全选光标后面的文字 再按住shift不放可以去掉尾部
static变量
能共享就用,如老师名字 被该类所有的对象共享
static的注意事项:
静态方法中,只能访问静态
非静态方法可以访问所有
静态方法中没有this关键字
因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量
工具类
类名一般以Util结尾
public class ArrayUtil {
private ArrayUtil(){
}
public static String printArray(int[] arr){
StringBuilder sb=new StringBuilder();
sb.append('[');
for (int i = 0; i < arr.length; i++) {
if (i==arr.length-1){
sb.append(arr[i]);
}else {
sb.append(arr[i]).append(',');
}
}
sb.append(']');
return sb.toString();
}
public static double getAverage(double[] arr){
double sum=0;
for (double v : arr) {
sum += v;
}
return sum/arr.length;
}
}
//
public class TestDemo {
public static void main(String[] args) {
int[] arr1={10,20,50,34,100};
String str=ArrayUtil.printArray(arr1);
System.out.println(str);
double[] arr2={1.5,3.7,4.9,5.8,6.6};
double avg=ArrayUtil.getAverage(arr2);
System.out.println(avg);
}
}
main方法
封装
对象代表什么,就得封装对应的数据,并提供数据对应的行为
继承
super代表的是父类对象的引用,this代表的是当前对象的引用
当类与类之间,存在共同(相同)的内容,并满足子类是父类的一种就能用。
构造方法:子类、父类名字不一样,继承不了。
成员变量:可以继承,不能直接使用 得用get set
成员变量的访问特点:就近原则 有this访问本类的成员变量 super父类
重写
当父类的方法不满足子类需求 @Override
多态
多态: 是指同一行为,具有多个不同表现形式。
例子:Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。
父类类型 对象名称=new 子类对象
使用父类类型作为参数,可以接受所有子类对象 register(Person p)
口诀:
解释:
原因:
举例来说:如果 Dog
类继承了 Animal
类,并且 Animal
类中有一个 makeSound()
方法,而 Dog
类中重写了这个方法,那么通过 a
引用变量调用 makeSound()
方法时,将会调用 Dog
类中的 makeSound()
方法,而不是 Animal
类中的。
多态的弊端:不能调用子类的特有功能 因为编译看左边,父类中没有子类的特有方法,所以就报错 解决方法:强制类型转换
instanceof
作用是测试它左边的对象是否是它右边的类的实例
场景:当我在看别人的代码时,不知道要强转成什么类型
包
常量final
静态代码块
用于数据初始化
匿名内部类
作用:
如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用
是为了简化代码。
格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
//实现
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 使用匿名内部类
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
}
}
抽象类
强制子类必须按照父类定义的格式来重写
是什么:父类只需要提供一个没有方法体的定义即可,具体实现交给子类自己去实现。
抽象类存在的意义是为了被子类继承。
例子
// 父类,抽象类
abstract class Employee {
private String id;
private String name;
private double salary;
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
// 抽象方法
// 抽象方法必须要放在抽象类中
abstract public void work();
}
// 定义一个子类继承抽象类
class Manager extends Employee {
public Manager() {
}
public Manager(String id, String name, double salary) {
super(id, name, salary);
}
// 2.重写父类的抽象方法
@Override
public void work() {
System.out.println("管理其他人");
}
}
// 定义一个子类继承抽象类
class Cook extends Employee {
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
@Override
public void work() {
System.out.println("厨师炒菜多加点盐...");
}
}
// 测试类
public class Demo10 {
public static void main(String[] args) {
// 创建抽象类,抽象类不能创建对象
// 假设抽象类让我们创建对象,里面的抽象方法没有方法体,无法执行.所以不让我们创建对象
// Employee e = new Employee();
// e.work();
// 3.创建子类
Manager m = new Manager();
m.work();
Cook c = new Cook("ap002", "库克", 1);
c.work();
}
}
接口
接口是更加彻底的抽象,接口中全部是抽象方法。
定义一种规则,使得代码统一
public interface SportMan {
void run(); // 抽象方法,跑步。
void law(); // 抽象方法,遵守法律。
String compittion(String project); // 抽象方法,比赛。
}
设计模式
就是解决问题的各种套路
字符串
自动生成左边 输入sc.next() 按alt+enter
字符串的比较
==号的作用
-
比较基本数据类型:比较的是具体的值
-
比较引用数据类型:比较的是对象地址值
StringBuilder
StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的
作用:拼接字符串和反转字符串等
集合
泛型
-
泛型的使用
用于约束集合中存储元素的数据类型(像一个看门老大爷,符合才能进来)
-
只支持引用数据类型
-
泛型类、泛型方法、泛型接口
-
泛型不具备继承性,数据数据可以继承
通配符 ?
ArrayList常用方法
Collections类是Java中针对集合类的一个工具类,其中提供一系列静态方法:
sort、reverse、fill等
数组的长度固定,集合的长度可变
ArrayList<String> list=new ArrayList<>();
Collection
set集合不能用普通for来遍历
遍历方式:
迭代器
增强for
lambda表达式
双列集合:Map
所有的单列集合(List,Set)和数组才能用增强for进行遍历。
List
List特有方法介绍
方法名 | 描述 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
ArrayList
底层是数组结构实现,查询快、增删慢
扩容时机一:
-
当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15.再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。
扩容时机二:
-
一次性添加多个数据,扩容1.5倍不够,怎么办呀?
如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。
举个例子: 在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够,
怎么办?
此时新数组的长度,就以实际情况为准,就是110
LinkedList
底层是双向链表结构实现,查询慢、增删快
核心步骤如下:
-
刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null
-
添加第一个元素时,底层创建一个结点对象,first和last都记录这个结点的地址值
-
添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值Set
Set
-
不可以存储重复元素
-
没有索引,不能使用普通for循环遍历
HashSet
哈希表结构【理解】
-
JDK1.8以后
-
节点个数少于等于8个
数组 + 链表
-
节点个数多于8个
数组 + 红黑树
-
hashCode:比较哈希值
equals:比较属性值
-
总结
HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
LinkedHashSet
TreeSet
-
自然排序、比较器排序 两种比较方式小结
-
自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
-
比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
-
在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
-
使用场景
Map
-
Map集合的特点
-
双列集合,一个键对应一个值(Entry对象)
-
键不可以重复,值可以重复
-
方法介绍
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
put注意:
Map集合的获取功能 方法介绍:
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V> values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
1.键找值
-
获取所有键的集合。用keySet()方法实现
//获取所有键的集合。用keySet()方法实现
Set<String> keySet = map.keySet();
//遍历键的集合,获取到每一个键。用增强for实现
for (String key : keySet) {
//根据键去找值。用get(Object key)方法实现
String value = map.get(key);
System.out.println(key + "," + value);
}
2.键值对
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
3.Lambda表达式
HashMap
-
HashMap底层是哈希表结构的
-
依赖hashCode方法和equals方法保证键的唯一
-
如果键要存储的是自定义对象,需要重写hashCode和equals方法
LinkHashMap
TreeMap
可变参数 ...
public class ChangeArgs {
public static void main(String[] args) {
int sum = getSum(6, 7, 2, 12, 2121);
System.out.println(sum);
}
public static int getSum(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
}
注意:
1.一个方法只能有一个可变参数
2.如果方法中有多个参数,可变参数要放到最后
Collections
集合工具类
java.utils.Collections
是集合工具类,用来对集合进行操作。
常用方法如下:
-
排序
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void swap(List list, int i , int j)//交换两个索引位置的元素
-
查找、替换
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素
排序
不可变集合
.of
什么东西:不想让别人修改集合中的内容
List.of()
、Set.of()
、Map.of()
和 Map.ofEntries()
等工厂方法来创建不可变集合
数组
Arrays类
JDK提供的java.util.Arrays类,包含了常用的数组操作,方便我们日常开发。
Arrays类包含了:排序、查找、填充、打印内容等常见的操作。
常见操作:
1、Arrays.toString()打印数组
2、Arrays.equals(int[] a, int[] a2)比较两个数组是否相同
3、Arrays.copyOf(int[] original, int newLength)复制指定的数组 ---效率低,会重新开辟新的数组空间original - 要复制的数组;newLength - 要返回的副本的长度 左闭右开
4、Arrays.fill(int[] a, int val)/Arrays.fill(int[] a, int fromIndex, int toIndex, int val)填充数组
5、Arrays.sort(int[] a);对数组进行升序排序
6、Arrays.binarySearch(int[] a, int key)二分法查找 有序升序
该方法是将数组转化成List集合的方法。
List<String> list = Arrays.asList("a","b","c");
用此方法得到的List的长度是不可改变的
API
Java.lang.System类Java.lang.System 类 (w3ccoo.com)
exit:停止虚拟机
currentTimeMills:获取当前时间的毫秒值
arraycopy:拷贝数组 参数(数据源,从数据源的第几个开始,拷贝到哪,目的地的索引,个数)
正则表达式
下载一个插件any-rule 右键就会有模板
忽略大小写
String str="Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
Pattern p=Pattern.compile("Java\\d{0,2}");
Matcher m=p.matcher(str);
while (m.find()){
String s=m.group();
System.out.println(s);
}
爬身份证号码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo7 {
public static void main(String[] args) throws IOException {
/* 扩展需求2:
把连接:https://m.sengzan.com/jiaoyu/29104.html?ivk sa=1025883i
中所有的身份证号码都爬取出来。
*/
//创建一个URL对象
URL url = new URL("https://m.sengzan.com/jiaoyu/29104.html?ivk sa=1025883i");
//连接上这个网址
//细节:保证网络是畅通
URLConnection conn = url.openConnection();//创建一个对象去读取网络中的数据
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
//获取正则表达式的对象pattern
String regex = "[1-9]\\d{17}";
Pattern pattern = Pattern.compile(regex);//在读取的时候每次读一整行
while ((line = br.readLine()) != null) {
//拿着文本匹配器的对象matcher按照pattern的规则去读取当前的这一行信息
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
br.close();
}
}
时间
data类
SimpleDateFormat类
Calendar类
判断闰年:
包装类
作用:很多情况,会创建对象使用,因为对象可以做更多的功能 ,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类
public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型
除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
-
装箱:从基本类型转换为对应的包装类对象。String s2 = String.valueOf(number)
-
拆箱:从包装类对象转换为对应的基本类型。int y = Integer.parseInt(s);
lambda表达式
stream
三类方法:获取Stream流--中间方法--终结方法
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list1=new ArrayList<>();
list1.add("张lzy");
list1.add("l");
list1.add("张zy");
list1.add("y");
list1.add("hq");
list1.stream().filter(name->name.startsWith("张")).filter(name->name.length()==3).forEach(System.out::println);
}
}
获取stream流
新加:map 映射
ArrayList<String> list = new ArrayList<>();
list.add("张三丰-15");
list.add("张无忌-90");
list.stream().map(s -> s.split("-")[1]).forEach(System.out::println);
方法引用
::方法引用符
//对比
//Lambda简化写法
usePrintable(s -> System.out.println(s));
//方法引用
usePrintable(System.out::println);
异常
概况
概念
编译时异常和运行时异常
Throwable中的常用方法:
-
public void printStackTrace()
:打印异常的详细信息。
异常的作用
try灵魂四问
//异常常用输出
e.printStackTrace();
自定义异常
目的:为了让控制台的报错信息更加的见名之意
文件
listFiles():获取该路径的所有内容
IO流
相对于程序 读入读出 内存是内往外是出
输出流:数据读入文件
输入流:数据从文件读出来
为什么要用IO:File类只能对文件本身进行操作,不能读写文件里面存储的数据。
IO流的分类
为什么使用字符流?
答:当字节流遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件
缓冲流:能够高效读写
缓冲流加快速度的原理?
答:缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率
转换流:能够转换编码的 是字符流和字节流之间的桥梁
序列化流:能够持久化存储对象
FileOutputStream
//把字符串转换为 byte[]类型
String str="lzyhaoshuai";
byte[] bWrite = str.getBytes();
//元素写出要是字符型 在后面+一个""
for (int i = 0; i < list.size(); i++) {
if(i==list.size()-1){
fw.write(list.get(i)+"");
}else {
fw.write(list.get(i)+"-");
}
}
两个好用工具包:Commons-io hutool 概述 (hutool.cn)
多线程
线程相关
-
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
同一时间执行多个线程,提升性能。
并发:在同一时刻,有多个指令在单个CPU上交替执行。
并行:在同一时刻,有多个指令在多个CPU上同时执行。
进程:是正在运行的程序
线程:是进程中的单个顺序控制流,是一条执行路径
Java 提供了三种创建线程的方法:
- 继承 Thread 类本身
- 实现 Runnable 接口
- 实现 Callable 接口。
Thread.currentThread().getName() 返回对当前正在执行的线程对象的引用
t2.setDaemon(true); 当普通线程执行完之后,那么守护线程也没有继续运行下去的必要了.
synchronized:锁
死锁:两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态
//同步方法
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
线程调度
-
两种调度方式
-
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
-
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
-
-
Java使用的是抢占式调度模型
阻塞队列
线程池
核心参数详解
public ThreadPoolExecutor( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime: 空闲线程最大存活时间,不能小于0
unit: 时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂,不能为null
handler: 任务的拒绝策略,不能为null
//创建线程池的两种方法
//创建一个默认的线程池
static ExecutorService newCachedThreadPool()
//创建一个指定最多线程数量的线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
核心线程数量,
最大线程数量,
空闲线程最大存活时间,
任务队列,
创建线程工厂,
任务的拒绝策略);
上下文切换
当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。
资料文件夹中有面试常考。juc额外扩展资料
JUC是java.util.concurrent包的简称,在Java5.0添加,目的就是为了更好的支持高并发任务
网络编程
C/S:客户端 服务器
B/S:浏览器 服务器
反射
通过反射可以获取任意一个类的所有属性和方法。
第一种forName最常用
动态代理
无侵入式的给方法增强功能(类似后面的spingboot AOP)