我上班摸鱼重新学习java基础做的笔记,从面向对象开始

面向对象基础

类与对象

人类、鸟类、鱼类。。。所谓类,就是对一类事物的描述

对象是某一类事物实际存在的每个个体,因此也称为实例

类是抽象概念,对象是具体的

  • A:谁拿了我的手机?
  • B:是个人(某一个类)
  • A:我还知道是个人呢,具体是谁呢?
  • B:是lbw(具体某个对象)

类的创建

public class Person {//定义人类具有的三个属性
//直接在类中定义变量,表示类具有的属性
String name;
int age;
String sex;
}

对象的创建

new 类名();

对象的使用

我们可以使用一个变量来指代某个对象,只不过引用类型的变量,存储的是对象的引用,而不是对象本身

public static void main(String[] args) {
//这里的a存放的是具体的值
int a = 10;
//创建一个变量指代我们刚刚创建好的对象,变量的类型就是对应的类名
//p存放的是对象的引用,而不是本体,我们可以通过对象的引用来间接操作对象
Person p = new Person();
p.name = "小明";//访问对象的属性
p.age = 18;
p.sex = "男";
System.out.println(p.name);
}

我们将p2赋值为p1的值,实际上只传递了对象的引用,不是对象本身的复制(String也是引用类型)

Person p1 = new Person();
Person p2 = p1;

包的访问权限

  • private:私有,无法被除当前类以外的任何位置访问
  • 默认:类本身和同包中其他类访问
  • protected:受保护,可以被类本身和同包中的其他类访问,也可被子类访问
  • public:公共,允许在任何地方被访问
权限 当前类 同一个包下的类 不同包下的子类 不同包下的类
public y y y y
protected y y y x
默认 y y x x
private y x x x

封装、继承和多态

封装:把对象的属性和方法结合成一个独立的整体,隐藏实现细节,并提供对外访问的接口

继承:从已知的一个类中派生出一个新的类,叫子类。子类实现了父类所有非私有化的属性和方法,并根据实际需求扩展出新的行为

多态:多个不同的对象对同一消息作出响应,同一消息根据不同的对象而采用不同的方法

类的封装

  • Person类
public class Person {//定义人类具有的三个属性
//直接在类中定义变量,表示类具有的属性
private String name;
private int age;
private String sex; //构造方法
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
private Person(){}
public static Person getInstance(){
Person person = new Person();
person.name = "小明";
return person;
} //get方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getSex() {
return sex;
} //set方法
public void setName(String name) {
if(name.contains("小"))//可以在这里对名字进行过滤
return;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSex(String sex) {
this.sex = sex;
}
}
  • Main类
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Person person = new Person("小明", 18, "男");
person.setName("小方");//更改名字不成功
System.out.println(person.getName()); // Person person1 = new Person();//创建不成功,对应的构造方法是私有的
Person person1 = Person.getInstance();
}
}

封装思想实际上就是把细节隐藏了,外部只需要知道这个方法是什么作用,而无需关心实现,要用什么由类自己来做,不需要外部来操作类内部的东西去完成,封装就是通过访问权限控制来实现的。

类的继承

在定义不同类的时候存在一些相同的属性,为了方便使用可以将这些共同属性抽象成一个父类,在定义其他子类时可以继承自该父类,子类可以使用父类中非私有的成员

父类

public class Person {//定义人类具有的三个属性
//直接在类中定义变量,表示类具有的属性
String name;
int age;
String sex; public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} public void test() {
}
}

子类

  • 使用extends关键字继承父类
public class Worker extends Person{
}
  • 父类存在一个有参构造方法,子类必须在构造方法中调用(super调用必须是构造函数中的第一条语句)
public class Student extends Person{
public Student(String name, int age, String sex) {
super(name, age, sex);//super调用必须是构造函数中的第一条语句
} public void study(){
test();
System.out.println("我是学生,我叫"+name);
}
}
  • 我们在使用子类时,可以将其当作父类来使用,只能访问父类对象的内容
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Person person = new Student("小明",18,"男");
person.test();
}
}
  • instanceof判断是否是对应类型
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Person person = new Student("小明",18,"男");
if(person instanceof Student){
System.out.println("是Student类型");
}
}
}
  • 子类可以定义父类同名属性,调用时就近原则
