JavaSE基础加强
static
static 静态关键字
- static 是静态的意思,可以修饰成员变量和成员方法
- static 修饰的成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改
成员变量可以分为2类
静态成员变量(有static修饰,属于类,内存中加载一次):常表示如在线人数信息、等需要被共享的信息,可以被共享访问。
public class User {
public static String onlineNumber = 161;
}
类名.静态成员变量
对象.静态成员变量
实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。
public class User {
private String name;
private int age;
}
对象名.实例成员变量
static修饰成员变量的内存原理
static 修饰成员方法的基本用法
成员方法的分类:
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可用对象访问
- 实例成员方法(无static修饰,归属于对象),只可用对象访问
表示对象自己的行为,且方法中需要访问实例成员的,则该方法必须声明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以声明成静态方法。
static 修饰成员方法的内存原理
static 注意事项
- 静态方法只能访问静态的成员,不能直接访问实例成员
- 实例方法可以访问静态成员,也可以访问实例成员
- 静态方法中不能使用this关键字
static 应用知识:工具类
类中都是一些静态方法,每个方法都是完成一个公共的功能为目的,这个类用来给系统开发人员共同使用的
好处:
- 调用方便
- 重复可以
static 应用知识:代码块
代码块的分类、作用
- 代码块是类中五大成分之一(成员变量、方法、构造器、代码块、内部类),定义在类中方法外。
- 在Java类下,使用{ }括起来的代码被称为代码块。
代码快分为:
- 静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
- 构造代码块
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
使用场景:初始化实例资源。
静态代码块的应用案例
斗地主游戏
package com.wuxibin.demo;
import java.util.ArrayList;
public class Test03 {
public static ArrayList<String> list = new ArrayList<>();
static {
String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String[] color = {"♠","♥","♦","♣"};
for (int i = 0; i < number.length; i++) {
for (int j = 0; j < color.length; j++) {
String code = number[i] + color[j];
list.add(code);
}
}
list.add("小王");
list.add("大王");
}
public static void main(String[] args) {
System.out.println(list);
}
}
static应用知识:单例设计模式
什么是设计模式
- 开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
- 设计模式有20多种,对应20多种软件开发中会遇到的问题。
- 学设计模式主要是学2点:
- 第一:这种模式用来解决什么问题。
- 第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例模式:
- 可以保证系统中,应用该模块的这个类永远只有一个实例,即一个类永远只能创建一个对象
- 例如任务管理器对象我们只需要一个对象就可以解决问题,节省内存空间
饿汉单例设计模式:
- 再用类获取对象的时候,对象已经为你创建好了
设计步骤:
- 定义一个类,把构造器私有
- 定义一个静态变量储存一个对象
public class SingleInstance{
public static SingleInstance instance = new SlingleInstance();
private SingleInstance(){
}
}
懒汉单例模式:
- 在真正需要该对象的时候,才去创建一个对象(延迟加载对象)
- 设计步骤:
- 定义一个类,把构造器私有
- 定义一个静态变量储存一个对象
public class SingleInstance {
private static SingleIntance intance;
public static SingleIntance(){
if(intance == null){
intance = new SingleIntance;
}
}
private SingleInstance(){
}
}
面向对象第二个特征 继承
什么是继承?
- java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
public class Student extends People{}
-
Student称为子类(派生类),People称为父类(基类或超类)
-
作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了
内存原理:,
继承的特点:
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
- Java是单继承模式:一个类只能继承一个直接父类。
- Java不支持多继承、但是支持多层继承。
- Java中所有的类都是Object类的子类。
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
如果子父类中,出现了重复的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
- 可以通过super();
方法重写:
- 在继承关系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法
@Override重写注解
- @Override是放在重写后的方法上,作为重写是否正确的校验注解
- 加上该注解后如果重写错误,编译阶段会出现错误提示
- 建议重写方法都加@Override注解,代码安全,优雅
方法重写注意事项和要求
- 重写方法的名称、形参列表与被重写的方法的名称和参数列表一致
- 私有方法不能被重写
- 子类重写父类方法时,访问权限必须大于或者等于父类(缺省<protected<public)
- 子类不能重写父类的静态方法,如果重写会报错
子类构造器的特点:
- 子类中所有的构造器会先访问父类的无参构造器在执行自己
为什么?
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有初始化,子类将无法使用父类的数据
- 子类在初始化之前,一定要调用父类构造器先完成父类数据空间的初始化
怎么调用父类构造器的?
- 子类构造器中的第一行语句默认都是:super();,不写也存在
子类 构造器访问父类有参数构造器
super调用父类有参数构造器的作用:
- 初始化继承自父类的数据
this和super使用总结
- this:代表本类对象的引用 super:代表父类存储空间的标识
this(...)和super(...)使用注意点:
- 子类通过this(...)去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的构造器,最终还是会调用父类构造器的
- 注意:this(...)和super(...)都只能放在构造器的第一行,所以二者不能共存在同一个构造器中
包
什么是包?
-
包是用来分门别类的管理各种不同类的,类似于文件夹、建包
-
建包的语法格式: package公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
package com.itheima.javabean; public class Student{ }
-
建包语句必须在第一行,一般IDEA工具会帮助创建
导包
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式: import包名.类名;
- 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
权限修饰符
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问的范围
- 可以修饰成员变量、方法、构造器、内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制
权限修饰符的分类和具体作用范围:
- 权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected -> public)
final
- final关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:表明该类是最终类,不能被继承。
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
常量
- 常量是使用了public static final 修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变
- 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能拿提高可读性
public class Constant {
public static final String SCHOOL_NAME = "教育";
public static final String LOGIN_NAME = "admin";
public static final String PASS_NAME = "123456";
}
// 常量命名规范:英文单词全部大写,多个单词下划线连接起来
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
枚举
枚举的概述
- 枚举是java中的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类“
定义枚举的格式
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称
}
enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
枚举的特征:
- 枚举类都是继承了枚举类型: java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 构造器的构造器都是私有的,枚举对外不能创建对象
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。
抽象类
- 在Java中abstract是抽象的意思,可以修饰类、成员方法
- abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
}
public abstract class Animal{
public abstract void run();
}
抽象类的使用场景
- 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
- 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
接口
接口的概述、特点
- 格式:
接口用关键字interface来定义
public inerface 接口名{
// 常量
// 抽象方法
}
- 接口也是一种规范
接口的使用
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类
修饰符 class 实现类 implements 接口1,接口2,接口3{
}
实现的关键字:implements
- 接口可以被单实现,也可以被类多实现
接口与接口的关系:多继承
- 类和类:单继承
- 类和接口:多实现
- 接口和接口:多继承,一个接口可以继承多个接口
JDK8后新增功能:
默认方法:
- 类似之前写的普通实例方法:必须用default
- 默认会public修饰。需要用接口的实现类的对象来调用
静态方法:
- 默认会public修饰,必须用static修饰
- 注意:接口的静态方法必须用本身的接口名来调用
私有方法:
- 就是私有的实例方法:必须用private修饰,从JDK1.9才开始有的
- 只能在本类中被其他的默认方法或者其他的私有方法访问
接口的注意事项:
- 接口不能创建对象
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突。
- 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
- 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
- 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
面向对象的三大特征之一:多态
什么是多态?
-
同类型的对象,执行同一个行为,会表现出不同的特征
-
常见的形式:
父类类型 对象名 = new 子类构造器
接口 对象名称 = new 实现类构造器
-
多态中成员访问特点:
-
- 方法调用:编译看左边,运行看右边
- 变量调用:编译看左边,运行也看左边(多态侧重行为多态)
-
多态的前提:
-
有继承关系;有父类引用指向子类对象;有方法重写;
优势
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护
Animal a = new Dog();
a.run(); // 后续业务行为随对象而变,后续代码无需修改
- 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利
- 多态下不能使用子类的独有功能
自动类型转换(从子到父):
- 子类对象复制给父类类型的变量指向
强制类型转换(从父到子):
-
此时必须进行强制类型转换:子类 对象变量 = (子类) 父类类型的变量
-
作用:可以解决多态下的劣势,可以实现调用子类独有的功能
-
注意:如果转型后的类型和对象真实类型不是同一类型,那么在转换的时候就会出现ClassCastExceptin
Java建议强转转换前使用instanceof判断低当前对象的真实类型,在进行强制转换
变量名 instanceof 真实类型
内部类
内部类概述
-
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)
public class People { //内部类 public class Heart{ } }
使用场景、作用:
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构由只为外部事物提供服务,那么整个内部的完整结构可以使用内部类来设计
- 内部类通常可以方便问外部类的成员,包括私有的成员。
- 内部类提供了更好的封装性,内部类本身就可以用private protectecd等修饰,封装性可以做到多控制
内部类之一:静态内部类
-
有static修饰,属于外部类本身
-
他的特点和普通类是完全一样的,类有的成分他都有,只是位置在别人里面而已
public static Outer{ //静态成员内部类 public static Inner{ } }
-
静态成员内部类创建对象的格式:
格式: 外部类名.内部类名 对象名 = new 外部类名.内部类构造器; 举例: Outer.Inner in = new Outer.Inner();
内部类之一:成员内部类
-
无static修饰,属于外部类的对象
-
JDK16之前,成员内部类中不能定义静态成员,JDK16开始也可以定义静态成员了
-
可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员
public static Outer{ //成员内部类 public class Inner{ } }
-
成员内部类创建对象的格式:
外部类名.内部类名 对象名 = new 外部类构造器.内部类构造器; Outer.Inner in = new Outer().Inner();
内部类之一:局部内部类
- 放在方法,代码块,构造器等执行体中
内部类之一:匿名内部类
-
本质上就是一个没有名字的局部内部类,定义在方法,代码块中,等
-
作用:方便创建子类对象,最终目的为了简化代码编写
格式: new 类|抽象类名|或者接口名(){ 重写方法; }; Employee a = new Enployee(){ public void work(){ } } a.work();
-
特点:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类写出来就会产生一个匿名内部类的对象
- 匿名内部类的对象类型相当于是当前new的那个类型的子类类型
匿名内部类常见使用形式
-
某个学校需要让老师,学生,运动员一起参加游泳比赛
package com.wuxibin.anonymous; public class Test { public static void main(String[] args) { Swimming s1 = new Swimming() { @Override public void swim() { System.out.println("学生缓慢的游泳"); } }; go(s1); System.out.println("-------------"); Swimming s2 = new Swimming() { @Override public void swim() { System.out.println("老师游的很快"); } }; go(s2); System.out.println("-------------"); Swimming s3 = new Swimming() { @Override public void swim() { System.out.println("运动员游的特别快"); } }; go(s3); } public static void go(Swimming s){ System.out.println("开始"); s.swim(); System.out.println("结束"); } } //class Student implements Swimming{ // @Override // public void swim() { // System.out.println("学生缓慢的游泳"); // } //} //class Teacher implements Swimming{ // @Override // public void swim() { // System.out.println("学生缓慢的游泳"); // } //} //class Athlete implements Swimming{ // @Override // public void swim() { // System.out.println("学生缓慢的游泳"); // } //} //游泳接口 interface Swimming{ void swim(); }
匿名内部类真实使用场景演示
-
匿名内部类在开发中的真实使用场景演示
-
给按钮绑定点击事件
btn.addActionListener(new ActionListener(){ @Override public boid actionPerformed(ActionEevent e){ System.out.println("登陆一下") } }); // btn.addActionListener(e -> System.out.println("登陆一下~~"));
-
package com.wuxibin.anonymous;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* 目标:通过GUI编程,理解匿名内部类的真实使用场景
*/
public class Test01 {
public static void main(String[] args) {
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
btn.addActionListener( e ->JOptionPane.showConfirmDialog(win,"点我一下"));
panel.add(btn);
win.setSize(400,300);
win.setLocationRelativeTo(null);
win.setVisible(true);
}
}
常用API
API(Application Programma Interface)应用程序编程接口
简单来说:就是Java帮我们已好写好的一些方法,我们直接拿过来用就可以了
Object
- 一个类要么默认继承了Object类,要么简介继承了Object类,Object类是Java中的祖宗类
- Object类的方法是一切子类的都可以直接使用的,所以我们要学习Object类的方法
常用方法
方法名 | 说明 |
---|---|
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的权限名@内存地址 |
public Boolean equals(Object 0) | 默认是比较当前对象与另一个对象的地址是否相同,相同则true,不同则false |
toString 存在的意义是为了被子类重写
@Override
public String toString(){
return "Student{"+"name='"
}
equals存在的意义是为了被子类重写,以便更好的限制
@Override
public boolean equals(Object o){
if(o instanceof Student){
Student s2 = (Student) o
if(this.name.equals(o.name)&&this.age == s2.age && this.sex = s2.sex){
return true;
}
}else{
return false;
}
}
Objects
Objects类与Object还是继承关系,Object类是从JDK1.7开始之后才有的
官方在进行字符串比较时,没有对象
Object.equals(Object a,Object b)
isNull:判断是否为null,返回true或false
StringBulider
- StringBuilder是一个可变字符串类,我们可以把它看成是一个对象容器
- 作用:提高字符串的操纵效率,如拼接,修改等
StringBuilder构造器
名称 | 说明 |
---|---|
publid StringBuilder | 创建一个空白的可变的字符串对象,不包含任何内容 |
publid StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
StringBuilder常用方法
方法名称 | 说明 |
---|---|
public StirngBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
publid int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现吧StringBuilder转换为String |
package com.wuxibin.object;
public class Test1 {
public static void main(String[] args) {
int[] arr1 = {11,34,56,34};
System.out.println(toString(arr1));
}
public static String toString(int[] arr){
if (arr != null){
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length-1 ? " " : ", ");
}
sb.append("]");
return sb.toString();
}else {
return null;
}
}
}
Math
- 包含执行基本数字运算的方法,Math类没有提供公开的类
- 如何使用类中的成员呢?看类的成员是否都是静态的,如果是,通过类名就可以直接调用
Math类的常用方法
方法名 | 说明 |
---|---|
public static int abs(int a) | 获取参数绝对值 |
public static double ceil (double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round(float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值,范围【0.0,1.0) |
System
- System的功能是通用的,都是直接用类名调用即可,所以System不能被实例化
System类的常用方法
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的java虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数) | 数组拷贝 |
BigDecimal
- 用于解决浮点型运算精度失真的问题
使用步骤:
-
创建BigDecimal封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val): 包装浮点数成为BigDecimal
BigDecimal常用API
方法名 | 说明 |
---|---|
public BigDecimal add(BigDecimal b) | 加法 |
public BigDecimal subtract(BigDecimal b) | 减法 |
public BigDecimal multiply(BigDecimal b) | 乘法 |
public BigDecimal divide(BigDecimal b) | 除法 |
public BigDecimal divide(另一个BigDecimal对象 ,精确位数,舍入模式) | 除法 |
日期和时间
Date类
- Date类的对象在Java中代表的是当前所在系统的此刻日期
-
Data构造器
public Date() 创建一个Date对象,代表的是系统当前此刻日期时间
-
Date常用的方法
public long getTime() 获取时间毫秒值
Date类记录时间
-
形式1:日期对象
//创建一个日期对象代表了:当前系统的此刻日期时间对象 Date d = new Date(); System.out.println(d);
-
形式2:时间毫秒值
- 值得是从1970年1月1日 00:00:00走到此刻的总毫秒数
//获取当前时间毫秒值 long time = d.getTime(); System.out.println(time)
SimpleDateFormat
-
可以对Date对象或时间毫秒值格式化成我们喜欢的时间形式
-
也可以把字符串的时间形式解析成日期对象
格式化: Date对象 2099年11月11日 11:11: 时间毫秒值 2099年11月11日 11:11: 解析: 2099年11月11日 11:11:11 Date对象
SimpleDateFormat构造器
构造器 | 说明 |
---|---|
public SimpleDateFormat() | 构造一个SimpleDateFormat,使用默认格式 |
public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat,使用指定的格式 |
SimpleDateFormat的格式化方法
格式化方法 | 说明 |
---|---|
publid final String format(Date date) | 将日期格式化成日期/时间字符串 |
public final String format(Object time) | 将时间毫秒值式化成日期/时间字符串 |
- y 年
- M 月 2022-11-11 13:27:06 yyyy-MM-dd HH:mm:ss
- d 日
- H 时 2022年11月11日 13:27:06 yyyy年MM月dd日 HH:mm:ss
- m 分
- s 秒
解析方法 | 说明 |
---|---|
public Date parse(String source) | 从给定字符串的开始解析文本以生成日期 |
案例:请计算出2021年08月06日11点11分11秒,往后走2天14小时49分06秒后的时间是多少
package com.wuxibin.object;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test2 {
public static void main(String[] args) throws ParseException {
String dateStr = "2021年08月06日 11:11:11";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d = sdf.parse(dateStr);
long time = d.getTime() + (2L*24*60*60 + 14*60*60 + 49*60 +6)* 1000;
System.out.println(sdf.format(time));
}
}
Calenda类
- Calendar代表了系统此刻日期对应的日历对象
- Calendar是一个抽象类,不能直接创建对象
方法名 | 说明 |
---|---|
public int get(int field) | 取日期中的某个字段信息 |
public void set(int field,int value) | 修改日历的某个字段信息 |
public void add(int field,int amount) | 为某个字段增加/减少指定的值 |
public final Date getTime() | 拿到此刻日期对象 |
public long getTimeInMillis | 拿到此刻时间毫秒值 |
注意:calendar是可变日期对象,一旦修改后其对象本身表示的时间将产生变化
JDK8新增日期类
LocalDate,LocalTime,LocalDateTime
-
他们分别代表日期,时间,日期时间对象,他们的类的实例是不可变的对象
-
他们三者构建对象和API都是通用的
Period
Buration
ChronoUnit类
包装类
- 其实就是8种基本数据类型对应的引用类型
基本数据类型 | 引用数据类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
-
为什么提供包装类?
- Java为了实现一切皆对象,为8类基本类型提供了对应的引用数据类型
- 后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型
-
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量
-
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量
包装类特有的功能
-
包装类的变量默认值都是null,容错率更高
-
可以吧基本类型的数据转换成字符串类型(用处不大)
1.调用toString()方法得到字符串结果 2.调用Integer.toString(基本类型数据)
-
可以把字符串类型的数值转换成真实的数据类型(有用)
1.Integer.parselnt("字符串类型的整数") 2.Double.parseDouble("字符串类型的小数") Integer.valueOf( ) // 效果完全一样 Double.valueOf()
正则表达式
-
正则表示可以用一些规定的字符来制定规则,并用来校验数据格式的合法性
-
字符串对象提供了匹配正则表达式规则的API
public boolean matches(String regex):判断是否匹配正则表达式,匹配返回true,不匹配返回false
-
正则表达式在字符串方法中的使用
方法名 说明 public String replaceAll(String regex,String newStr) 按照正则表达式匹配的内容进行替换 public String split(String regex) 按照正则表达式匹配的内容进行分割字符串,返回一个字符串数组
Arrays类
-
数组操作工具类,专门用于操作数组元素
方法名 说明 public static String toString(类型[] a) 对数组进行排序 public static void sort(类型[] a) 对数组进行默认升序排序 public static void sort(类型[] a,Comparator<?super T>C) 使用比较器对象自定义排序 public static int binarySearch(int[] a,int key) 二分搜索数组中的数据,存在返回索引,不存在返回-1 -
自定义排序规则:
设置Comparator接口对应的比较器对象,来定制比较规则
如果认为左边数据 大于 右边数据 返回正整数
如果认为左边数据 小于 右边数据 返回负整数
如果认为左边数据 等于 右边数据 返回0
public class Test2 { public static void main(String[] args){ Integer[] ages = {34,56,23,1,39}; Arrays.sort(ages, new Comparator<Integer>(){ @Override public int compare(Integer o1,Integer o2) { return - (o1 -o2); } }); System.out.println(Arrays.toString(ages)); } }
常见算法
选择排序
public class Test2 {
public static void main(String[] args){
int[] arr = {5,1,3,2};
// 0 1 2 3
// 0 1 2 3
// 1 2 3
// 2 3
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
冒泡排序
public class Test2 {
public static void main(String[] args){
int[] arr = {5,1,3,2};
// 0 1 2 3
// 0 1,5,3,2 1,3,5,2 1,3,2,5
// 1 1,3,2,5 1,2,3,5
// 2 1,2,3,5
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length -i -1 ; j++) {
if (arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
二分查找
/**
* 二分查找算法
* @param arr 排序的数组
* @param data 要找的数据
* @return 索引,如果元素不存在,直接返回-1
*/
public static int binarySearch(int[] arr,int data){
int left = 0;
int right = arr.length -1;
while (left <= right){
int middleIndex = (left + right)/2;
if (data > arr[middleIndex]){
left = middleIndex +1;
}else if (data < arr[middleIndex]){
right = middleIndex -1;
}else {
return middleIndex;
}
}
return -1;
}
}
Lambda表达式枚举
-
新语法形式
-
简化匿名内部类的代码写法
(匿名内部类被重写方法的形参列表)->{ 被重写方法的方法体代码 } 注:->是语法形式,无实际含义
-
Lambda表达式只能简化函数式接口的匿名内部类的写法形式
函数式接口
- 首先必须是接口,其次接口中有且仅有一个抽象方法的形式
- 通常我们会在接口上加上一个@FunctionalInterface注解,标记该接口必须是满足函数式接口
Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
- 参数类型可以省略不写。
- 如果只有一个参数,参数类型可以省略,同时()也可以省略。
- 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
- 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写
package com.wuxibin.object;
public class Test2 {
public static void main(String[] args){
Swimming s1 = new Swimming() {
@Override
public void run() {
System.out.println("学生游的很慢~~");
}
};
go(s1);
System.out.println("----------------");
Swimming s2 = () -> System.out.println("老师游的很快~~");
go(s2);
System.out.println("-------------------");
go(() -> System.out.println("运动员游的特别快~~"));
}
public static void go(Swimming swimming){
System.out.println("开始。。。");
swimming.run();
System.out.println("结束。。。");
}
}
interface Swimming{
public void run();
}
集合概述
集合
- 集合和数组都是容器
- 集合是Java中存储对象数据的一种容器
- 集合的特点:
- 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球
- 集合非常适合做元素的增删操作
注意:集合中只能存储引用数据类型,如果要存储基本类型数据可以选用包装类
- 集合适合的场景:
- 数据的个数不确定,需要进行增删元素
数组
- 数组定义完成并启动后,类型确定,长度稳定
- 在进行增删数据操作的时候,数组是不太合适的,增删数据都需要放弃原有数组或者移动
- 当业务数据的个数是固定的,且都是同一批数据类型的时候,可以采取定义数组存储
Collection集合的体系特点
Collection集合体系
Collection集合特点
- List系列集合:添加的元素是有序、可重复、有索引
- ArrayList、LinekdList:有序、可重复、有索引
- Set系列集合:添加的元素是无序、不重复、无索引
- HashSet:无序、不重复、无索引;LinkedHashSet:
有序
、不重复、无索引 - TreeSet:
按照大小默认升序排序
、不重复、无索引
- HashSet:无序、不重复、无索引;LinkedHashSet:
集合对于泛型的支持
-
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>(); Collection<String> lists = new ArrayList<>();//JDK1.7开始后面的泛型类型申明可以省略不写
-
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象
//存储基本类型使用包装类 Collection<Integer> lists = new ArrayList<>(); Collection<Double> Lists = new ArrayList<>();
Collection集合常用的API
Collection集合
- Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
方法名称 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中的元素个数 |
pulic Object[] toArray() | 把集合中的元素,存储到数组中 |
public boolean addAll(E e) | 把集合e复制到集合中 |
Collection集合的遍历方式
方式一:迭代器
- 遍历就是一个一个的把容器中的元素访问一遍
- 迭代器在Java中代表Iterator,迭代器是集合的专用遍历方式
public class Test2 {
public static void main(String[] args){
Collection<String> lists = new ArrayList<>();
lists.add("Java");
lists.add("Python");
lists.add("HTML");
lists.add("MySQL");
System.out.println(lists);
//1.得到当前集合的迭代器对象
Iterator<String> it= lists.iterator();
/* String ele = it.next();
System.out.println(ele);
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());*/
//2.定义while循环
while (it.hasNext()){
System.out.println(it.next());
}
}
}
方式二:foreach/增强for循环
- 增强for循环:既可以遍历集合也可以遍历数组
- 他是JDK5之后出现的,七内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法
- 实现Interable接口的类才可以使用迭代器和增强for,Collection接口已经实现类Iterable接口
格式
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
Collection<String>list = new ArrayList<>();
...
for(String ele : list){
System.out.println(ele);
}
方式三:lambda表达式
Collection结合Lambda遍历的API
public class Test2 {
public static void main(String[] args){
Collection<String> lists = new ArrayList<>();
lists.add("Java");
lists.add("Python");
lists.add("HTML");
lists.add("MySQL");
System.out.println(lists);
//1.得到当前集合的迭代器对象
/*for (String ele:lists){
System.out.println(ele);
}*/
/* lists.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
lists.forEach(s -> System.out.println(s) );
// lists.forEach(System.out::println);
}
}
Collection集合存储自定义类型的对象
package com.wuxibin.object;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class Test2 {
public static void main(String[] args){
Collection<Moves> movies = new ArrayList<>();
movies.add(new Moves("《你好,李焕英》",9.5,"张小斐,贾玲"));
movies.add(new Moves("《唐人街探案》",9.5,"王宝强,刘昊然"));
movies.add(new Moves("《刺杀小说家》",9.5,"雷佳音,杨幂"));
movies.forEach(new Consumer<Moves>() {
@Override
public void accept(Moves moves) {
System.out.println("片名:"+moves.getName()+" 得分:"+moves.getGrade()+" 主演:"+moves.getActor());
}
});
}
}
package com.wuxibin.object;
public class Moves {
private String name;
private double grade;
private String actor;
public Moves(){
}
public Moves(String name, double grade, String actor) {
this.name = name;
this.grade = grade;
this.actor = actor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getGrade() {
return grade;
}
public void setGrade(double grade) {
this.grade = grade;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
}
常见数据结构
数据结构概述、栈、队列
- 数据结构是计算机底层储存、组织数据的方式。是指数据互相之间是以什么方式排列在一起的
- 通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
栈
- 后进先出,先进后出
队列
- 先进先出,后进后出
数组
- 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同(元素在内存中是连续存储的)
- 删除效率低:要将原始数据删除,同时后面每个数据前移
- 增加效率极低:添加位置后的每个数据后移,在添加元素
链表
- 链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址
- 链表中的元素是游离存储的,每个元素节点包含数据值和下一个元素的地址
- 链表查询慢。无论查询哪个数据都要从头开始
- 链表增删相对快
二叉树、二叉查找树
- 只能有一个根节点,每个节点最多支持2个直接子节点。
- 节点的度:节点拥有的子树的个数,二叉树的度不大于2叶子节点度为0的节点,也称之为终端结点。
- 高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
- 层:根节点在第一层,以此类推
- 兄弟节点:拥有共同父节点的节点互称为兄弟节点
平衡二叉树
- 任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树
平衡二叉树-旋转的四种情况
左边高右边拉,右边高左边拉
- 左左
- 左右 先左旋在右旋
- 右右
- 右左 先右旋在左旋
红黑树
- 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
- 1972年出现,当时被称之为平衡二叉B树。1978年被修改为如今的"红黑树"。
- 每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。
- 红黑规则:
- 每一个节点或是红色的,或者是黑色的,根节点必须是黑色。
- 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,叶节点是黑色的。
- 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
红黑树增删改查的性能都很好
List系列集合
List集合特点、特有API
- ArrayList、LinekdList:有序、可重复、有索引。
- 有序:存储和取出的元素顺序一样
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
List特有方法:
-
List集合因为支持索引,所以有很多索引操作的独特api,其他Collection的功能List也都继承了
方法名称 说明 void add(int index,E element) 在此集合中的指定位置插入指定的元素 E remove(int index) 删除指定索引处的元素,返回被删除的元素 E set(int index,E element) 修改指定索引出的元素,返回被修改元素 E get(int index) 返回指定索引处的元素
List集合的遍历方式小结
for循环:
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
System.out.println(ele);
}
迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
foreach
for (String s : list) {
System.out.println(s);
}
Lambda
// list.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
list.forEach(s -> System.out.println(s));
ArrayList集合的底层原理
- ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
- 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
LinkedList集合的底层原理
public class Test {
public static void main(String[] args) {
//栈
LinkedList<String> stack = new LinkedList<>();
// 压栈、入栈 push
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack);
//出栈,弹栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
//队列
LinkedList<String> queue = new LinkedList<>();
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue);
//出队
System.out.println(queue.getFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
}
}
补充知识:集合的并发修改异常问题
迭代器
while (it.hasNext()){
String ele = it.next();
if ("Java".equals(ele)){
it.remove();
}
}
for循环
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if ("Java".equals(ele)){
list.remove(i);
i --;
}
}
for (int i = list.size()-1; i >=0; i--) {
String ele = list.get(i);
if ("Java".equals(ele)){
list.remove(i);
}
}
补充知识:泛型深入
泛型的概述和优势
泛型概述
- 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
- 泛型的格式:<数据类型>;注意:泛型只能支持引用数据类型。
- 集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的好处
- 统一数据
- 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来
泛型可以在很多地方进行定义
- 类后面 泛型类
- 方法申明 泛型方法
- 接口后面 泛型接口
自定义泛型类
-
定义类时同时定义了泛型的类就是泛型类
-
泛型类的格式:修饰符class 类名<泛型变量>{}
范例: public class MyArrayList<T>{ }
-
此处泛型类变量T可以随便写为任意标识,常见的入E、T、K、V等
-
作用:编译阶段可以指定数据类型,类似于集合的作用
-
原理:把出现泛型变量的地方全部换成传输的真实数据类型
自定义泛型方法
-
定义方法时同时定义了泛型的方法就是泛型方法
-
格式 : 修饰符<泛型变量>方法返回值 方法名(形参列表){}
public <T> void show(T t){ }
-
作用:方法中使用泛型方法可以接收一切实际类型的参数,方法更具备通用性
-
原理:把出现泛型变量的地方全部换成传输的真实数据类型
自定义泛型接口
-
使用泛型定义的接口就是泛型接口
-
格式: 修饰符 interface Date<泛型变量>
public interface Date<E>{ }
-
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
-
原理:实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。
泛型通配符、上下限
通配符 ?
- ?可以在“使用泛型”的时候代表一切类型。
- ETKV是在定义泛型的时候使用的。
上下限
- ? extends Car: ?必须是Car或者其子类 泛型上限
- ? super Car: ?必须是Car或者其父类 泛型下限ا