Java基础学习-4
121~176
static
static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量
被static修饰的成员变量叫做静态变量
特点:被该类所有对象共享;不属于对象,属于类;随着类的加载而加载,优先于对象存在
调用方式:类名调用(推荐)、对象名调用
被static修饰的成员方法叫做静态方法
特点:多用在测试类和工具类(帮助我们做一些事情的,但是不描述任何事物的类)中,javabean类中很少会用
调用方式:类名调用(推荐),对象名调用
小练习
需求:请按照如下要求编写一个数组的工具类:ArrayUtil
提供一个工具类方法printArr,用于返回整数数组的内容
返回的字符串格式如:[10,20,50,34,100](只考虑整数数组,且只考虑一维数组)
提供这样一个工具方法getAverage,用于返回平均分(只考虑浮点型数组,且只考虑一维数组)
定义一个测试类TestDemo,调用该工具方法,并返回结果。
public class ArrayUitl{
private ArrayUtil(){}
public static String printArr(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(int i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum/arr.length;
}
]
int[] arr1={1,2,3,4,5};
String str = ArrayUtil.printArr(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);
注意事项
静态方法只能访问静态变量和静态方法
非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
静态方法中是没有this关键字
重新认识main方法
public:被JVM调用,访问权限足够大
static:被JVM调用,不用创建对象,直接类名访问
因为main方法是静态的,所以测试类中其他方法也需要是静态的
void:被JVM调用,不需要给JVM返回值
main:一个通用的名称,虽然不是关键字,但是被JVM识别
String[] args:以前用于接受键盘录入数据的,现在没用
String[] args
[]:数组
Stirng:数据类型
args:数组名
继承
封装小回顾
对象代表什么,就得封装对应的数据,并提供数据对应的行为
继承是面向对象的三大特征之一,可以让类跟类之间产生子父的关系
可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性
继承的格式:
java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
public class Student extends Person {}
Student称为子类(派生类),Person称为父类(基类或超类)
使用继承的好处:
可以把多个子类中重复的代码抽取到父类中了,提高代码的复用性。
子类可以在父类的基础上,增加其他的功能,使子类更强大
什么时候用继承:
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码
继承后子类的特点
子类可以得到父类的属性和行为,子类可以使用
子类可以在父类的基础上新增其他功能,子类更强大
继承的特点:
java只支持单继承,不支持多继承,但支持多层继承
单继承:一个子类只能继承一个父类
不支持多继承:子类不能同时继承多个父类
多层继承:子类A继承父类B,父类B可以继承父类C(祖宗三代,直接父类和间接父类)
每一个类都直接或间接地继承于Object(继承体系)
小练习
需求:现在有四种动物:布偶猫、中国狸花猫、哈士奇、泰迪
暂时不考虑属性,只考虑行为。
请按照继承地思想特点进行继承体系的设计。
四种动物分别有以下的行为:
布偶猫:吃饭、喝水、抓老鼠
中国狸花猫:吃饭、喝水、抓老鼠
哈士奇:吃饭、喝水、看家、拆家
泰迪:吃饭、喝水、看家、蹭一蹭
public class Animal {
public void eat(){
System.out.println("吃东西");
}
public void drink(){
System.out.println("喝水");
}
}
public class Cat extends Animal{
public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗在看家");
}
}
public class Ragdoll extends Cat{
}
public class lihua extends Cat{
}
public class Husky extends Dog{
public void breakHome(){
System.out.println("哈士奇在拆家");
}
}
public class Teddy extends Dog{
public void touch(){
System.out.println("泰迪又在蹭我的腿了~");
}
}
public class Test{
public static void main(String[] args){
//创建对象并调用方法
}
}
注意事项
子类只能访问父类中非私有的成员
子类到底能继承父类中的哪些内容(第126集)***
构造方法----非私有(不能)----private(不能)
成员变量----非私有(能)----private(能)
成员方法----非私有(能)----private(能)
继承过程中成员变量和成员方法的访问特点
继承中:成员变量的访问特点:
就近原则:谁离我近,我就用谁
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
public class Test{
public static void main(String[] args){
Zi z = new Zi();
z.ziShow();
}
}
class Fu {
String name = "Fu";
}
class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name="ziShow";
System.out.println(name);//ziShow
System.out.println(this.name);//Zi
System.out.println(super.name);//Fu
}
}
如果出现重名成员变量怎么办
System.out.println(name);//从局部位置开始网上找
System.out.println(this.name);//从本类成员位置开始往上找
System.out.println(super.name);//从父类成员位置开始往上找
继承中成员方法的访问特点:
直接调用满足就近原则:谁离我近,我就用谁
super调用,直接访问父类
父类的重写
当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
@Override重写注解
1、@Override是放在重写后的方法上,校验子类重写时语法是否正确
2、加上注解后如果有红色波浪线,表示语法错误
3、建议重写方法都加@Override注解,代码安全,优雅!
方法重写注意事项和要求
1、重写方法的名称、形参列表必须与父类中的一致
2、子类重写父类方法时,访问权限子类必须大于等于父类(暂时不了解:空着不写<protected<public)
3、子类重写父类方法时,返回值类型子类必须小于等于父类
4、建议:重写的方法尽量和父类保持一致
5、只有被添加到虚方法表中的方法才能被重写
小练习
需求:现在有三种动物:哈士奇、沙皮狗、中华田园犬
暂时不考虑属性,只考虑行为
请按照继承的思想特点进行继承体系的设计
三种动物分别有以下的行为:
哈士奇:吃饭(吃狗粮)、喝水、看家、拆家
沙皮狗:吃饭(吃狗粮、吃骨头)、喝水、看家
中华田园犬:吃饭(吃剩饭)、喝水、看家
public class Dog{
public void eat(){
System.out.println("狗在吃狗粮");
}
public void drink(){
System.out.println("狗在喝水");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
public class Husky extends Dog{
public void breakHome(){
System.out.println("哈士奇又在拆家了");
}
}
public SharPei extends Dog{
@Override
public void eat(){
super.eat();
System.out.println("狗啃骨头");
}
}
public class ChineseDog extends Dog{
@Override
public void eat(){
System.out.println("吃剩饭");
}
}
public class DogTest{
public static void main(Stirng[] args){
Husky h = new Husky();
h.eat();
h.drink();
h.lookHome();
h.breakHome();
ChineseDog cd = new ChineseDog();
cd.eat();
cd.drink();
cd.lookHome();
}
}
小总结
1、继承中成员方法的访问特点
this调用:就近原则。
super调用:直接找父类
2、什么是方法重写
在继承体系中,子类出现了和父类中一模一样的方法声明,
我们就称子类的这个方法是重写的方法。
3、方法重写建议加上哪个注解,有什么好处
@Override注解可以校验重写是否正确,同时可读性好。
4、重写方法有哪些基本要求
子类重写的方法尽量跟父类中的方法保持一致
只有虚方法表里面的方法可以被重写
5、方法重写的本质
覆盖虚方法表中的方法
构造方法的特点
父类中的构造方法不会被子类继承
子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完全初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
怎么调用父类构造方法的?
子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行
如果想调用父类有参构造,必须手动写super进行调用
继承中构造方法的访问特点是什么?
子类不能继承父类的构造方法,但是可以通过super调用
子类构造方法的第一行,有一个默认的super();
默认先访问父类中无参的构造方法,再执行自己。
如果想要方法文父类有参构造,必须手动书写
this、super使用总结
this:理解为一个变量,表示当前方法调用者的地址值
super:代表父类存储空间
小练习
需求:带有继承结构的标准javabean类
1、经理
成员变量:工号,姓名,工资,管理奖金
成员方法:工作(管理其他人),吃饭(吃米饭)
2、厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
标准的javabean的特征
1、类名见名知意
2、所有的成员变量都需要私有
3、构造方法(空参 带全部参数的构造)
4、get/set
public class Employee{
private String id;
private String name;
private double salary;
public Employee(){}
public Emoloyee(String id,String name,double salary){
this.id=id;
this.name=name;
this.salary=salary;
}
public String getId(){
return id;
}
public void setId(String id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public double getsalary(){
return salary;
}
public void setSalary(double salary){
this.salary=salary;
}
public void work(){
System.out.println("员工在工作");
}
public void eat(){
System.out.println("吃米饭");
}
}
public class Manager extends Employee{
private double bouns;
//空参构造
public Manage(){}
//带全部参数的构造
//父类+子类
public Manager(String id,String name,double salary,double bouns){
super(id,name,salary);
this.bouns=bouns;
}
public double getBouns(){
return bouns;
}
public void setBouns(double bouns){
this.bouns=bouns;
}
@Override
public void work(){
System.out.println("管理其他人");
}
}
public 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 Test{
public static void main(String[] args){
Manager m = new Manager(id:"heima001",name:"zhangsan",salary:15000,bouns:8000);
System.out.println(m.getId()+","+m.getName()+","+m.getSalary()+","+m.getBouns);
m.work();
m.eat();
Cook c = new Cook();
c.setId("heima002");
c.setName("lisi");
c.setSalary(8000);
System.out.println(c.getId()+","+c.getName()+","+c.getSalary());
c.work();
c.eat();
}
}
多态
封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为
多态就是数据的多种形态
根据传递对象的不同,调用不同的show方法
什么是多态
同类型的对象,表现出的不同形态
对象的多种形态
多态的表现形式
父类类型 对象名称 = 子类对象;
多态前提
有继承关系
有父类引用指向子类对象
有方法重写
多态的好处
使用父类型作为参数,可以接受所有子类对象
体现多态的拓展性与便利
多态调用成员的特点
变量调用:编译看左边,运行也看左边
编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
方法调用:编译看右边
编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败
运行看右边:java运行代码的时候,实际运行的是子类中的方法。
理解:
Animal a = new Dog();
现在用a去调用变量和方法
而a是Animal类型的,所以默认都会从Animal这个类中去找
成员变量:在子类的对象中会把弗雷德成员变量也继承下去的。父:name 子:name
成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
多态的优势和弊端
优势:
在多态形势下,右边对象可以实现解耦合,便于拓展和维护
定义方法的时候,使用父类型作为参数,可以接受所有子类对象,体现多态的拓展性与便利
弊端
不能调用子类的特有功能
报错的原因?
当调用成员方法的时候,编译看左边,运行看右边
那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错
解决方案
变回子类类型就可以了
细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
a instanceof Dog
判断a是不是Dog类型的
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c= (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
新特性
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
小总结
多态的优势
方法中,使用父类型作为参数,可以接受所有子类对象
多态的弊端是什么
不能使用子类的特有功能
引用数据类型的类型转换,有几种方式
自动类型转换、强制转换类型
强制类型转换能解决什么
可以转换成真正的子类类型,从而调用子类独有的功能
转换类型与真实对象类型不一致会报错
转换的时候用instanceof关键字进行判断
包
包就是文件夹。用来管理各种不同功能的java类,方便后期代码维护。
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。com.itheima.domain
使用其他类的规则
使用同一个包中的类时,不需要导包。
使用java.lang包中的类时,不需要导包
其他情况都需要导包
如果同时使用两个包中的同名类,需要用全类名。
什么是全类名?
包名+类名
什么时候需要导包?什么时候不需要导包?
使用同一个包中的类时,不需要导包
使用java.lang包中的类时,不需要导包。
其他情况都需要导包
如果同时使用两个包中的同名类,需要用全类名。
final
final修饰方法
表明该方法是最终方法,不能被重写
final修饰类
表明该类是最终类,不能被继承
final修饰变量
叫做常量,只能被赋值一次
关于常量
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
常量的命名规范
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
细节:
final修饰的的变量是基本类型:那么变量存储的数据值不能发生变化
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变
权限修饰符
权限修饰符:是用来控制一个成员能够被访问的范围的
可以修饰成员变量,方法,构造方法,内部类。
private----私房钱,只能自己用
默认----只能本包中能用
protected----受保护的
public----公共的
权限修饰符的使用规则
实际开发中,一般只用private和public
成员变量私有
方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
代码块
局部代码块、构造代码块、静态代码块
局部代码块
提前结束变量的生命周期(已淘汰)
构造代码块
抽取构造方法中的重复代码(不够灵活)
1、写在成员位置的代码块
2、作用:可以把多个构造方法中重复的代码抽取出来
3、执行时机:我们创建本类对象的时候会先执行构造代码块再去执行构造方法
静态代码块
数据初始化(重点)
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
抽象类和抽象方法
抽象方法:将共性的行为(方法)抽取到父类之后
由于每一个子类执行的内容是不一样的
所有,在父类中不能确定具体的方法体
该方法就可以定义为抽象方法
抽象类:如果一个类中存在抽象方法,那么该类就必须申明为抽象类
抽象方法的定义格式:public abstract 返回值类型 方法名(参数列表)
抽象类的定义格式:public abstact class 类名{}
抽象类和抽象方法的注意事项
抽象类不能实例化
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
可以有构造方法
抽象子类(要么重写抽象类中的所有抽象方法,要么是抽象类)
小练习
需求:编写带有抽象类的标准javavean类
青蛙----属性:名字,年龄----行为:吃虫子,喝水
狗----属性:名字,年龄----行为:吃骨头,喝水
山羊----属性:名字,年龄----行为:吃草,喝水
public abstract class Animal{
private String name;
private int age;
public Animal(){}
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
public void drink(){
System.oyt.println("动物在喝水");
}
public abstract void eat();
}
public class Frog extends Animal{
public Frog(){}
public Frog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("青蛙在吃虫子");
}
}
public class Dog extends Animal{
public Dog(){
}
public Dog(Stirng name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
public class Sheep extends Animal{
public Sheep(){}
public Sheep(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("山羊在吃草");
}
}
public class Test{
public static void main(Stirng[] args){
Frog f = new Frog(name:"小绿",age:1);
System.out.println(f.getName()+","+f.getAge());
f.drink();
f.eat();
}
}
抽象类和抽象方法的意义
疑问:
把子类中共性的内容抽取到父类之后
由于方法体不确定,需要定义为抽象。子类使用时需要重写
那么我不抽取到父类,直接在子类写不是更节约代码?
强制子类必须按照这种格式进行重写
小结
1、抽象类的作用是什么样的?
抽取共性是,无法确定方法体,就把方法定义为抽象的。
强制让子类按照某种格式重写
抽象方法所在的类,必须是抽象类
2、抽象类和抽象方法的格式
public abstract 返回值类型 方法名(参数列表);
public abstract class 类名{}
3、继承抽象类有哪些要注意?
要么重写抽象类中的所有抽象方法
要么是抽象类
接口
接口就是一种规则,是对行为的抽象
接口的定义和使用
接口用关键字interface来定义
public interface 接口名{}
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
注意1:接口和类的实现关系,可以单实线,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口。
public class 类名 extends 父类 implements 接口名1,接口名2{}
小练习
需求:编写带有接口和抽象类的标准Javabean类
青蛙----属性:名字、年龄----行为:吃虫子、蛙泳
狗----属性:名字、年龄----行为:吃骨头、狗刨
兔子----属性:名字、年龄----行为:吃胡萝卜
public abstract class Animal{
private String name;
private int age;
public Animal(){}
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
public abstract void eat();
}
public interface Swim{
public abstract void swim();
}
public class RAbbit extends Animal{
public Rabbit(){}
public Rabbit (String name,int age){
super(name,age);
}
@Overide
public void eat(){
Syste.out.println("兔子在吃胡萝卜");
}
]
public class Frog extends Animal implements Swim{
public Frog(){}
public Frog(String name ,int age){
suoer(name,age);
}
@Override
public void eat(){
System.oyt.println("青蛙在吃虫子");
}
public void swim(){
System.out.println("青蛙在蛙泳");
}
}
public class Dog extends Animal implements Swim{
public Dog(){}
public Dog(String name ,int age){
suoer(name,age);
}
@Override
public void eat(){
System.oyt.println("狗在吃骨头");
}
public void swim(){
System.out.println("狗刨");
}
}
public class Test{
public static void main(String[] args){
Frog f = new Frog(name:"小青",age:1);
System.out.println(f.getName()+","+f.etAge());
f.eat();
f.swim();
Rabbit r = new Rabbit (name:"小白",age:2);
System.out.println(r.getName()+","+r.getAge());
r.eat();
}
}
接口中成员的特点
成员变量
只能是常量
默认修饰符:public static final
构造方法:没有
成员方法:只能是抽象方法。默认修饰赋:public abstract
接口和类之间的关系
类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
类和接口之间的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系
继承关系,可以单继承,也可以多继承(如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法)
接口中新增方法
JDK7以前:接口中只能定义抽象方法
JDK8的新特性:接口中可以定义有方法。(默认、静态)
允许在接口中定义默认方法,需要使用关键字default修饰(解决接口升级的问题)
接口中默认方法的定义格式:
格式:public defalut 返回值类型 方法名(参数列表){}
范例:public defalut void show(){}
接口中默认方法的注意事项
默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
public可以省略,default不能省略
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
JDK9的新特性:接口中可以定义私有方法。
接口应用
接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
当一个方法的参数就是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态
适配器设计模式
当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
书写步骤:
编写中间类XXXAdapter,实现对应的接口
对接口中的抽象方法进行空实现
让真正的实现类继承中间类,并重写需要用的方法
为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
内部类
类的五大成员:
属性、方法、构造方法、代码块、内部类
什么是内部类?
在一个类的里面,再定义一个类
public class Car {//外部类
String carName;
int carAge;
int carColor;
class Engine{//内部类
String engineName;
int engineAge;
}
}
内部类表示的事物是外部类的一部分
内部类单独出现没有任何意义
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
什么时候用到内部类:
B类表示的事物是A类的一部分,且B单独存在没有意义
比如:汽车的发动机,ArratList的迭代器,人的心脏等等
内部类的分类
成员内部类
静态内部类
局部内部类
匿名内部类
成员内部类
写在成员位置的,属于外部类的成员
成员内部类可以被一些修饰符所修饰,比如:private,默认,protected,public,static等
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量
public class Car{//外部类
String carName;
int carAge;
int carColor;
class Engine{//成员内部类
String engineName;
int engineAge;
}
}
获取成员内部类对象
方式一:在外部类中编写方法,对外提供内部类的对象
public class Outer{
String name;
private class Inner{
}
public Inner getInstance(){
return new Inner();
}
}
方式二:直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象
范例:Outer.Inner oi = new Outer().new Inner();
小练习
class Outer{
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(??); //10
System.out.println(??); //20
System.out.println(??); //30
}
}
}
class Outer{
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(Outer.this.a); //10
System.out.println(this.a); //20
System.out.println(a); //30
}
}
}
小总结
1、内部类的分类?
成员内部类、静态内部类、局部内部类、匿名内部类
2、什么是成员内部类?
写在成员位置的,属于外部类的成员。
3、获取成员内部类对象的两种方式?
方式一:当成员内部类被private修饰时。
在外部类编写方法,对外提供内部类对象
方式二:当成员内部类被非私有修饰时,直接创建对象。
Outer.Inner oi = new Outer().new Inner();
4、外部类成员变量和内部类成员变量重名时,在内部类如何访问?
System.out.println(Outer.this.变量名);
静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
public class Car{//外部类
String carName;
int carAge;
int carColor;
static class Engine{//静态内部类
String engineName;
int engineAge;
}
}
创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
局部内部类
1、将内部类定义在方法里面就叫做局部内部类,类似方法里面的局部变量
2、外界是无法直接使用,需要在方法内部创建对象并使用。
3、该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
匿名内部类
匿名内部类本质上就是隐藏了名字的内部类
new 类名或者接口名(){
重写方法;
}
new Swim{
@Override
public void swim (){
System.out.println("重写了游泳的方法")
}
}
1、把前面的class删掉,剩余的内容就变成了一个没有名字的类
2、这个没有名字的类想要实现Swim接口。
把Swim写在了大括号的前面,表示这个没有名字的类实现了Swim接口,所以需要在类中重写接口里面所有的抽象方法。
3、还想要创建这个没有名字的类的对象,该怎么办?
在测试类中调用匿名内部类
要自己写一个子类继承类
再创建子类的对象,传递给方法
整体我们可以理解为Swim接口的实现类对象
接口多态
Swim s = new Swim(){
@Override
public void swim(){
System.out.println("重写之后游泳的方法");
}
}
//编译看左边,运行看右边的原则
s.swim();
小总结
1、什么是匿名内部类?
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
2、匿名内部类的格式
new 类名或者接口名(){
重写方法;
};
3、格式的细节
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
4、使用场景
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,
如果实现类只要使用一次,就可以用匿名内部类简化代码。
常见APL
记一下类名和类的作用
养成查询APL帮助文档的习惯
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 int min(int a , int b)–获取两个int值中的较小值
public static double pow(double a,double b)----返回a的b次幂
public static double random()----返回值为double的随机值,范围为[0.0,1.0 )
abs函数有bug
以int类型为例,取值范围:-2147483648~2147483647
如果没有正数与负数对应,那么传递负数结果有误
·-2147483648没有正数与之对应,所以abs结果产生bug
System.out.println(Math.abs(-2147483648));//-2147483648
System.out.println(Math.abs(-2147483647));//2147483647
面对此bug可以使用absExact函数,当发生上述的bug时他会报出一个错误
pow函数的小细节:
如果第二个参数0~1之间的小数
System.out.println(Math.pow(4,0.5))
建议:
第二个参数:一般传递大于等于1的正整数
Math.sqrt()开根号
Math.cbrt()开立方根
小总结
1、Math:帮助我们进行数学计算的工具类
2、里面的方法都是静态的。
3、常见方法如下:
abs:获取绝对值
absExact:获取绝对值
ceil:向上取整
floor:向下取整
round:四舍五入
max:获取最大值
min:获取最小值
pow:获取a的b次幂
sqrt:开平方根
cbrt:开立方根
random:获取[0.0~1.0)之间的随机数
小练习
需求:判断一个数是否为质数(优化版)
public class MathDemo2{
public static void main(String[] args){
System.out.println(isPrime(number:13));
System.out.println(isPrime(number:10));
System.out.println(isPrime(number:997));
}
public static boolean isPrime(int number){
for(int i=0;i<=Math.sqrt(number);i++){
if(number%i==0){
return false;
}
}
return true;
}
}
需求:自幂数,一个n位自然数等于自身各个数位上数字的n次幂之和
举例1:三位数 13+53+33=153
举例2:四位数 14+64+34+43=1634
如果自幂数是一位数,也叫做独身数
三位自幂数:水仙花数
四位自幂数:四叶玫瑰花数
五位自幂数:五角星数
六位自幂数:六合数
七位自幂数:北斗七星数
八位自幂数:八仙数
九位自幂数:九九重阳数
十位自幂数:十全十美数
public class MathDemo3{
public static void main(String[] args){
//要求1:统计一共有多少个水仙花数。
//水仙花数:100~999
//得到每一个三位数
int count = 0;
for(int i=100;i<=999;i++){
int ge = i%10;
int shi = i/10%10;
int bai = i/100%10;
//判断:
//每一位的三次方之和跟本身进行比较。
double sum = Math.pow(ge,3)+Math.pow(shi,3)+Math.pow(bai,3);
if(sum==i){
count++;
//System.out.println(i);
}
}
System.out.println(count);
}
}
System
System也是一个工具类,提供了一些与系统相关的方法
public static void exit(int status)----终止当前运行的java虚拟机
public static long currentTimeMillis()----返回当前系统的时间毫秒值形式
public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数)----数组拷贝
计算机中的时间原点
1970年1月1日 08:00:00
原因:
1969年8月,贝尔实验室的程序员肯汤普逊利用妻儿离开的一个月的机会。开始着手创造了一个全新的革命性的操作系统。他使用B编译语言在老旧的PDP-7机器上开发出了Unix的一个版本。随后,汤普逊和同事丹尼斯里奇改进了B语言,开发了C语言,重写UNIX。
1970年一月一日算c语言的生日
1秒=1000毫秒
1毫秒=1000微秒
1微秒=1000纳秒
//方法的形参:
//状态码:
//0:表示当前虚拟机是正常停止
//非0:表示当前虚拟机异常停止
System.exit(status:0);
System.out.println("看看我执行了吗");//没执行,虚拟机停止运行
long l = System.out.currentTimeMillis();
System.out.println(l);//打印出来的值是从时间原点到现在的运行代码的时间点通过的毫秒数
int[] arr1={1,2,3,4,5,6,7,8,9,10};
int[] arr2=new int[10];
//把arr1数组中的数据拷贝到arr2中
//参数一:数据源,要拷贝的数据从那个数组而来
//参数二:从数据源数组中的第几个索引开始拷贝
//参数三:目的地,我要把数据拷贝到哪个数组中
//参数四:目的地数组的索引
//参数五:拷贝的个数
System.arraycopy(arr1,srcPos:0,arr2,desPos:0,length:5);
for(int i=0;i<arr2.length;i++){
System.out.println(arr2[i]+" ");//1 2 3 4 5 0 0 0 0 0
}
关于arraycopy的细节
1、如果数据源数组和目的地数组都是基本数据类型,那么两者的类型必须保持一致,否则会报错
2、在拷贝的时候需要考虑数组的长度,如果超出范围也会报错
3、如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型
小总结
1、System:也是一个工具类,提供了一些与系统相关的方法
2、时间的原点:1970年1月1日0:00:00,我国在东八区,有八小时时差。
3、1秒=100毫秒
4、常见方法如下
exit:停止虚拟机
currentTimeMills:获取当前时间的毫秒值
arraycopy:拷贝数组
Runtime
Runtime表示当前虚拟机的运行环境
这个类里面的方法不是静态的,所以想要调用方法就要先获取到Runtime的对象,但是有一个小细节,这个类的对象我们不能直接用而是通过一个静态方法获取
public static Runtime getRuntime()----当前系统的运行环境对象
public void exit(int status)----停止虚拟机
public int availableProcessors()----获得CPU的线程数
public long maxMemory()----JVM能从系统中获取总内存大小(单位byte)
public long totalMemory()----JVM已经从系统中获取总内存大小(单位byte)
public long freeMemory()----JVM剩余内存大小(单位byte)
public Process exec(String command)----运行cmd命令
Runtime r1 = Runtime.getRuntime();
Runtime r1 = Runtime.getRuntime();
r1.exit(status:0);//0是正常停止,1是异常停止
System.out.println(Runtime.getRuntime().availableProcessors());
System.out.println(Runtime.getRuntime().maxMemory());
System.out.println(Runtime.getRuntime().totalMemory());
System.out.println(Runtime.getRuntime().freeMemory());
Runtime.getRuntime().exec(command:"notepad");//打开记事本
cmd有趣操作
shutdown:关机
加上参数才能执行
-s:默认在1分钟之后关机
-s -t:指定时间:指定关机时间
-a:取消关机操作
-r:关机并重启
Object
Object是java中的顶级父类。所有的类都直接或间接的继承于Object类
Object类中的方法可以被所有子类访问,所以我们要学习Object类和其中的方法
构造方法
public Object()----空参构造
成员方法
public String toString()----返回对象的字符串表示形式
public boolean equals(Object obj)----比较两个对象是否相等
protected Object clone(int a)----对象克隆
toString----返回对象的字符串表示形式
细节:
System:类名
out:静态变量
System.out:获取打印的对象
println():方法
参数:表示打印的内容
核心逻辑:
当我们打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串。
然后再打印在控制台上,打印完毕换行
思考:默认情况下,因为Object类中的toString方法返回的是地址值
所以,默认情况下,打印一个对象打印的就是地址值
但是地址值对于我们是没什么意义的?
我想要看到对象内部的属性值?我们该怎么办?
处理方案:重写父类Object类中的toString方法
toString方法的结论:
如果我们打印一个对象,想要看到属性值的话,那么就重写toString方法就可以了
在重写的方法中,把对象的属性值进行拼接。
equals方法的结论:
1、如果没有重写equals方法,那么默认使用Object中的方法进行比较,比较的是地址值是否相等
2、一般来说地址值对于我们意义不大,所以我们会重写,重写之后比较的就是对象内部的属性值了
public class ObjectDemo3{
public static void main(String[] args){
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
System.out.println(s.equals(sb));//false
//因为equals方法是被s调用的,而s是字符串
//所以equals要看String类中的
//字符串中的equals方法,先判断参数是否为字符串
//如果是字符串,再比较内部的属性
//但是如果参数不是字符串,直接返回false
System.out.println(sb.equals(s));//false
//因为equals方法是被sb调用的,而sb是StringBuilder
//所以这里的equals方法要看StringBuilder中的equals方法
//那么在StringBuilder当中,没有重写equals方法
//使用的是Object中的
//在Object当中默认是使用==号比较两个对象的地址值
//而这里的s和sb记录的地址值是不一样的,所以结果返回false
}
}
对象克隆
把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制
public class User{
int id;//角色
String username;//用户名
String password;//密码
String path;//游戏图片
int[] data={...};//游戏进度
}
//当游戏人数过多时就需要进行分区
//电信一区:艾欧尼亚
//电信二区:祖安
//电信三区:诺克萨斯
//电信四区:战争学院
//当需要账号转移的时候就需要用到对象克隆
//Cloneable
//如果当前的接口是一个标志性接口
//现在Cloneable表示一旦实现了,那么当前类的对象就可以被克隆
//如果没有实现,当前类的对象就不能克隆
public class User imlements Cloneable{
private int id;
private String username;
private String password;
private String path;
private int[] data;
public User(){
}
public User(int id,String username,String password,String path,int[] data){
this.id=id;
this.username=username;
this.password=password;
this.path=path;
this.data=data;
}
public int getId(){return id;}
public void (int id){this.id=id;}
public String getUsername(){return username;}
public void setUsername(String username){this.username=username;}
public String getPassword(){this.password=password;}
public String setPassword(String password){this.password=password;}
public String getPath(){return path;}
public void setPath(String path){this.path=path;}
public int[] getDate(){return data;}
public void setDate(int[] data){this.data=data;}
public String toString(){
return "角色编号:"+id+",用户名:"+username+",密码"+password+",游戏图片"+path+""+arrToString;
}
public String arrToString(){
StringJoiner sj = new StringJoiner(delimiter:",",prefix:"[",suffix:"]");
for(int i=0;i<data.length;i++){
sj.add(data[i]+" ");
}
return sj.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException{
//调用父类中的clone方法
//相当于让java帮我们克隆一个对象,并把克隆之后的对象返回出去。
return super.clone();
}
}
public class ObjectDemo4{
public static void main(String[] args) throws CloneNotSupprotedException{
//1、先创建一个对象
int[] data={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
User u1 = new User{id:1,username:"zhangsan",password:"1234qwer",path:"girl11",data};
//2、克隆对象
//细节:
//方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。
//书写细节:
//1、重写Object中的clone方法
//2、让javabean类实现Cloneable接口
//3、创建原对象并调用clone就可以了。
User u2 =(User) u1.clone();
System.out.println(u1);
System.out.println(u2);
}
}
浅克隆:不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
深克隆:基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的
深克隆小工具(第三方工具)
1、第三方写的代码导入到项目中
2、编写代码(把对象变成一个字符串,再把字符串变回对象就可以了)
小总结
1、Object是java中的顶级父类
所有的类都直接或者间接的继承于Object类。
2、toString():
一般会重写,打印对象时打印属性
3、equals():
比较对象时会重写,比较对象属性值是否相同
4、clone():
默认浅克隆。
如果需要深克隆需要重写方法或者使用第三方工具类
Objects
Objects是一个工具类,提供了一些方法去完成一些功能
public static boolean equals(Object a,Object b)----先做非空判断,比较两个对象
public static boolean isNull(Object obj)----判断对象是否为null,为null返回true,反之
public static boolean nonNull(Object obj)----判断对象是否为null,跟isNull的结果相反
boolean result = Object.equals(s1,s2);
System.out.println(result);
//细节:
//1、方法的底层会判断s1是否为null,如果为null,直接返回false
//2、如果s1不为null,那么就利用s1再次调用equals方法
//3、此时s1是Student类型,所以最终还是会调用Student中的equals方法。
//如果没有重写,比较地址值,如果重写了,就比较属性值。
小总结
1、Objects是一个对象工具类,提供一些操作对象的方法
2、equals(对象1,对象2):先做非空判断,比较两个对象
3、isNull(对象):判断对象是否为空
4、nonNull(对象):判断对象是否不是空
BigInteger和BigDecimal
BigInteger的构造方法
在Java中,整数有四种类型:byte,short,int,long
在底层占用字节个数:byte1个字节、short2个字节、int4个字节、long8个字节。
public BigInteger(int num,Random rnd)----获取随机大整数,范围[0~2的num次方-1]
public BigInteger(String val)----获取指定的大整数
public BigInteger(String val, int radix)----获取指定进制的大整数
public static IbgInteger valueOf(long val)----静态方法获取BigInteger的对象,内部有优化
细节:对象一旦创建,内部记录的值不能发生改变
//获取一个随机的大整数
Random r = new Random();
BigeInteger bd1 = new BigInteger (numBits:4,r);
System.out.println(bd1);//0~2^4-1
//获取一个指定的大整数
//细节:字符串中必须是整数,否则会报错
BigInteger bd2 = new BigInteger(val:"99999999999999999999");
System.out.println(bd2);
//获取指定进制的大整数
//细节:
//1、字符串中的数字必须是整数
//2、字符串中的数字必须要跟进制吻合
//比如二进制中,那么只能写0和1,写其他的就报错。
BigInteger bd3 = new BigInteger(bal:"1111",radix:2);
System.out.println(bd4);
//细节:
//1、能表示的范围比较小,在long的取值范围内,如果超出long的范围就不行了
//2、在内部对常用的数字:-16~16进行了优化。
//提前把-16~16先创建好BigInteger的对象,如果多次获取不会重新创建新的
BigInteger bd5 = new BigInteger.valueOf(100);
System.out.println(bd5);//100
BigInteger bd6 = new BigInteger.valueOf(16);
BigInteger bd7 = new BigInteger.valueOf(16);
System.out.println(bd6==bd7);//true
BigInteger bd8 = new BigInteger.valueOf(17);
BigInteger bd9 = new BigInteger.valueOf(17);
System.out.println(bd8==bd9);//false
BigInteger bd10 = BigInteger.valueOf(1);
BigInteger bd11 = BigInteger.valueOf(2);
BigInteger result = bd10.add(bd11);
System.out.println(result);//3
//此时,不会修改参与计算的BigInteger对象中的值,而是产生了一个新的BigInteger对象记录3
小总结
1、如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取
2、如果BigInteger表示的超出long的范围,可以用构造方法获取
3、对象一旦创建,BigInteger内部记录的值不能发生改变
4、只要进行计算都会产生一个新的BigInteger对象
BigInteger常见成员方法
public BigInteger add(BigInteger val)----加法
public BigInteger subtract(BigInteger val)----减法
public BigInteger multiply(BigInteger val)----乘法
public BigInteger divide(BigInteger val)----除法,获取商
public BigInteger[] divideAndRemainder(BigInteger val)----除法,获取商和余数
public Boolean equals(BigInteger val)----比较是否相同
public BigInteger equals(BigInteger val)----次幂
public BigInteger max/min(BigInteger val)----返回较大值/较小值
public int intValue(BigInteger val)----转为int类型整数,超出范围数据有误
BigInteger bd1 = BigInteger.valueOf(10);
BigInteger bd2 = BigInteger.valueOf(5);
BigInteger[] arr =bd1.divideAndRemainder(bd2);
System.out.println(arr[0]);//2
System.out.println(arr[1]);//0
BigInteger bd4 = bd1.pow(2);
System.out.println(bd4);//100
BigInteger bd5 = bd1.max(bd2);//不会创建新的对象
System.out.println(bd5==bd1);//true
System.out.println(bd5==bd2);//false
BigInteger底层存储方式
1、对于计算机而言,其实是没有数据类型的改变,都是010101
2、数据类型是编译语言自己规定的
BigInteger的存储上限
存储方式:[1,-2147483648,0]
数组的最大长度是int的最大值:2147483648
数组中每一位能表示的数字:-2147483648~2147483647
数组中最多能存储元素个数:21亿多
数组中每一位能表示的数字:42亿多
BigInteger能表示的最大数字为42亿的21亿次方
小总结
1、BigInteger表示一个大整数
2、如何获取BigInteger的对象?
BigInteger b1 = BigInteger.valueOf(0.1);
BigInteger b2 = new BigInteger(”整数“);
3、常见操作
加:add
减:substract
乘:multiply
除:divide、divideAndRemainder
比较:equals、max、min
次幂:pow
转成整数:intValue、longValue
BigDecima基本使用和原理分析
计算机中的小数
十进制69.875分为整数部分的二进制:0100 0101,小数部分的二进制:111
十进制:0.9需要45位表示
十进制:0.226需要55位表示
float 占用4个字节 总占用32个bit位,小数部分23个bit位
double 占用8个字节 总占用64个bit位,小数部分52个bit位
BigDecima
用于小数的精确计算
用来表示很大的小数
不可变的、任意精度的有符号的十进制数
//1、通过传递double类型的小数来创建对象
//细节:
//这种方式有可能是不精确的,所以不建议使用
BigDecimal bd1=new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1);//0.01000000000000000020816681711721685132943093776702880859375
System.out.println(bd2);//0.0899999999999999966693309261245303787291049957275390625
//2、通过传递字符串表示的小数来创建对象
BigDecimal bd3=new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
System.out.println(bd3);//0.01
System.out.println(bd4);//0.09
//3、通过静态方法获取对象
BigDecimal bd6 = BigDecimal.valueOf(10);
//细节:
//1、如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法
//2、如果要表示的数字比较大,超出了double的取值范围,建议使用构造方法
//3、如果我们传递的是0~10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new
BigDecima的使用
public static BigDecima valueOf(double val)----获取对象
public BigDecima add(BigDecima val)----加法
public BigDecima subtract(BigDecima val)----减法
public BigDecima multiply(BigDecima val)----乘法
public BigDecima divide(BigDecima val)----除法
public BigDecima divide(BigDecima val,精确几位,舍入模式)----除法
up:远离零方向的舍入模式
down:向零方向舍入的舍入模式
ceiling:向正无限大方向舍入的舍入模式
floor:向负无限大方向舍入的舍入模式
half_up:四舍五入
BigDecima底层存储方式
底层是字符串的遍历,将其中遍历的字符转换为ASCII码存储
小总结
1、BigDecima的作用是什么?
表示较大的小数和解决小数运算精度失真问题
2、BigDecima的对象如何获取?
BIgDecima bd1 = new BigDecima(“较大的小数”);
BigDecima bd2 = new BigDecima.valueOf(0.1);
3、常见操作
加:add
减:substract
乘:multiply
除:divide(四舍五入:RoundingMode.HALF_UP)
正则表达式
正则表达式可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性
需求:假如现在要求校验一个QQ号码是否正确
规则:6位及20之内,0不能在开头,必须全部是数字
public static boolean checkQQ(String qq){
//核心思想:
//先把异常数据进行过滤
//下面的就是满足要求的数据了
int len =qq.length();
if (len<6||len>20){
return false;
}
if (qq.startsWith("0")){
return false;
}
for (int i = 0; i < qq.length(); i++) {
char c = qq.charAt(i);
if(c<'0'||c>'9'){
return false;
}
}
return true;
}
qq.matches(regex:"[1~9]\\d{5,19}")
正则表达式的作用
1、校验字符串是否满足规则
2、在一段文字中查找满足要求的内容
爬虫
有如下文本,请按照要求爬取数据
java自从95年问世以来,经历了很多版本,目前企业中用的最多的是java8和java11,因为这两个版本是长期支持版本,下一个长期支持版本是java17,相信在未来不久java17也会逐渐登上历史舞台
要求:找出里面所有的javaXX
Pattern:表示正则表达式
Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取,在大串中去找符合匹配规则的字串
public static void main (String[] args){
String str="java自从95年问世以来,经历了很多版本,目前企业中用的最多的是java8和java11,因为这两个版本是长期支持版本,下一个长期支持版本是java17,相信在未来不久java17也会逐渐登上历史舞台";
//method1(str);
//1、获取正则表达式的对象
Pattern p = Pattern.compile("java\\d{0,2}");
//2、获取文本匹配器的对象
//拿着m去读取str,找符合p规则的子串
Matcher m = p.matcher(str);
//3、利用循环获取
while(m.find()){
String s = m.group();
System.out.println(s);
}
}
private static void method1(String str) {
//Pattern:表示正则表达式
//Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始。
// 在大串中去找符合匹配规则的子串
//获取正则表达式的对象
Pattern p = Pattern.compile("java\\d{0,2}");
//获取文本匹配器的对象
//m:文本匹配器的对象
//str:大串
//p:规则
//m要在str中找符合p规则的小串
Matcher m = p.matcher(str);
//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
//如果没有,方法返回false
//如果有,返回true,在底层记录子串的起始索引和结果索引+1
boolean b=m.find();
//方法底层会根据find方法记录的索引进行字符串的截取
//subString(起始索引,结束索引);包头不包尾
//(0,4)但是不包含4索引
//会把截取的小串进行返回
String s1 = m.group();
System.out.println(s1);
//第二次在调用find的时候,会继续读取后面的内容
//读取到第二个满足要求的子串,方法会继续返回true
//并把第二个子串的起始索引和结束索引+1,进行记录
b = m.find();
//第二次调用group方法的时候,会根据find方法记录的索引再次截取子串
String s2 = m.group();
System.out.println(s2);
}
public String[] matches(String regex)----判断字符串是否满足正则表达式的规则
public String replaceAll(String regex,String newStr)----按照正则表达式的规则进行替换
public String split(String regex)----按照正则表达式的规则切割字符串
replaceAll细节:
方法在底层跟之前一样也会创建文本解析器的对象
然后从头开始去读取字符串中的内容,只要有满足的,那么就用第二个参数去替换。
分组
需求:判断一个字符擦胡你的开始字符和结束字符是否一致?只考虑一个字符
String regex1="(.).+\\1";
System.out.println("a123a".matched(regex1));
System.out.println("b456b".matched(regex1));
System.out.println("17891".matched(regex1));
System.out.println("&abc&".matched(regex1));
System.out.println("a123b".matched(regex1));
需求:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
String regex2="(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
需求:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//(.):把首字母看作一组
//\\2:把首字母拿出来再次使用
//*:作用于\\2,表示后面重复的内容出现0次或多次
String regex3="((.)\\2)*.+\\1"
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
需求:解决口吃
String str = "我要学学编编编编程程程程程程";
String result = str.replaceAll(regex:"(.)\\1+",replacement:"$1");
System.out.println(result);
捕获分组:
后续还要继续使用本组的数据。
正则内部使用:\组号
正则外部使用:$组号
非捕获分组:
分组之后不需要再用本组数据,仅仅是把数据括起来
(?:正则)----获取所有
(?=正则)----获取前面部分
(?!正则)----获取不是指定内容的前面部分
小总结
1、正则表达式中分组有两种:
捕获分组、非捕获分组
2、捕获分组(默认):
可以获取每组中的内容反复使用。
3、组号的特点:
从1开始,连续不断
以左括号为基准,最左边的是第一组
4、非捕获分组:
分组之后不需要再用本组数据,仅仅把数据括起来,不占组好(?:)(?=)(?!)都是非捕获分组
Date
Date----时间
SimpleDateFormat----格式化时间
Calendar----日历
时间的相关知识点
全世界的时间,有一个统一的计算标准
以前:格林威治时间,GMT
现在:原子钟:利用铯原子的震动的频率计算出来的时间,作为世界标准时间
Date时间类
Date类是一个JDK写好的Javabean类,用来描述时间,精确到毫秒。
利用空参构造的对象,默认表示系统当前的时间。
利用有参构造创建的对象,表示指定的时间
public class Date{
private long time;
public Date(){
this.time=System.currentTimeMillis();
}
public Date(long time){
this.time=time;
}
public long getTime(){
return time;
}
public void setTime(long time){
this.time=time;
}
}
小总结
1、如何创建日期对象?
Date date = new Date();
Date date = new Date(指定毫秒值);
2、如何修改时间对象中的毫秒值
setTime(毫秒值);
3、如何获取时间对象中的毫秒值
getTime();
SimpleDateFormat
格式化:把时间变成我们喜欢的格式
解析:把字符串表示的时间变成Date对象
public SimpleDateFormat()----构造一个SimpleDateFormat,使用默认格式
public SimpleDateFormat(String pattern)----构造一个SimpleDateFormat,使用指定格式
public final String format(Date date)----格式化(日期对象->字符串)
public Date parse(String source)----解析(字符串->日期对象)
利用空参构造SimpleDateFormat对象
细节:
创建对象的格式要跟字符串的格式完全一致
小练习
需求:
假设,你初恋的出生年月日为2000-11-11
请用字符串表示这个数据,并将其转换为:2000年11月11日
String str = "2000-11-11";
//解析
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf1.parse(str);
//格式化
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
String result = sdf2.format(date);
System.out.println(result);
恋爱小demo
自动计算出第100天纪念日和第520天纪念日
后续可以输入自己想要查询的第几天纪念日
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class lianai2 {
public static void main(String[] args) throws ParseException {
String startday="2023-4-2";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date start = sdf.parse(startday);
Date start2 = sdf.parse(startday);
long time =100*24*60*60*1000l+start.getTime();
start.setTime(time);
String str = sdf.format(start);
System.out.println("第一百天纪念日在"+str);
time+=420*24*60*60*1000l;
start.setTime(time);
str = sdf.format(start);
System.out.println("第五百二十天纪念日在"+str);
while(true){
Scanner sc=new Scanner(System.in);
System.out.println("请输入你想要的纪念日天数:");
int daytime = sc.nextInt();
long time1 = daytime*24*60*60*1000l+start2.getTime();
start.setTime(time1);
String str2 = sdf.format(start);
System.out.println("第"+daytime+"天的纪念日在"+str2);
}
}
}
小demo2
可以自动计算出已经一起度过了几天
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class lianai1 {
public static void main(String[] args) throws ParseException {
String start="2023-4-2";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date day = sdf.parse(start);
long mytime = day.getTime();
long nowtime = System.currentTimeMillis();
long time =nowtime-mytime;
long days=time/1000/60/60/24;
System.out.println("咱们已经一起度过了"+days+"天了!");
}
}
小总结
1、SimpleDateFormat的两个作用
格式化
解析
2、如何指定格式
yyyy年MM月dd日HH:mm:ss
Calendar
概述:
Calendar代表了系统当前时间的日历对象,可以单独修改、获取时间中的年,月,日
细节:Calendar是一个抽象类,不能直接创建对象
public static Calendar getInstance()----获取当前时间的日历对象
public final Date getTime()----获取日期对象
public final setTime(Date date)----给日历设置日期对象
public long getTimeInMillis()----拿到时间毫秒值
public void setTimeInMillis(long millis)----给日历设置时间毫秒值
public int get(int field)----取日历中的某段信息
public void set(int field ,int value)----修改日历的某个字段信息
public void add(int field,int amout)----为某个字段增加/减少指定的值
//1、获取日历对象
//细节:1、Calendar是一个抽象类,不能直接new而是通过一个静态方法获取到了子类对象
//底层原理
//会根据系统的不同时区来获取不同的日历对象,默认表示当前时间。
//会把时间中的纪元,年,月,日,时,分,秒,星期,等等的都放到一个数组当中
//0:纪元
//1:年
//2:月
//3:一年中的第几周
//4:一个月中的第几周
//5:一个月中的第几天
//....16
//2、
//月份:范围0~11 如果获取出来的是0,那么实际上是1月。
//星期:在老外的眼里,星期日是一周中的第一天
Calendar c = Calendar.getInstance();
//2、修改一下日历表的时间
Date d = new Date(0L);
c.setTime(d);
//java在Calendar类中,把索引对应的数字都定义成常量
int year = c.get(1);//c.get(Calendar.YEAR)
int month = c.get(2)+1;//c.get(Calendar.MONTH)+1
int date = c.get(5);//c.get(Calendar.DAY_OF_MONTH)
小总结
1、Calendar表示什么?
表示一个时间的日历对象
2、如何获取对象
通过getInstance方法获取对象
3、常见方法:
setXxx:修改
getXxx:获取
add:在原有基础上进行增加或者减少
4、细节点:
日历类中月份的范围:0~11
日历类中星期的特点:星期日是一周中的第一天
JDK8新增时间类
代码层面:
JDK7:代码麻烦 日期对象通过计算比较获取毫秒值。
JDK8:判断的方法,计算时间间隔的方法。
安全层面:
JDK7:多线程环境下会导致数据安全的问题。
JDK8:时间日期对象都是不可变的,解决了这个问题。
Date类
ZoneId:时区
Instance:时间戳
ZoneDateTime:带时区的时间
日期格式化类:SimpleDateFormat:
DateTimeFormatter:用于时间的格式化和解析
日历类:Calendar
LocalDate:年、月、日
LocalTime:时、分、秒
LocalDateTime:年、月、日、时、分、秒
工具类:
Duration:时间间隔(秒,纳秒)
Period:时间间隔(年、月、日)
ChronoUnit:时间间隔(所有单位)
细节:
JDK8新增的时间对象都是不可变的
如果我们修改了,减少了,增加了时间
那么调用者是不会发生改变的,产生一个新的时间。
Zoneld时区
static Set< String>getAvailableZoneIds()----获取java种支持的所有时区
static ZoneId systemDefault()----获取系统默认时区
static ZoneId of(String zoneId)----获取一个指定时区
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds);
ZoneId zoneId = ZoneId.systemDefault();
System.out.prontln(zoneId);
ZoneId zoneId1 = ZoneId.of("Asia/Pontianak");
System.out.println(zoneId1);
Instant时间戳
static Instant now()----获取当前时间的Instance对象(标准时间)
static Instant ofXxxx(long epochMilli)----根据(秒/毫秒/纳秒)获取Instant对象
ZoneDateTime atZone(ZoneId zone)----指定时区
boolean isXxx(Instance otherInstant)----判断系列的方法
Instant minusXxx(long millisToSubtract)----减少时间系列的方法
Instant plusXxx(long millisToSubtract)----增加时间系列的方法
ZoneDateTime带时区的时间
static ZonedDateTime now()----获取当前时间的ZonedDateTime对象
Static ZonedDateTime ofXxx()----获取指定时间的ZoneDateTime对象
ZonedDateTime withXxx(时间)----修改时间系列的方法
ZonedDateTime minusXxx(时间)----减少时间系列的方法
ZonedDateTime plusXxx(时间)----增加时间系列的方法
DateTimeFormat用于时间的格式化和解析
static DateTimeFormatter ofPattern(格式)----获取格式对象
String format(时间对象)----按照指定方式格式化
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");
System.out.println(dtf1.format(time));
LocalDate、LocalTime、LocalDateTime
static XXX now()----获取当前时间的对象
static XXX of(…)----获取指定时间的对象
get开头的方法----获取日历中的年、月、日、时、分、秒等信息
isBefore,isAfter----比较两个LocalDate
with开头的----修改时间系列的方法
minus开头的----减少时间系列的方法
plus开头的----增加时间系列的方法
Duration、Period、ChronoUnit
Duration:用于计算两个“时间”间隔(秒,纳秒)
Period:用于计算两个“日期”间隔(年、月、日)
ChronoUnit:用于计算两个“日期”间隔
包装类
什么是包装类?
基本数据类型对应的引用类型
包装类:用一个对象,把基本数据类型给包起来
基本数据类型的包装了对应
byte----Byte
short----Short
char----Character
int----Integer
long----Long
float----Float
double----Double
boolean----Boolean
Integer
获取Integer对象的方式(了解)
public Integer(int value)----根据传递的整数创建一个Integer对象
public Integer(String s)----根据传递的字符串创建一个Integer对象
public static Integer valueOf(int i)----根据传递的整数创建一个Integer对象
public static Integer valueOf(String s)----根据传递的字符串创建一个Integer对象
public static Integer ValueOf(String s , int radix)----根据传递的字符串和进制创建一个Integer对象
//1、利用构造方法获取Integer的对象(JDK5以前的方式)
Integer i1 = new Integer(value:1);
Integer i2 = new Integer(s:"1");
System.out.println(i1);
System.out.println(i2);
//2、利用静态方法获取Integer的对象(JDK5以前的方式)
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf("123");
Integer i5 = Integer.valueOf(s:"123",randix:8);
System.out.println(i3);
System.out.println(i4);
System.out.println(i5);//83
//3、这两种方式获取对象的区别(掌握)
//底层原理:
//因为在实际开发中,-128~127之间的数据,用的比较多
//如果每次使用都是new对象,那么太浪费内存了
//所以,提前把这个范围之内的每一个数据都创建好对象
//如果要用到了不会创建新的,而是返回已经创建好的对象
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6==i7);//true
Integer i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8==i9);//false
//因为看到了new关键字,在java中,每一次new都是创建了一个新的对象
//所以下面的两个对象都是new出来,地址值不一样
Integer i10 = new Integer (value:127);
Integer i11 = new Integer (value:127);
System.out.println(i10==i11);//false
Integer i12 = new Integer (value:128);
Integer i13 = new Integer (value:128);
System.out.println(i10==i11);//false
//在以前包装类如何进行计算
Integer i1 = new Integer(value:1);
Integer i2 = new Integer(value:2);
//需求:要把两个数据进行相加得到结果3
//对象之间是不能直接进行计算的
//步骤:
//1、把对象进行拆箱,变成基本数据类型
//2、相加
//3、把得到的结果再次进行装箱(再变回包装类)
//在JDK5的时候提出了一个机制:自动装箱和自动拆箱
//自动装箱:把基本数据类型会自动地变成其对应的包装类
//自动拆箱:把包装类自动的变成其对象的基本数据类型
//在底层,此时还会自动调用静态方法valueof得到一个Integer对象,只不过这个动作不需要我们自己去操作了
//自动装箱的动作
Integer i1=10;
Integer i2 = new Integer (value:10);
//自动拆箱的动作
int i=i2;
//在JDK5以后,int和Integer可以看作是同一个东西,因为在内部可以自动转化。
成员方法
public static String toBinaryString(int i)----得到二进制
public static String toOctalString(int i)----得到八进制
public static String toHexString(int i)----得到十六进制
public static int parselnt(String s)----将字符串类型的整数转成int类型的整数
String str1 = Integer.toBinaryString(i:100);
String str2 = Integer.toOctalString(i:100);
String str3 = Integer.toHexString(i:100);
将字符串类型的整数转成int类型的整数
强类型语言:每种数据在Java中都有各自的数据类型
在计算的时候,如果不是同一种数据类型,是无法直接计算的
细节1:
在类型转换的时候,括号中的参数只能是数字不能是其他,否则代码会报错
细节2:
8种包装类当中,除了Character都有对应的parseXxx的方法,进行类型转换
//改写键盘录入
Scanner sc = new Scanner (System.in);
String i = sc.newx();
//弊端:
//当我们在使用next,nextInt,nextDouble在接受数据的时候,遇到空格,回车,制表符的时候就停止了
//键盘录入的时123 123 那么此时只能接受到空格前面的数据
//我想要的是一整行的数据
//约定:
//以后我们如果想要键盘录入,不管想要什么,统一使用nextline
//特点:遇到回车才停止
小总结
1、什么是包装类?
基本数据类型对应的对象
2、JDK5以后对包装类新增了什么特性?
自动装箱、自动拆箱
3、我们以后如何获取包装类对象?
不需要new,不需要调用方法,直接赋值即可
Integer i =10;