public class Worker extends Person{

    String name;

    public Worker(String name, int age, String sex) {
super(name, age, sex);
} public void work(){
System.out.println("我是"+name+",我在工作");
}
}
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Worker worker = new Worker("小刚",19,"man");
worker.work();//我是null,我在工作
}
}
  • 使用super关键字指定要访问的父类属性(不能使用super.super连续访问父类,只能访问1层)
public class Worker extends Person{

    String name;

    public Worker(String name, int age, String sex) {
super(name, age, sex);
} public void work(){
System.out.println("我是"+super.name+",我在工作");
}
}

顶层object类

所有的类默认继承自object类,底层是有c++实现的

方法的重写

  • 方法的重写不同于方法的重载,方法的重载是为某个方法提供更多种类,而方法的重新是覆盖原有的方法实现
  • 子类重写的方法访问权限可以比父类高(如父类protected,子类可以是public),但是不能比父类低
  • 基于方法重写的特性,不同的子类可以出现不同的行为,比如学生考试可以得到A,而工人去考试只能得到D,多态的特性的一种体现
  • 如果不希望子类重写某个方法,可以在方法前添加final关键字,表示这个方法已经是最终形态(依然可以被重载)
  • 如果父类方法的访问权限是private,那么子类如果尝试重写就相当于是新的方法,无法加@Override,也无法使用super
  • 变量加fianl,这个变量只能被赋值1次
  • 如果在类上加final,那么这个类就不能再继承了
  • 在重写父类方法时,如果希望调用父类原本的方法实现,那么同样可以使用super关键字
  • static关键字的方法中无法使用superthis
public class Person {//定义人类具有的三个属性
//直接在类中定义变量,表示类具有的属性
String name;
int age;
String sex; public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} @Override//可以不加
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj instanceof Person) {
Person person = (Person) obj;
//如果名字、年龄、和性别都相等,返回真
return this.name.equals(person.name) &&
this.age == person.age &&
this.sex.equals(person.sex);
}
return false;
} @Override//通过重新toString方法打印更人性化的信息
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
// Person p1 = new Person("mike",18,"man");
// Person p3 = new Person("mike",18,"man");
Object p1 = new Person("mike",18,"man");
Object p3 = new Person("mike",18,"man"); System.out.println(p1.equals(p2));//优先调用重写的equals,返回true
}
}

抽象类

处于顶层的类,可以进一步进行抽象,使用abstract关键字

public abstract class Person {//定义人类具有的三个属性
public abstract void exam(){
System.out.println("我是考试方法");
}
}
  1. 抽象类由于不是具体的类定义(它是类的抽象),可能会存在某些方法没有实现,因此无法直接通过new关键字来直接创建对象
  2. 要使用抽象类,可以去创建它的子类
public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Person person = new Student("小明",12,"男");
}
}
  1. 抽象类一般只用作继承使用,当然,抽象类的子类也可以是一个抽象类
  2. 注意:抽象方法的访问权限不能为private

接口

public interface Study {
void study();
}
  1. 接口甚至比抽象类还抽象,他只代表某个确切的功能!
  2. 也就是只包含方法的定义,甚至都不是一个类!
  3. 接口一般只代表某些功能的抽象,
  4. 接口包含了一系列方法的定义,类可以实现这个接口,表示类支持接口代表的功能
  5. 类似于一个插件,只能作为一个附属功能加在主体上,同时具体实现还要由主体来实现
  6. 接口的方法默认是抽象方法,abstract关键字可以省略
  7. 使用implements关键字实现接口
public class Student extends Person implements Study{
public void study() {
System.out.println("我学的比较佛系");
}
}
  1. 接口的所有抽象方法都要实现
  2. 同一个类可以实现多个接口,类的继承只能有一个(只能有一个父亲,但可以有多个师父)
  3. 接口跟抽象类一样,不能直接创建对象,但是我们可以将接口实现类的对象以接口的新式去使用
  4. 当做接口使用时,只能使用接口中定义的方法和object类的方法,无法使用类本身的方法和父类的方法
  5. 从java8开始接口中可以存在方法的默认实现,使用default关键字
