Path环境变量的作用
介绍:path环境变量用于记住程序路径,方便在命令行窗口的任意目录启动程序。
环境变量中包括用户变量和系统变量,用户变量中设置path变量路径只能该登录用户使用,而系统变量设置path变量路径后任何登录的用户都可使用。
另需注意:需配置JAVA_HOME环境变量,JAVA_HOME环境变量告诉操作系统jdk安装在了哪个位置(将来其他技术要通过这个环境变量找JDK)
特殊空格关键字
制表符'\t'
注意:long类型的变量定义时需要在最后端加上一个大写的L,例:
long money=999999999L;
float类型的变量定义时也需要在最后端加上一个后缀,F,例:
float course=20.2F;
idea工具开发学习
基础步骤
idea设置主题字体
idea常用快捷键
数字或字符.sout可以快捷输出打印语句
ctrl+p 展示括号内的参数
alt + enter 快捷创建方法
ctrl + alt + V 自动生成等式左边内容 eg: new Person(); 点击快捷键后自动生成左边内容
算数运算符
整数的算数运算只能得到整数,小数的算数运算有可能不精确,具体到SE进阶详解
System.out.println(10/2); //5
System.out.println(10/3); //3
System.out.println(10.0/3); //3.333333335
拆分个位十位百位
Scanner sc=new Scanner(System.in);
System.out.println("输入三位数");
int x=sc.nextInt();
int ge=x%10;
int shi = x/10%10; //十位/10%10
int bai = x/100%10; //百位/100%10 千位/1000%10 万位以此类推
System.out.println(ge);
System.out.println(shi);
System.out.println(bai);
算数运算符-----数字相加
隐式转换:小转大,取值范围小的自动转换为取值范围大的
强制转换:大转小,取值范围大的强制转为取值范围小的
字符串的 + 操作
只要是与字符串相加或者字符串与其相加的都会产生一个新的字符串
逻辑运算符
短路逻辑运算符
三元运算符
流程控制语句
判断和循环之判断
if和if else
int score=90;
//if....else会逐个判断if内的条件运算,若都不满足会执行else内的语句
if(score>=90)System.out.println("Good");//满足条件但依然会往下继续判定
if(score<90 && score>=80)System.out.println("Better");//依然执行判断
if(score<80)System.out.println("Bad");
//if...else if...else...备胎式判定,若if条件不满足会进入到else if判定,若else if条件满足则跳出
所有判定
if(score>=100)System.out.println("Good");//不满足条件,向下继续判定
else if(score<100 && score>=90)System.out.println("Better");//满足条件,判断终止
else if(score<90)System.out.println("Bad");
switch判断
Switch渗透
Switch渗透,
switch(2){
case 1:
case 2: System.out.println("GOOD");//这里1和2都会打印GOOD,没有break就会向下渗透
break;
default: System.out.println("Bad");
break;
}
Switch新写法:省略了break和":",可以直接定义变量进行赋值
switch(2){
case 1 -> System.out.println("GOOD");
case 2 -> System.out.println("GOOD");
default-> System.out.println("Bad");
}
String str=switch (i){
case '0'->"";
case '1'->"I";
case '2'->"II";
case '3'->"III";
case '4'->"IV";
case '5'->"V";
case '6'->"VI";
case '7'->"VII";
case '8'->"VIII";
case '9'->"IX";
default -> "";
};
判断和循环之循环
循环中的continue和break
Random随机数的使用
获取一个指定范围的随机数:
1.区间数两端-区间内最小数 获得一个新的区间
2.尾巴+1 得到的数为r.nextInt(X)括号内的数
3.再加上第一步减去的数,得到的数便是指定范围的随机数 r.nextInt(X)+Y
eg:随机获得55-79之间的随机数:
Random r=new Random();
//(55~79)-55 ->(0~24)
//24+1=25
//(25)+55
int i=r.nextInt(25)+55;
得到的就是55-79之间的随机数
数组
数组的定义
java内存分配
栈内存中存放的是定义的变量以及定义的数组,当数组定义时如int[] a={1,2,3};此时栈内存中存放的是堆内存中数组对应的地址
方法
方法的重载
return和break的区别:
return:其实跟循环没有什么关系,跟方法有关,表示结束方法,返回结果
如果方法执行到了return,那么整个方法全部结束,里面的循环自然也随之结束。
break:其实跟方法没有什么关系,只是结束循环或者swith
给循环体起名字
格式为: 名字:循环体
循环体有了名字后break 循环体名字,break可以跳出指定名字的循环体,如下例子跳出名字为loop的while循环体。
system.exit(0);
停止虚拟机运行,虚拟机运行停止,所有的循环代码自然也停止执行。
基本数据类型和引用数据类型
基本数据类型:变量中存储的是真实的数据
引用数据类型:new出来的都是引用数据类型,引用数据类型的变量中存储的是地址值,引用指的是使用了其他空间的数据。
快捷键自动抽取方法:Ctrl+Alt+M
自动抽取重复部分作为一个方法
快捷键自动生成循环:Ctrl+Alt+T
选择对应的代码部分按ctrl+Alt+T 可以在最外层嵌套if while for try...catch等循环
快捷键替换:
本类替换:Ctrl + R 本类中替换,当前文件内替换。
全局替换:Ctrl + Shift + R 全局替换。
二维数组:
静态二维数组:
二维数组中的每个元素是一维数组,array[i][j] 指的是第i个数组元素中的第j个元素。
int[][] array={{1,2,3,0},{11,12,13,14,15,16}};
System.out.println(array[0][3]);//0
System.out.println(array[1][5]);//16
System.out.println("----------------------------");
for (int i = 0; i < array.length; i++) {
for (int m = 0; m < array[i].length; m++) {
System.out.println(array[i][m]);
}
}
二维数组遍历:
方法一:上图
方法二:
int[][] a=new int[x][y];
for(int i=0;i<a.length();i++){
sout(a[i/y][i%y]); //需是二维数组的列数
}
动态二维数组:
二维数组内存图:
arr指的是栈内存中arr对应在堆内存中的地址 ,arr[0]指的是二维数组中第一个数组元素在堆内存中的地址,arr[0][0]指的是二维数组中第一个数组中第0个元素。
特殊情况:
自己创建一维数组后手动赋值给二维数组
动态初始化里自动分配地址后可以再手动把一维数组赋值给二维数组,此时会把原来的二维数组地址覆盖。
面向对象
设计对象并使用
JavaBean类和测试类的区别
对象的成员变量默认值规则:
封装:
private关键字:
封装思想的应用:
我们可以把一些零散的数据封装成一个对象
以后传递参数的时候只传递一个整体就够了,不用管这些零散的数据。
this关键字:
this指的是方法的调用者的地址值,例如:
this.getName() 指的是main方法中调用attack方法的对象他的Name值。
public void attack(GemeUser gemeUser){
Random r=new Random();
//每次攻击造成1-20随机伤害
int hurt=r.nextInt(20)+1;
int remainBlood=gemeUser.getBlood()-hurt;
remainBlood=remainBlood<0?0:remainBlood;
gemeUser.setBlood(remainBlood);
System.out.println(this.getName()+"打了"+gemeUser.getName()+"一下造成了"+hurt+"伤害,"
+gemeUser.getName()+"还剩下"+gemeUser.getBlood()+"血。");
}
局部变量和成员变量:
当局部变量和成员变量重名时,this.变量 指的是成员变量,当方法中未用this指定时,采用就近原则,离哪个变量近使用哪个。
构造方法:
作用:在创建对象的时候给成员变量赋值的,给成员变量进行初始化。
格式:
执行时机:
1. 创建对象的时候由虚拟机调用,不能手动调用构造方法。
2. 每创建一次对象就会调用一次构造方法。
注意事项:
快速生成javaBean快捷键以及插件
对象的内存图
键盘录入:
普通录入:如果遇到空格、制表符或者回车就停止接收2,符号后的数据默认被后面的录入接收
如:123 456 空格后的456被num2自动接收
nexLine()录入:可以接收空格制表符,遇到回车才停止接收数据
注意:两种录入方法不能混用,否则next()录入方式后的回车或者空格会被nextLine()方法接收到以至于接收不到想要的数据。
字符串:
比较字符串:
String a="a";
String b="b";
a.compareTo(b);//按照字母顺序比较两者大小,a>b=1;a<b=-1;
根据字符数组和字节数组再创建一个新的字符串对象
创建String对象的两种方式:
String在内存中的内存模型:
1.直接赋值的方式:
字符串会在堆内存中开辟出来一片区域用作串池
2.new 出来的方式:每new一次都会开辟出来新的地址空间,地址不会复用
常用方法:比较
字符串的比较:
字符串的遍历:
字符串变为char[] 数组:String str=""; str.toCharArray();
根据索引返回字符(charAt(int index))
字符串截取:
敏感值替换:
StringBuilder概述:
StringBuilder构造方法:
StringBuilder常用方法:
StringBuilder还有一个insert()方法,即
StringBuilder sb=new StringBuilder();
sb.insert(int index,int i);每次插入从index下标开始插入,若从0下标开始插入,将会实现反转的效果。
链式编程:当我们在调用一个方法的时候不需要用变量接收它的结果,可以继续调用别的方法。
依赖前一个方法的结果再去调用别的方法
StringJoiner概述
和StringBuilder一样也是一个容器,只不过容器内的内容是可变的
构造方法:
StringJoiner的成员方法:
字符串原理小结:
集合:
特点:自动扩容
集合和数组的对比
创建集合对象:
ArrayList成员方法:
基本数据类型对应的包装类:
面向对象进阶
static静态:
静态变量:
static内存,堆内存中会有一个单独存放静态变量的存储区,叫做静态区。
静态变量随着类的加载而加载,优先于对象出现。
堆内存中对象如果想调用静态变量,则会直接在堆内存中调用静态区的静态变量。
变量是否可以被静态修饰主要看两个字,共享。
静态方法:
工具类
创建工具类时要求:
1.类名要见名知意
//处理数组的工具类
public class ArrUntil{}
2.同时要私有化构造方法。
私有化构造方法是为了防止创建对象,因为工具类是帮助我们做一些事情的,不需要用来创建对象
//处理数组的工具类
public class ArrUntil{
private ArrUntil(){}
}
3.方法定义为静态的方便调用。
//处理数组的工具类
public class ArrUntil{
private ArrUntil(){}
//获取最大值
public static int getMax(){}
}
static的注意事项:
![](https://i-blog.csdnimg.cn/blog_migrate/fd50fffb798a7f4aeaaf32a85df233e6.png)
继承
什么是继承以及继承的好处:
什么时候用继承:
当类与类之间,存在相同的内容并且满足子类是父类的一种,就可以考虑使用继承来优化代码。
继承小结:
继承特点:
Java继承不支持多继承,但支持多层继承
子类能继承父类中的哪些内容:
构造方法:非私有和私有的都不能继承
成员变量:非私有和私有的成员变量都可以被继承,但是私有的成员变量不可以直接被使用
成员方法:私有的成员方法不能被继承,但非私有的成员的方法能被继承
成员方法是否可以被继承:
父类在创建时会产生一个虚方法表(表中只能存放非privete,非static,非final类型的方法),子类在继承父类时会继承父类的虚方法表,同时也会在其基础上添加自己类中的虚方法。
注意:只有父类的虚方法才能被子类继承。
这时在创建A a=new A()对象且调用 a.方法c(); 时可以直接在A类的虚方法表中查看而不需要一层一层向上寻找。
继承中成员变量的访问特点:
super.变量名 指的是在父类中寻找该变量
如果变量名在本类中没有在父类中有,则会一级一级的找直到找到为止。
继承中成员方法的访问同变量访问一致,同为就近原则。
方法的重写:
重写的本质:子类覆盖了从父类继承过来虚方法表中的方法。
方法重写注意事项和要求:
继承中:构造方法的访问特点
super,this关键字
多态
同类型的对象表现出的不同形态。
多态调用成员的特点:
多态调用内存图解:
Test运行解读:
首先加载字节码文件,加载Test,Dog,Animal的字节码文件到方法区,加载字节码文件有一个默认的规则,先加载父类字节码文件再加载子类字节码文件,如果有非final,private,static的方法会先加载到虚方法表中,子类会覆盖虚方法表中父类的方法。
其次栈内存会开辟一段小空间记录变量a,类型为Animal类型,然后等号右边new出来的对象会到堆内存中开辟空间并分别记录父类的变量和子类的变量。
之后调用了sout(a.name);这时会从栈内存中变量a的地址找到堆内存中具体的值,若a是父类类型,会找到堆内存中父类name的变量及赋值带回,若a是子类类型,会到堆内存中找到子类的name变量及所赋予的值带回,这里a是Animal类型,及从堆中带回的值是父类型。
至此多态调用变量编译看左边运行看左边解读完毕。
理解:
Animal a=new Dog();
调用成员变量时:编译看左边,运行看左边
意思为,javac在编译时会看左边父类中是否有这个变量,没有就会报错
java代码运行时,会获取左边父类中成员变量的值。
调用成员方法时:编译看左边,运行看右边。
意思为,javac编译时会看左边父类中有没有这个方法,没有的话编译失败,有的话编译成功
Java代码运行时,首先方法区会加载object类,接着加载父类,再加载子类,此时虚表中记录的是子类已经重写后的方法。之后再运行的是虚表中重写后的子类的方法,所以运行看右边。
总结:因为多态调用方法时都是重写后子类的方法,而重写又是继承的特性,所以多态的前提是有继承/实现关系,有重写。
判断类的类型关键字:判断,类类型,instanceof
if(a instanceof Dog) 判断a类是不是狗类型。结果为true或false
以上判断太麻烦JDK14出了一个新特性:
if(a instanceof Dog d) 判断a类是不是Dog类型,如果是,转换之后变量名为d。如果不是,则不强转,结果直接是false.
多态的优势、多态的弊端、多态的强转:
包、final、权限修饰符、代码块
包:
final关键字:
意思为最终的,被final修饰的就为不可改变的
常量:
权限修饰符:
代码块:
1.局部代码块
2.构造代码块
构造代码块不够灵活
3.静态代码块(重点掌握)
应用场景:需要数据初始化时且只希望加载一次的时候,可以使用静态代码块。
抽象类和抽象方法
抽象方法:
抽象类和抽象方法的定义格式:
抽象类和抽象方法的注意事项:
抽象类中构造方法的作用,在创建子类时给属性来进行赋值。
总结:
接口:
为什么要有接口:
接口可以理解为是一种规则,当我们需要给多个类同时定义规则的时候就需要用到接口了,继承接口的类必须要遵守这种规则 。
抽象类和接口的异同:
抽象类是一种事物,而接口是一种规则,是对行为的抽象,其实现接口的类必须遵守这种规定。
接口的定义和使用:
接口中成员的特点:
成员变量只能是常量,默认修饰符为public static final 。也好理解,因为接口是一种规则,所以变量是不能轻易改变的。
接口和类之间的关系:
当一个类在同时实现多个接口且接口内有相同名字方法时,重名的方法只会重写一个。
接口也可以继承接口,且可以继承多个接口,当接口1继承了接口2,接口3,此时若一个类在实现接口1时,会重写接口1继承的所有接口的方法。
JDK8以后接口中新增的方法:
接口中的默认方法:
作用:解决接口升级问题
接口中的静态方法:
JDK9中接口新增的方法:
接口的私有方法:
默认的私有方法private void show(){}是给默认的接口方法服务的。
而静态的私有方法private static void show(){}只能静态的接口方法public static void show(){}来调用。
接口的应用:
接口多态:
适配器设计模式:
当规则接口中定义了多个方法而我们的类中只用到个别方法时,如果实现次接口将接口内全部方法重写显然有些冗余,这时我们可以运用适配器设计模式,即定义一个中间类来实现此接口并空重写接口中的所有的方法,然后我们在实际用到的类中来继承中间类并重写我们要用到的方法。
内部类:
什么是内部类:
为什么要学习内部类:
内部类访问特点:
1.内部类可以直接访问外部类的成员,包括私有的。
2.外部类要访问内部类的成员必须创建对象。
总结:
内部类的分类:
1.成员内部类(了解)
2.静态内部类(了解)
3.局部内部类(了解)
4.匿名内部类(掌握)
成员内部类
获取成员内部类对象:
方法2是以私有的成员内部类为例子
案例:当内部类中方法的变量和内部类中的成员变量还有外部类的成员变量重名时怎么区分:
直接打印变量名是就近原则打印内部类方法中的本方法变量。
this.变量名:调用的是内部类本类的成员变量。
外部类.this.变量名:调用的是外部类中的变量。
切换JDK版本来测试项目:
当内部类变量和外部类变量同名时调用方法:
调用内部类本类的成员变量用this.变量名;
调用外部类的成员变量用外部类名.this.变量名。
静态内部类
局部内部类
匿名内部类
匿名内部类解读:上图这个整体不是类,是new出来的一个对象,该对象实现/继承了接口/父类,{}大括号内部为类体,且需要重写接口/父类中的方法,Swim()是接口/父类名,new 对象实际上是new{} new大括号内的类。
整体可以理解为Swim接口的实现类对象,可赋值给Swim s=;实现接口多态
匿名内部类是new出来的一个类,且该类实现/继承 了 接口/父类 ,可以直接通过.方法名,来调用子类或父类的方法,只不过调用虚方法表中的方法时是重写后的方法。
总结:
多学一招:
反编译: cmd打开输入 javap 字节码文件名.class
GUI拼图小游戏项目
组件:
创建主页面1
javax.swing包中有一个JFrame类,这个类是javaBean类,就是用来描述界面的;
JFrame gameJframe=new JFrame();
gameJframe.setSize(int width,int height); //setSize()方法是用来设置窗体大小的,width是长,height是高。
gameJframe.setVisible(true/false);//用来控制窗体是否显示的,TRUE为显示,FALSE为不显示
成果:
2.将ui主页面分化
分化为注册、登录、游戏主页面几大模块
src包中创建一个类作为程序的启动入口
在各个页面中创建构造方法来初始化创造页面;
设置游戏的页面配置,置顶,标题,页面居中,游戏关闭程序停止
菜单的制作
注意: 构造函数设计思想,如果构造函数中代码太多,可以将其中的代码ctrl+alt+M,封装成一个方法,然后构造函数中调用方法即可。
添加图片:
ImageIcon对象放入图片,再把对象放到JLabel对象管理区域
JLabel不仅能添加图片,还可以添加文字。
添加文字:
ImageIcon对象的构造函数可以传入string 类型的文件地址
此时图片被添加到了界面中且默认居中显示。若不想居中显示用坐标点的形式来显示的话,需要把窗体默认居中的设置给关了,即:
在JFrame主页面中取消默认居中:
添加图片引用了坐标的概念(JLablel)
坐标以左上角为原点,往右是X轴,往下是Y轴,而图片的左上角的点是图片在坐标中位置的基点。
事件(让图片进行移动)
事件是可以被组件识别的操作
当你对组件干了某件事之后,就会执行对应的代码
动作监听只包括鼠标点击和空格
按钮实例展示
创建按钮对象
JButton jbt=new JButton("按钮名称");
设置按钮的长宽位置
jbt.setBounds(x,y,width,height);
添加按钮的动作
jbt.addActionListener(实现接口并重写接口中的方法,该方法是监听的具体的动作);
把按钮添加到界面中
jFrame.getContentPane().add(jbt); jFrame是界面JFrame的对象
重写ActionListener接口中的方法,getSource():返回最初发生 Event 的对象,哪个对象被触发,就返回这个对象
鼠标监听机制-MouseListener
键盘监听机制-KeyListener
细节:
加载背景图片和边框
路径分为两种:
绝对路径和相对路径
绝对路径一般是从盘符开始的; D:\ C:\
相对路径不是从盘符开始的;相对路径相对于当前项目而言的。aaa\\bbb
在当前项目下可以先找aaa的文件夹再找bbb,实际开发中多用相对路径
例:
移动
//清空原本已经出现的所有图片 jframe.getContentPane().removeAll(); jframe是JFrame对象
//刷新一下界面 jframe.getContentPane().repaint();
重新游戏,关于我们,退出游戏
重新游戏:加载鼠标单击监听,重新打乱数组,计步器清零,重新加载图片
关于我们:弹窗设置JDialog,图片对象ImageIcon;不要忘了图片对象ImageIcon还是有JLabel来管理的。
退出游戏:加载单击监听,System.exit(0);
明文输入框JTextField和密文显示输入框JPasswordField
游戏打包成.exe安装包
常用API
Math,System,Runtime,Object和Objects,BigInteger和BigDecimal,正则表达式
Math类常用方法:
补充:
开平方根:Math.sqrt(double a) 开平方根
Math.cbrt(double a) 开立方根
System类
注意:
system.arraycopy();
在拷贝的时候需要考虑数组的长度,如果超出范围就会报错。即拷贝的目标数组长度不能小于被拷贝数组长度。
引用数据类型数组拷贝:
RunTime
表示当前虚拟机的运行环境
Object对象:
equals()方法:
equals()快捷重写,Alt+Ins键,找到equals()然后下一步即可
clone()方法:
因为父类object类中的clone方法是受保护的,不是lang包的类或者其子类不能使用clone方法。所以我们需要对其进行方法重写。
重写完之后要对类做一个实现,实现Cloneable接口
如果一个接口里没有抽象方法表示当前的接口是一个标记性的接口
克隆对象注意事项:
对象克隆有两种方式:浅克隆和深克隆 Object类中的克隆是浅克隆
浅克隆:把A对象的属性值完全拷贝给B对象,属性值是基本数据类型则直接把值拷贝,如果是引用数据类型或者数组,则直接把地址值拷贝过去,A对象和B对象共用同一片地址值的堆空间,当A对象引用数据类型修改时,B对象也跟着修改。
深克隆:基本数据类型会全部拷贝,克隆数组时会在堆中创建一个新的数组,然后把原来A数组的值复制到新数组中,然后再把数组的地址值给B。深克隆需要手动重写clone()方法。
Objects:是一个工具类,提供了一些方法去完成一些功能
Objects.equals():
BIgInteger:
一下为构造方法,需要创建对象才可以使用;
例如:BigInteger big=new BigInteger("9999999999999999999999");
sout(big);
BigInteger.valueOf(long类型);里面只能传入long类型范围内的整数,同获取指定大整数作用不同
静态方法valueOf()内部优化功能,在内部对常用数字-16~16进行了优化,提前创建好了其范围内的BIgInteger对象,在你调用其范围内的数时如果多次获取不会重新创建新的对象。
注意:BigInteger对象一旦创建内部的数据不能发生改变
BigInteger常见的成员方法
加减乘除,其中divideAndRemainder()方法获取的是一个数组,数组0索引存储的是商,1索引存储的是余数。
BigInteger底层存储方式:
计算机中的小数
BIgDecima对象:
BigDecimal的方法:
带精确位的除法中,舍入模式需调用RoundingMOde.方法,其中HALF_UP是四舍五入的方法。
BigDecimal底层存储原理:
会以字符的方式以ASCII码来存储起来,小数点有专门的ASCII码来存储,负数的负号也会有专门的ASCII码来存储
正则表达式
作用1:校验字符串是否满足规则
作用2:在一段文本中查找满足要求的内容
注意:
‘a’.matches("[abc]"); ‘aa’.matches("[abc][abc]");
字符类只匹配一个字符,即,一个正则表达式只匹配一个字符。若匹配对象是‘aa’,则需要两个正则表达式来与之匹配
a.matches(".");true aa.matches(".");flase
预定义字符同样只匹配一个字符,若有两个..则需要两个字符来与之匹配,否则报错。
java中\表示转义字符,改变后面那个字符原本的含义,例如打印字符串““”,sout(" \" ");
数量词:前面的X指其他的正则表达式
忽略大小写的正则表达式书写方式:(?i)
(?i)写在哪里,它后面的内容就自动忽视大小写
只忽略某个字母的大小写,只需要将该字母与((?i)x) 一起括起来即可
作用2:爬虫
爬虫:
Pattern:表示正则表达式
Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取
在大串中去找符合匹配规则的字串。
Matchaer的group()方法会对记录的索引进行字符串的截取
带条件爬取:
"java(?=8|11|17)";?理解为前面的数据,即java,=表示在java后面要跟随的数据,但是获取时只获取前部分,即括号前面的部分
同理以下的代码主体分为了两部分,获取的也是括号前的部分
获取整体:(java)(?:8|11|17);
等号获取的是前面的部分,而冒号获取的是整体。
去除部分获取:(java)(?!8|11|17)
感叹号指的是获取去除带感叹号后面的字符串。 、
贪婪爬取和非贪婪爬取
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
在java中默认的是贪婪爬取,例ab+ ab*等默认就是贪婪爬取,而在+ 和 * 后面再接上一个问号就为非贪婪爬取,ab+? ab*?
正则表达式在字符串方法中的使用
replaceAll()方法细节:
方法在底层一样会创建文本解析器的对象,然后从头开始去读字符串的内容,只要有满足的就用第二个参数去替代。
split()按照正则表达式的规则进行字符串的切割,切割完的结果是以字符串数组的形式存储:
分组:
每组都是有组号的,也就是序号:
规则1:从1开始连续不断
规则2:以左括号为基准,最左边的是第一组,其次为第二组,以此类推。
例如:String str1="a123a" "17891"。。。。。
若想要判断第一个字符和最后一个字符是否一致,只需要考虑第一个字符
正则表达式为:"(.).+\\1";意思为第一组为第一个字符,中间可以是任意字符且至少有一个,最后\\组号表示把第X组的内容再拿出来用一次,组号以左括号为准。这里指的是把第一组的内容再拿出来用了一次。
捕获分组:正则外部使用:
$+组号可以在正则外部把组号复用:
非捕获分组用?= ?: ?!做前缀后,此时是不占用组号的
常用API:
JDK7以前时间相关类
Data 时间
SimpleDataFormat 格式化时间
Calendar 日历
创建对象表示一个指定的时间时,应当传入long类型的数据。
Date d1=new Date(0L); System.out.println(d1); //打印的是1970年8:0:0的初始时间
Date d1=new Date(); System.out.println(d1); //不传递参数打印的是当前电脑的时间
SimpleDateFormat类
parse.parse()解析方法的运用:
解析完后可以调用Date.getTime()方法进行运算
Calendar类:
Calender c=new Calender.getInstance();
c.get(X);
//Calender会把时间中的纪元、年月日时分秒星期等存放在一个数组中,其中0:纪元;1:年
2:月。3: 一年中第几周。4:一月中的第几周。5:一月中的第几天。
同时这些下标对应的值也有对应的常量,如c.get(Calender.MONTH);获取的是月份。因为月份是从0开始的,0-11,所以我们通常要把获取到的月份+1;
JDK 8.0的时间类:
Date类的ZoneId相关使用:
ZoneId zoneId = ZoneId.systemDefault(); System.out.println(zoneId); //获取系统时区
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); System.out.println(availableZoneIds); //获取所有的时区
Instant时间戳:
ZoneDateTime带时区的时间:
细节: JDK8.0新增的时间对象是不可改变的,如果我们修改了减少了或者增加了时间,那么调用者是不会发生改变的,会产生一个新的时间。
DateTimeFormatter日期格式化类:
日历相关的类:
LocalDate.get只能获取到年月日
LocalTime.get获取的时分秒
LocalDateTime可以获取所有
LocalDate ld=LocalDate.of(2022,3,1); System.out.println(ld.isLeapYear());
ld.isLeapYear();可判断是不是闰年。
此外LocalDateTime可以转换成LocalDate、LocalTime
总结:
1. Calendar中的月份是0-11;而LocalDate中的月份是1-12是正常的
包装类:
Integer对象会把-128~127提前创建好对象并放入到数组中,当使用valueOf()来判断时会先找数值是不是在-128~127之间,如果在的话,Integer对象地址值相等,如果不是则创建新的对象来存储
Integer的成员方法:
8种包装类中,除了Character都有对应的parseXXX的方法来进行类型转换:
注意:获取年是int类型,而获取月是Month对象类型,方式一和方式二都可将月份转为阿拉伯数组
基本查找、二分查找/折半查找、分块查找
基本查找略过。
二分查找/折半查找
前提条件:数组中的数据必须是有序的
核心逻辑:每次排除一半的查找范围
private static int halfSelect(int[] arr,int i) {
int min=0;
int max=arr.length-1;
int mid;
while (min<max){
mid=(min+max)/2;
if(arr[mid]>i){
max=mid-1;
}else if(arr[mid]<i){
min=mid+1;
}else if(arr[mid]==i){
return mid;
}
}
return -1;
}
插值查找(二分查找升级版):
要求:数值分布比较均匀。
斐波那契查找:
分块查找:
块内无需,但块与块之间有序
分块原则1:前一块中的最大数组,小于后一块中的所有数据(块内无序,块中有序)
分块原则2:块数数量一般等于数字的个数开根号。如;16个数字一般分为4块左右
核心思路:先确定要查找的元素在那一块,然后在块内挨个查找。
public class Block {
private int max;
private int startIndex;
private int endIndex;
public Block() {
}
public Block(int max, int startIndex, int endIndex) {
this.max = max;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
int[] arr={11,12,15,16,
111,120,160,177};
Block b1=new Block(16,0,3);
Block b2=new Block(177,4,7);
Block[] blockArr={b1,b2};
int index=selectIndex(blockArr,111,arr);
if(index!=-1) System.out.println(index);
else {
System.out.println("不存在");
}
//在该元素所对应的分块下查找具体在数组中的下标
private static int selectIndex(Block[] bk,int number,int[] arr) {
int index=selectBlockIndex(bk,number);
int startIndex = bk[index].getStartIndex();
int endIndex = bk[index].getEndIndex();
for (int i = startIndex; i <=endIndex; i++) {
if(arr[i]==number){
return i;
}
}
return -1;
}
//查找该元素在哪个分块里
private static int selectBlockIndex(Block[] bArr,int number) {
for (int i = 0; i < bArr.length; i++) {
if(bArr[i].getMax()>=number){
System.out.println("在第几块:"+i);
return i;
}
}
return -1;
}
倘若给的是一串无规律的数据,我们在分块的时候要注意块之间不能有交集:
int[] arr={27,22,30,40,36,
13,19,16,20,
7,10,
43,50,48};
Block b1=new Block(40,22,0,4);
Block b2=new Block(20,13,5,8);
Block b3=new Block(10,7,9,10);
Block b4=new Block(50,43,11,13);
Block[] blockArr={b1,b2,b3,b4};
int index=selectIndex(blockArr,0,arr);
if(index!=-1) System.out.println(index);
else {
System.out.println("不存在");
}
}
//在该元素所对应的分块下查找具体在数组中的下标
private static int selectIndex(Block[] bk,int number,int[] arr) {
int index=selectBlockIndex(bk,number);
if(index==-1){
return -1;
}
int startIndex = bk[index].getStartIndex();
int endIndex = bk[index].getEndIndex();
for (int i = startIndex; i <=endIndex; i++) {
if(arr[i]==number){
return i;
}
}
return -1;
}
//查找该元素在哪个分块里
private static int selectBlockIndex(Block[] bArr,int number) {
for (int i = 0; i < bArr.length; i++) {
if(bArr[i].getMax()>=number && number>=bArr[i].getMin()){
//因为数组内数据是无序的,但比较块内最大值没意义,
// 这时找number在哪一个块内要比较小于最大值,大于最小值的满足需求的块
System.out.println("在第几块:"+i);
return i;
}
}
return -1;
}int[] arr={27,22,30,40,36,
13,19,16,20,
7,10,
43,50,48};
Block b1=new Block(40,22,0,4);
Block b2=new Block(20,13,5,8);
Block b3=new Block(10,7,9,10);
Block b4=new Block(50,43,11,13);
Block[] blockArr={b1,b2,b3,b4};
int index=selectIndex(blockArr,0,arr);
if(index!=-1) System.out.println(index);
else {
System.out.println("不存在");
}
}
//在该元素所对应的分块下查找具体在数组中的下标
private static int selectIndex(Block[] bk,int number,int[] arr) {
int index=selectBlockIndex(bk,number);
if(index==-1){
return -1;
}
int startIndex = bk[index].getStartIndex();
int endIndex = bk[index].getEndIndex();
for (int i = startIndex; i <=endIndex; i++) {
if(arr[i]==number){
return i;
}
}
return -1;
}
//查找该元素在哪个分块里
private static int selectBlockIndex(Block[] bArr,int number) {
for (int i = 0; i < bArr.length; i++) {
if(bArr[i].getMax()>=number && number>=bArr[i].getMin()){
//因为数组内数据是无序的,但比较块内最大值没意义,
// 这时找number在哪一个块内要比较小于最大值,大于最小值的满足需求的块
System.out.println("在第几块:"+i);
return i;
}
}
return -1;
}
哈希查找:
排序算法:
冒泡排序:
选择排序:
从0索引开始,拿着每一个索引上面的元素依次比较,小的放前面,大的放后面,以此类推。
插入排序:
for (int i = indexof; i < arr.length; i++) {
for (int j = i; j>0; j--) {
int temp=arr[j];
if(arr[j-1]>arr[j]){
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
}
递归算法:
递归指的是方法中调用方法本身的现象。
递归核心:1.找出口 2.找规律
快速排序:
public static void main(String[] args) {
int[] arr={6,4,2,5,1,3,9,7,8};
quckSort(arr,0,arr.length-1);
printArr(arr);
}
//传入三个形参,数组,开始索引,结束索引
public static void quckSort(int[] arr,int i,int j){
int start=i;
int end=j;
if(start>end){
return;
}
int baseNumber=arr[i];
//查找比基索引小的end和比基索引大的start
while(end!=start){
while (true){
if (end<=start || arr[end]<baseNumber){
break;
}
end--;
}
while (true){
if (start>=end || arr[start]>baseNumber){
break;
}
start++;
}
int temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
}
int tem=arr[start];
arr[start]=baseNumber;
arr[i]=tem;
quckSort(arr,i,start-1);
quckSort(arr,start+1,j);
}
Arrays
操作数组的工具类;
二分查找:
拷贝数组copyof():
Arrays.sort();按照指定规则排序:
重写sort里面的Comparator接口。接口内的方法就是排序的规则,o1-o2是升序,o2-o1是降序
Lambda表达式:
简化匿名内部类
简化前:
Arrays.sort(in, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
Lambda简化后 :
Arrays.sort(in, (Integer o1, Integer o2)->{
return o1-o2;
}
);
Lambda表达式省略写法:
1.参数类型可以省略不写
2.如果只有一个参数,参数类型可以省略,同时()也可以省略
3.如果Lambda表达式的方法体只有一行,大括号、分号、return 可以省略不写,但需要同时省略。
匿名内部类格式:
练习:按照字符串的长度进行排序:
递归练习:
集合进阶
集合的体系结构
Collection和Map:
Collection又分为List和Set,其中红色的是接口,蓝色的是实现类:
注意:List系列集合中的有序是指的存和取的顺序是有序的,而不是数据按从小到大这种的有序
Collection接口的方法:
判断contains()当前集合是否包含给定的对象,若对象是自定义对象,则需要在自定义对象中重写equals()方法。因为contains()方法底层实现是用object类的equals方法的,匹配的是地址值。
Collection的遍历方式:
迭代器遍历、增强for遍历、Lambda表达式遍历
迭代器注意事项:
Collection<String> coll=new ArrayList<>();
Iterator<String> iterator=coll.iterator();
iterator.remove();
增强for遍历:
快捷方法:集合名字.for可以直接出来
Lambda表达式遍历:
匿名内部类形式:
转为Lambda形式:
List集合:
List集合特有方法:
add()方法细节:把元素插在指定位置,若该位置有元素。则原来索引上的元素会向后移动
remove()方法细节:当你不知道删除int类型不知道是元素还是下标时,如果传入的是没封装的int类型,删除的就是下标,如果封装成了Integer类型,删除的就是元素。
list的遍历方式:
列表迭代器:
可以在迭代器里进行修改删除等操作
list五种遍历方式对比:
数据结构:
常见数据结构:
List中的ArrayList集合:
底层原理是运用了数组的数据结构
完整的扩容机制:
LinkedList集合:
底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的。
LinkedList本身多了很多直接操作首尾元素的API。
list.remove()方法,在删除前一个元素后,被删除元素之后的元素会自动向前补位。
集合进阶:
泛型深入:
泛型可在很多地方进行定义
泛型类、泛型方法、泛型接口
自定义泛型类:
当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类。
泛型方法:
泛型方法定义时需要注意,泛型定义在修饰符的后面。如:public static<E>或者public<E>
以泛型方法工具类为例:
泛型接口:
泛型接口两种使用方式:
1.实现类给出具体的类型
2.实现类延续泛型
泛型的继承和通配符
泛型不具备继承性,但数据具备继承性
方法里面传的泛型是什么就是什么,其子类不能继承泛型
若只希望能传递限定类型,如父子关系 :
此时就可以使用泛型的通配符:
?表示不确定的类型
他可以进行类型的限定
?extend E 表示可以传递所有的E以及E的子类类型
?super E 表示可以传递E或者E所有的父类类型
public class Test13 {
public static void main(String[] args) {
ArrayList<ye> list1=new ArrayList<>();
ArrayList<fu> list2=new ArrayList<>();
ArrayList<zi> list3=new ArrayList<>();
ArrayList<Student> list4=new ArrayList<>();
method(list1);
method(list2);
method(list3);
method(list);//报错
}
public static void method(ArrayList<? super zi> list){}
//public static void method(ArrayList<? extend ye> list){}
//表示只能传入ye类及其子类类型
}
class ye{}
class fu extends ye{}
class zi extends fu{}
class Student{}
应用场景:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以用泛型的通配符。
关键点:限制类型的范围。
Set
树数据结构
二叉查找树
平衡二叉树:
左旋案例2:
平衡二叉树需要旋转的四种情况:
1.左左 2.左右
3.右右 4.右左
左右:先局部的左旋,再整体的右旋
右左:根节点的左子树有节点插入导致二叉树不平衡,先局部右旋,再整体左旋
平衡二叉树也是查找二叉树的一种,也遵循小的存左边,大的存右边,相等的不存
红黑树:
Set系列集合:
HashSet:
HashSet底层原理;
加载因子0.75是HashSet的扩容时机,默认是16个,当数组达到16*0.75=12个时会自动扩容成原先的两倍。
当链表的长度大于8而且数组长度大于等于64时,这两个条件要同时满足,数组下挂元素的链表会自动转为红黑树,从而提高了查找效率。
LinkedHashSet:
TreeSet:
默认打印规则是有序的,且是从小到大的:
TreeSet默认排序规则:
TreeSet有两种指定比较规则:
方式一:
当TreeSet自定义数据类型时,如Student类型,需要实现Comparable<Student>并重写里面的compareTo()方法,在这个方法里自定义排序规则。
方式二:比较器排序
当方式一和方式二同时存在时,以方式二为准,比如TreeSet<String>,包装类里面都默认有方式一的比较方式,我们重写了方式二,就以方式二为准了。