public interface Study {
default void test(){
System.out.println("我是默认实现");
}
}
  1. 接口不同与类,不允许存在成员变量和成员方法,但可以存在静态变量和静态方法(默认定义的变量为public static final,静态方法也只能是public)
  2. 接口可以继承自其他接口,接口可以多继承
public interface C {
} public interface B {
void test();
} public interface A extends B, C {
void hello();
} public class Test implements A{ @Override
public void hello() { } @Override
public void test() { }
}

枚举类

假设我们想给小明添加一个状态(跑步、学习、睡觉),外部可以实时获取小明的状态

public class Student {
private String status; public String getStatus() {
return status;
} public void setStatus(String status) {
this.status = status;
}
}

我们希望开发者拿到使用的就是我们预先定义好的状态(如不希望出现打飞机状态),我们使用枚举来完成

public enum Status {
RUNNING, STUDY, SLEEP;
} public class Student {
private Status status; public Status getStatus() {
return status;
} public void setStatus(Status status) {
this.status = status;
}
} public class Main {
public static void main(java.lang.String[] args){//有歧义,需要指名对对应的包名
Student student = new Student();
student.setStatus(Status.STUDY);
System.out.println(student.getStatus());
}
}

枚举类型本质是一个普通的类,但是它继承自Enum类,我们定义的每一个状态其实就是一个public static final的Status类型成员变量

枚举类的构造方法、get方法

public enum Status {
RUNNING("跑步"), STUDY("学习"), SLEEP("睡觉"); private final String name;//枚举的成员变量 Status(String name) {//覆盖原有构造方法(默认private,只能内部使用!)
this.name = name;
} public String getName() {//获取封装的成员变量
return name;
}
} public class Student {
private Status status; public Status getStatus() {
return status;
} public void setStatus(Status status) {
this.status = status;
}
} public class Main {
public static void main(java.lang.String[] args){//有歧义,需要指名对对应的包名
Student student = new Student();
student.setStatus(Status.SLEEP);
System.out.println(student.getStatus().getName());
}
}

面向对象高级

基本类型包装类

  • java并不是纯面向对象的语言,虽然java语言是一个面向对象的语言,但是java中的基本数据类型却不是面向对象的
  • java中的的基本类型,如果想通过对象的形式去使用他们,java提供基本类型包装类,使得java能够更好的体现面向对象思想,同时也使得基本类型能够支持对象操作

包装类介绍

所有包装类层次结构如下:

其中能够表示数字的基本类型包装类,Byte,Short,Integer,Long,Float,Double继承自Number类,

Boolean,Character继承自Object

public class Main {
public static void main(java.lang.String[] args){//有歧义,需要指名对对应的包名
Integer i = new Integer(10);
System.out.println(i);
}
}

自动装箱,简化语法

public class Main {
public static void main(java.lang.String[] args){//有歧义,需要指名对对应的包名
Integer i = new Integer(10);
Integer a = 20;
Integer b = Integer.valueOf(20);
System.out.println(i+j);//40
}
}

自动拆箱,类型要对应

public class Main {
public static void main(java.lang.String[] args){//有歧义,需要指名对对应的包名
Integer a = 20;
int i = a;
System.out.println(i+a);
}
}

因为包装类是一个类,不是基本类型,所有说值相同两个不同的对象是不相等的

public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
int a = 10;
int b = 10;
System.out.println(a == b);//true
Integer i = new Integer(20);
Integer j = new Integer(20);
System.out.println(i == j);//false
}
}

自动装箱的情况

public class Main {
public static void main(java.lang.String[] args) {//有歧义,需要指名对对应的包名
Integer i = Integer.valueOf(20);
Integer j = 20;
System.out.println(i == j);//true
Integer a = Integer.valueOf(128);//超出了128就不是同一个对象
Integer b = 128;
System.out.println(a == b);//false
}
}

使用包装类字符串转数字

public class Main {
public static void main(java.lang.String[] args) {
String str = "666";
Integer i = new Integer(str);
Integer j = Integer.parseInt(str);
Integer k = Integer.valueOf(str);
System.out.println(i * 10 + j);//7326
}
}

使用包装类进制转换(16->10,8->10,10->16)

  • 十六进制、八进制转十进制
public class Main {
public static void main(java.lang.String[] args) {
Integer i = Integer.decode("0xaa");//16^1*10+10
System.out.println(i);//170
i = Integer.decode("077");//8^1*7+7
System.out.println(i);//63
}
}
  • 十进制转十六进制
public class Main {
public static void main(java.lang.String[] args) {
Integer i = 16;
System.out.println(Integer.toHexString(i));//10
}
}

特殊包装类

  • Void,不能new对象
public class Main {
public static void main(java.lang.String[] args) {
Void v = null;
}
}
  • BigInteger,用于计算超大数字
public class Main {
public static void main(java.lang.String[] args) {
BigInteger i = BigInteger.valueOf(Long.MAX_VALUE);
i = i.multiply(BigInteger.valueOf(Long.MAX_VALUE));//使用BigInteger表示Long最大值相乘
System.out.println(Long.MAX_VALUE*Long.MAX_VALUE);//直接相乘会溢出
System.out.println(i);//可以计算出结果
System.out.println(i.pow(10));//10次方
}
}
  • BigDecimal,实现小数的精确计算
public class Main {
public static void main(java.lang.String[] args) {
BigDecimal i = BigDecimal.TEN;
i = i.divide(BigDecimal.valueOf(3),100, RoundingMode.CEILING);
//计算10/3的结果,精确到小数后100位
//RoudingMode是舍入模式,CEILING是向上取整,FLOOR向下
System.out.println(i);
}
}

数组

一维数组

数组是相同类型数据的有序集合,数组可以代表任何相同类型的一组内容(包括引用类型和基本类型)其中存放的每一个数据称为数组的一个元素,定义一个数组变量:

public class Main {
public static void main(java.lang.String[] args) {
int[] arr;
}
}

数组本身也是类,但是编程不可见(底层c++写的),以对象形式存在,要创建一个数组,要使用new关键字

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = new int[10];//指定数组长度为10
System.out.println(arr.toString());//toString,equals方法没有重写
Object obj = arr;
}
}

创建数组的方式,length属性在一开始就确定了,不允许进行修改

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = new int[]{1,2,3,4,5};
System.out.println(arr.length);//长度确定后不能再修改,final
int[] arr1 = new int[10];
System.out.println(arr1.length);//长度确定后不能再修改,final
int[] arr2 = {};
System.out.println(arr2.length);//长度确定后不能再修改,final
int[] arr3 = new int[-1];//长度不能是-1
System.out.println(arr1.length);//长度确定后不能再修改,final
}
}

默认值跟对象成员变量是一样的

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = new int[10];
System.out.println("第一个元素为"+arr[0]);//0
}
}

toString,equals方法没有重写,要判断内容是否相等,要用for逐个判断

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = {1,2,3,4,5};
int[] b = {1,2,3,4,5};
System.out.println(arr.toString());
System.out.println(arr.equals(b));
}
}

clone方法重写了,可以使用

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = {1,2,3,4,5};
int[] cl = arr.clone();
for (int i = 0; i < arr.length; i++) {
System.out.print(cl[i]+",");
}
}
}

使用foreach遍历数组

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = {1, 2, 4, 4, 5};
for (int i : arr) {
System.out.print(i+" ");
}
}
}

由于基本数据类型和引用数据类型不同,int类型数组不能被Object类型的数组变量接收

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = {1, 2, 4, 4, 5};
// Object[] test = arr;//不可以这样
String[] str = {"1","rast","sss"};
Object[] test = str; for (Object s : test) {
System.out.println(s.toString());
}
}
}

final数组

public class Main {
public static void main(java.lang.String[] args) {
int[] arr = {1, 2, 4, 4, 5};
arr = new int[]{4,5,6,7};
final int[] b = {1, 2, 4, 4, 5};
b[0] = 100;//里面的值可以改
b = new int[]{4,5,6,7};//无法指向新的数组
}
}

多维数组

定义二维数组,访问指定元素

public class Main {
public static void main(java.lang.String[] args) {
int[][] arr = new int[][]{
{1,2,3,4},
{5,6,7,8}
};
System.out.println(arr[1][2]);//7
}
}

遍历二维数组

public class Main {
public static void main(java.lang.String[] args) {
int[][] arr = new int[][]{
{1,2,3,4},
{5,6,7,8}
};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}

可变长参数

支持可变长参数

public class Main {
public static void main(java.lang.String[] args) {
test();//0
test("123","343","aaa","bbb");//4
}
public static void test(String... s){
System.out.println(s.length);//像数组一样使用可变长参数
}
}

可变长参数只能放在最后,不允许出现多个可变长参数

public static void test1(int a,int b,String... s) {
}

main函数中字符串数组的作用:外部传入参数时可以使用,类似ls -l之类后面的参数,一般有特殊配置才会使用

public class Main {
public static void main(java.lang.String[] args) {
for (String arg : args) {
System.out.println(arg);//可以把传入的参数打印出来
}
}
}

字符串

字符串是一个特殊的类,它用于保存字符串。字符串中的字符一旦确定,无法进行修改,只能重新创建

String类

String本身也是一个类,每个用双引号包裹的字符串,都是String类型的一个实例对象

public class Main {
public static void main(java.lang.String[] args) {
String str = "hello world";
}
}

可以象征性的使用一下new关键字

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
}
}

如果是直接用双引号创建的字符串,如果内容相同,为了优化效率,那么始终都是同一个对象,使用构造方法创建就不是同一个对象

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
String str1 = "hello world";
String str2 = "hello world";
System.out.println(str == str1);//false
System.out.println(str1 == str2);//true
}
}

要判断两个字符串的内容是否相等,不要使用==,String重载了equal()

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
String str1 = "hello world";
System.out.println(str == str1);//false
System.out.println(str.equals(str1));//true
}
}

求字符串长度length()

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
System.out.println(str.length());//11
}
}

裁剪字符串substring()

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
String sub = str.substring(str.length() - 3, str.length());//裁剪末尾
System.out.println(sub);//rld }
}

分割字符串split(),支持正则表达式

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
String[] strs = str.split(" ");//通过空格分割字符串
for (String s : strs) {
System.out.println(s);
}
}
}

字符串转字符数组toCharArray()

public class Main {
public static void main(java.lang.String[] args) {
String str = new String("hello world");
char[] chars = str.toCharArray();
System.out.println(chars[0]);//h
System.out.println(chars);//hello world,支持直接打印字符数组
}
}

使用new关键字字符数组转字符串

public class Main {
public static void main(java.lang.String[] args) {
char[] chars = new char[]{'奥','里','给'};//字符使用单引号
String str = new String(chars);
System.out.println(str);//奥里给
}
}

其他的常用方法:

  • contains()判断是否包含指定字符串,返回值为boolean类型
  • charAt()获取指定下标的字符,0为开头
  • toLowerCase()把字符串中的字符全部转换成小写,返回值为全小写的字符串
  • toUpperCase()把字符串中的字符全部转换成大写,返回值为全大写的字符串
  • startsWith()判断是否以指定字符串开头
  • indexOf()求指定字符串首次出现的下标,找不到返回值为-1
  • lastIndexOf()求指定字符串最后一次出现的下标
  • isEmpty()判断字符串是否为空即"",返回值为boolean,如果字符串为null,直接报错
  • replace()替换单词或字符,出现多次会全部替换
  • replaceFirst() 将目标字符串中匹配某正则表达式的第一个子字符串替换成新的字符串
  • replaceAll()替换字符串,支持正则表达式

StringBuilder类

public class Main {
public static void main(java.lang.String[] args) {
String str = "arstad"+new Object();//会自动调用toString方法
System.out.println(str);//arstadjava.lang.Object@6bc7c054
}
}

字符串支持使用++=进行拼接操作

public class Main {
public static void main(java.lang.String[] args) {
String str1 = "你看";
String str2 = "这";
String str3 = "汉堡";
String result = str1 + str2 + str3;
System.out.println(result);
}
}

直接使用加的话,每次运算都会产生一个新的对象,实际上会被优化:

public class Main {
public static void main(java.lang.String[] args) {
String str1 = "你看";
String str2 = "这";
String str3 = "汉堡";
StringBuilder builder = new StringBuilder();//一开始创建时,内部什么都没有
builder.append(str1).append(str2).append(str3);
System.out.println(builder);
}
}

delete()根据下标删除

public class Main {
public static void main(java.lang.String[] args) {
String str1 = "你看";
String str2 = "这";
String str3 = "汉堡";
StringBuilder builder = new StringBuilder();
builder.append(str1).append(str2).append(str3);
builder.delete(3,4);
System.out.println(builder);//你看这堡
}
}

replace()根据下标来替换指定字符串

public class Main {
public static void main(java.lang.String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("AAA");
builder.append("BBB");
builder.replace(0,3,"bbb");
System.out.println(builder);//bbbBBB
}
}
  • reverse()翻转字符串
public class Main {
public static void main(java.lang.String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("AAA");
builder.append("BBB");
builder.append("cde");
builder.reverse();
System.out.println(builder);//edcBBBAAA
}
}

其他常用方法和字符串有的基本一样

正则表达式

我们想要实现对给定字符串进行判断,如果字符串符合我们的规则,我们返回真,否则返回假,可以使用正则表达式

public class Main {
public static void main(java.lang.String[] args) {
String str = "oooo";
System.out.println(str.matches("o+"));//+表示对前面这个字符匹配一次或多次,这里返回true
}
}

用于规定组件必须出现多少次才能满足匹配的,我们一般称为限定符:

  • *:匹配前面的子表达式零次或多次。如zo*能匹配z以及zoo,*等价{0,}
  • +:匹配前面的子表达式一次或多次。如zo+能匹配zo以及zoo,不能匹配z+等价{1,}
  • ?:匹配前面的子表达式零次或一次。如do(es)?能匹配do以及does,其中把es看作一个整体。?等价{0,1}
  • {n}:n是一个非负整数,匹配确定的n次。如o{2}不能匹配Bob中的o,但是能匹配food中的两个oo
  • {n,}:n是一个非负整数,至少匹配n次。如o{2,}不能匹配Bob中的o,但是能匹配fooooood中的所有o
  • {n,m}:n和m均是非负整数,其中n<=m,最少匹配n次最多匹配m次。如fo{1,3}d匹配foood,food,fod。逗号在两个数之间不能有空格

如果想要表示一个范围内的字符,可以用方括号

public class Main {
public static void main(java.lang.String[] args) {
String str = "abcccbbaac";
System.out.println(str.matches("[abc]*"));//表示abc这三个字符可以出现0-N次,true
}
}
  • [abc]:匹配方括号中的字符
  • [^abc]:匹配除了方括号中的字符
  • [A-Z]:表示一个区间,匹配所有大小字母,[a-z]表示所有小写字母
  • .:匹配除了换行符\n\r之外的任何字符,等价[^\n\r]
  • [\s\S]:匹配所有。\s是匹配所有空白符,包括换行,\S匹配非空白符,不包括换行
  • \w:匹配字母数组下划线。等价于[A-Za-z0-9]

内部类

成员内部类

直接在类的内部定义成员内部类

public class Test {
public class Inner{//内部类也可以有成员变量、方法等,甚至可以套娃成员内部类
}
}

成员内部类和成员方法、成员变量一样,是对象所有的,而不是类所有的,如果我们要使用成员内部类,就需要创建对象

public class Main {
public static void main(java.lang.String[] args) {
Test test = new Test();
Test.Inner inner = test.new Inner();//注意权限问题
}
}

在成员内部类中,是可以访问到外层的变量的,因为内部类本身就是某个对象所有的,每个对象都有一个这样的类定义,这里的name是其所依附对象的

外部无法访问内部类变量

public class Test {
private final String name; public Test(String name) {
this.name = name;
// System.out.println(test);//外部无法访问内部类变量
} public class Inner {//内部类也可以有成员变量、方法等,甚至可以套娃成员内部类
public String test;
public void test() {
System.out.println("我是内部类"+name);//可以访问外部变量和常量
}
}
} public class Main {
public static void main(java.lang.String[] args) {
Test test = new Test("小明");
Test.Inner inner = test.new Inner();
inner.test();//我是内部类小明
}
}

如果内部类定义了同名的变量,怎样明确要使用的是哪一个?

public class Test {
private final String name;//外部的变量 public Test(String name) {
this.name = name;
} public class Inner {//内部类也可以有成员变量、方法等,甚至可以套娃成员内部类
String name="inner";
public void test(String name) {//方法参数的name
System.out.println("方法参数的name,"+name);//就近原则
System.out.println("成员内部类的name,"+this.name);//使用this关键字访问内部类变量
System.out.println("外部的name,"+Test.this.name);//添加外部类名称来访问外部变量
}
}
} public class Main {
public static void main(java.lang.String[] args) {
Test test = new Test("out");
Test.Inner inner = test.new Inner();
inner.test("parameter");
/**
方法参数的name,parameter
成员内部类的name,inner
外部的name,out
*/
}
}

包括对方法的调用和super关键字的调用,也是一样的:

public class Test {
private final String name;//外部的变量 public Test(String name) {
this.name = name;
} @Override
public String toString() {
return "我是外部";
} public class Inner {//内部类也可以有成员变量、方法等,甚至可以套娃成员内部类
@Override
public String toString() {
return "我是内部类";
} public void test(String name) {//方法参数的name
System.out.println(this.toString());//内部类自己的toString
System.out.println(super.toString());//内部类父类的toString
System.out.println(Test.this.toString());//外部类的toString
System.out.println(Test.super.toString());//外部类父类的toString
}
}
} public class Main {
public static void main(java.lang.String[] args) {
Test test = new Test("out");
Test.Inner inner = test.new Inner();
inner.test("parameter");
/**
我是内部类
com.test.entity.Test$Inner@6bc7c054
我是外部
com.test.entity.Test@232204a1
*/
}
}

成员内部类一般在类的内部自己使用

静态内部类

前面的成员内部类是属于对象的;静态内部类是属于类的,可以直接创建使用

public class Test {
private final String name;//外部的变量 public Test(String name) {
this.name = name;
} public static class Inner {//内部类也可以有成员变量、方法等,甚至可以套娃成员内部类
public void test(){
System.out.println("我是静态内部类");
}
}
} public class Main {
public static void main(java.lang.String[] args) {
Test.Inner inner = new Test.Inner();//可以直接创建
inner.test();
/*我是静态内部类
*/
}
}

静态内部类由于是静态的,所有相对于外部来说,整个内部类都处于静态上下文(注意只是相对于外部来说),是无法访问到外部类的非静态内容的

因为静态内部类是属于外部类的,不依附任何对象

初始化顺序相关

public class Test {

    public static String test = "";

    static {
System.out.println("外部类初始化");
} public static class Inner{
static {
System.out.println("内部类初始化");
}
public static void test(){
System.out.println("我是静态内部类!");
}
}
} public class Main {
public static void main(java.lang.String[] args) {
Test.Inner.test();//只初始化了内部类
System.out.println(Test.test);//明确使用外部类时才会初始化
/*
内部类初始化
我是静态内部类!
外部类初始化
*/
}
}

局部内部类

局部内部类就像局部变量一样,可以在方法中定义

public class Test {
private final String name;//外部的变量 public Test(String name) {
this.name = name;
} public void test() {
class Inner {//直接在方法中创建局部内部类
}
Inner inner = new Inner();
}
}

既然在方法中声明的类,那作用范围也只能在方法中

只不过这种形式的使用频率很低

匿名内部类

你们内部类时使用频率非常高的一种内部类,它时局部内部类的简化版

在学习抽象类和接口时,明确说了不能直接new的方式创建一个抽象类或时接口对象

正常情况下,要创建一个抽象类的实例对象,只能对其进行继承,先实现未实现的方法,然后创建子类对象

但是我们可以使用匿名内部类,将其中抽象方法实现,并创建实例对象

public abstract class Student {
protected String name;
public abstract void test();
} public class Main {
public static void main(java.lang.String[] args) {
Student student = new Student() {
@Override
public void test() {
System.out.println("我是匿名内部类的实现");
}
};
student.test();
}
}

匿名内部类同样可以使用类中的属性(因为他本质上相当于对应类型的子类)

public class Main {
public static void main(java.lang.String[] args) {
Student student = new Student() {
int a;//因为本质相当于子类,所以定义一些子类属性完全没问题
void xxx(){}
@Override
public void test() {
name = "ceshi";//可以直接使用父类中的name变量
System.out.println("我是匿名内部类的实现"+name);//我是匿名内部类的实现ceshi }
};
student.test();
}
}

同样的,接口也可以通过这种匿名内部类的形式,直接创建一个匿名接口实现类:

public interface Study {
void study();
} public class Main {
public static void main(java.lang.String[] args) {
Study study = new Study() {
@Override
public void study() {
System.out.println("我在学习");
}
};
study.study();
}
}

普通类也可以这样创建匿名内部类,只不过意义不大

Lambda表达式

如果一个接口中有且仅有一个待实现的抽象方法,那我们可以将匿名内部类简写为Lambda表达式

public interface Study {
void study();
} public class Main {
public static void main(java.lang.String[] args) {
Study study = ()-> System.out.println("我是学习方法");//如果只有一行代码,花括号可以省略
study.study();
}
}

Lambda表达式的具体规范:

  • 标准格式:([参数类型 参数名称,]...) ->
  • 和匿名内部类不同,Lambda仅支持接口,不支持抽象类
  • 接口内部必须有且仅有一个抽象方法(可以有多个方法,但是必须保证其他方法有默认实现,必须留一个抽象方法出来)
  • 如果只有一行代码,花括号可以省略

有多个参数和返回值:

public interface Study {
String study(String a,String b);
} public class Main {
public static void main(java.lang.String[] args) {
Study study = (String a,String b) -> {
System.out.println("我是学习方法");
return "今天学会了" + a + b;
};
System.out.println(study.study("吃饭","睡觉"));
}
}

javase_note的更多相关文章

随机推荐

  1. 浙江某男子对多端应用开发工具HBuilderX在windows下安装的解说

    同学,学uni-app好啊,大致上写一套代码能生成这么多个平台的应用,我简单念一下,它们分别是Android应用.IOS应用.Web应用.微信小程序.支付宝小程序.百度小程序.字节跳动小程序.快应用. ...

  2. 力扣每日一题2023.1.16---1813. 句子相似性 III

    一个句子是由一些单词与它们之间的单个空格组成,且句子的开头和结尾没有多余空格.比方说,"Hello World" ,"HELLO" ,"hello w ...

  3. JAVA虚拟机20-基于栈的解释器执行过程示例

    1.准备代码 public int calc() { int a = 100; int b = 200; int c = 300; return (a + b) * c; } 2.使用javap -v ...

  4. Vue搭建项目的完整流程 如何搭建一个完整的vue项目 vue项目架构

    vue项目架构 技术栈:vue3.vue-router .vuex(和pinia).element plus .axios.ts.sass 1.安装vue3 脚手架+ ts vue create ad ...

  5. Powershell获取当前文件夹内所有一级子文件夹大小

    需求:查看Windows某个文件夹所有一级子文件夹大小,并按照从大到小排序 解决方案:使用Powershell脚本 脚本内容如下 function filesize () {   [string]$f ...

  6. JZOJ 5353. 【NOIP2017提高A组模拟9.9】村通网

    题目 为了加快社会主义现代化,建设新农村,农夫约(Farmer Jo)决定给农庄里每座建筑都连上互联网,方便未来随时随地网购农药. 他的农庄很大,有N 座建筑,但地理位置偏僻,网络信号很差. 一座建筑 ...

  7. 题解 Trie 但是你要最小化它的节点数量

    名字瞎取的 Description 给定 \(n\) 个字符串 \(s\),可以对 \(s_i\) 的字符打乱,将这些字符串加入一个 trie 里面求节点数量最小值. \(n\le 16, \sum ...

  8. Vulhub 漏洞学习之:Docker

    Vulhub 漏洞学习之:Docker 目录 Vulhub 漏洞学习之:Docker 1 docker daemon api 未授权访问漏洞 1.1 漏洞利用过程 1 docker daemon ap ...

  9. Postgresql WAL日志浅析

    一.预写日志(WAL) 预写式日志(Write Ahead Log,WAL)是保证数据完整性的一种标准方法.简单来说,WAL的中心概念是数据文件(存储着表和索引)的修改必须在这些动作被日志记录之后才被 ...

  10. 一文搞懂│http 和 https 的通信过程及区别

    目录 两者的区别 HTTP的通信过程 HTTPS的通信过程 两者的区别 端口: http 端口号是80,https 端口号是443 传输协议: http 是超文本传输协议,属于明文传输:https 是 ...