【Java基础】最全(也不一定)笔记整理向

个人学习笔记记录 | 课程参考:b站黑马程序员

了解Java

  • 特性:可移植性 | 企业级开发
  • 技术体系:JaveSE 标准版:核心和基础 | JaveEE 企业版:互联网企业级解决方案,充分被市场认可 | JavaME 小型版:移动端

DOS

Disk Operating System 磁盘操作系统 通过win+R输入cmd进入

  • 常用命令
    • md:创建文件夹
    • cd:进入文件夹
      • cd..:回上一级目录
      • cd\:回根目录
    • rd:删除文件夹。 ps:文件夹中不能有内容,只能递归删除
    • del:删除文件。ps:模糊匹配可删除该格式的所有文件,比如del *.txt删除了所有txt格式的文件。
    • dir:查询当前文件夹下有哪些文件夹
    • cls:清屏
    • exit:关闭cmd界面
    • D::切盘

开发程序 Hello World

流程:编写 --> 编译 -->运行

要求:后缀为java | 类名与代码文件名称相同

public class HelloWorld{ //产生class 文件
	public static void main(String[] args){
        System.out.println("Hello World");
    }
}

最重要的程序:Javac - 编译程序 | Java - 执行程序

执行原理

编程语言发展历史:机器–>汇编–>高级

原理:翻译成计算机底层可识别的机器语言(0和1)

JDK的组成(产品)、跨平台原理

  1. Java Development Kit:Java程序开发工具包:必须安装它才可以使用Java

    JDK 8 11 17 - LTS版本

  2. Java Runtime Environment:Java程序运行时的环境

  3. Java Virtual Machine:Java虚拟机,真正运行Java程序的地方

  4. 关系:

    1. JDK = JRE + 开发工具集(如Javac编译工具Javac和执行工具Java)
    2. JRE = JVM + Java SE标准类库(核心类库)(API)

什么是JVM,JRE,JDK?

JVM是Java虚拟机,是真正运行Java程序的地方;JVM加上Java SE标准类库或者也叫核心类库就组成了JRE,这是Java程序运行时的环境;JRE加上开发工具,比如编译工具Javac和执行工具Java就组成了我们常用的JDK,Javac程序开发工具包

跨平台:一次编译、处处可用,我们的程序只需要开发一次,就可以在各种安装了JVM的系统平台上。

Path环境变量和Java_HOME设置

Path的作用:记住程序的路径,方便在命令行窗口的任意目录驱动程序

新版本JDK安装时会自动配置javac和java的路径直接到Path环境变量中去,但老版本时需要自动配置(届时自己上网搜一下手动配置的教程)

字面量

告诉程序员,数据在程序中的书写格式

package com.yjy.literal;

public class LiteralDemo {
    public static void main(String[] args) {
        // 整数
        System.out.println("6");

        // 小数
        System.out.println("12.04");

        // 字符:必须用单引号,有且只能有一个字符
        System.out.println('你');
        System.out.println(' '); //空字符
        //特殊的字符: \n 换行 \t 一个tab
        System.out.println('\n');
        System.out.println('好');

        //字符串:必须是双引号
        System.out.println("你好");

        //布尔值 true false
        System.out.println(true);
    }
}

标识符与关键字

  • 类名 、变量名都是标识符。要求:数字、字母、下划线_、美元符$ 不能以数字开头、不能以关键字作为名字、区分大小写
    建议:类:大驼峰;变量:小驼峰

  • 有一些标识符,Java 语言已经赋予了其特殊的含义,只能用于特定的地方,这些特殊的标识符就是关键字 。简单来说,关键字是被赋予特殊含义的标识符 。比如,在我们的日常生活中,如果我们想要开一家店,则要给这个店起一个名字,起的这个“名字”就叫标识符。但是我们店的名字不能叫“医院”,因为“医院”这个名字已经被赋予了特殊的含义,而“医院”就是我们日常生活中的关键字。

  • Tips:所有的关键字都是小写的,在 IDE 中会以特殊颜色显示。default这个关键字很特殊,既属于程序控制,也属于类,方法和变量修饰符,还属于访问控制。在程序控制中,当在 switch 中匹配不到任何情况时,可以使用 default 来编写默认匹配的情况。在访问控制中,如果一个方法前没有任何修饰符,则默认会有一个修饰符 default,但是这个修饰符加上了就会报错。⚠️ true, false, 和 null 不是关键字,是字面值,也不可以作为标识符来使用。

变量

存储原理

二进制:0和1 8421

数据的最小单元:字节,byte,简称B,8位(bit,简称b)一组,不满8位的前面补零

字符:ASCII编码表

图片:无数个像素点,用0~255X255X255表示其颜色,存储的是每个像素点的RGB三个值的二进制

声音:脉冲调制PWM波,最终还是映射到二进制

八大数据类型

        byte b = 127; //在内存中占8位 (1个字节)
        short s = 13244;
        int i = 23; // 在内存中占32位 (4个字节)
        long l = 1234567894561234569L; // 整形字面量过大(超过int范围)时,需要在末尾加上 L 或 l,定义为long型

        float f = 3.14F; // 小数字面量默认是double类型,若需要成为float类型则需要在末尾加上 F 或 f
        double d = 3.1415;

        char c = '耶'; // 只能存一个字符哦~ 2个字节,C语言里面占1个

        boolean flag = true;

        String name = "开心"; //字符串类型,定义的变量用于记住一个字符串数据,这是一种引用数据类型

类型转换

  1. 自动:类型范围小的变量,可以直接赋值给类型范围大的。

    byte -> short -> int -> long -> float -> double
    ​char -> int -> long -> float -> double

  2. 表达式的自动类型转换

    表达式的最终结果由表达式的最高类型决定;

    在表达式中,byteshortchar是直接转换成int类型参与计算的。

    int a = 10;
    long b = 30;
    double c = a + b + 1.0; //最高类型是1.0 double型
    
    byte i = 10;
    short j = 20;
    int k = i + j; //最高类型是int型
    
    byte x = 10;
    byte y = 80;
    int b1 + b2; //转换成int进行运算了!
    
  3. 强制类型转换:大范围怎么转小范围?

    数据类型 变量2 = (数据类型)变量1 // 在IDEA中的快捷键:ALT + ENTER

    int a = 20;
    byte b = (byte)a; // 把a的后8位数据直接给b
    

    tips: 可能出现数据丢失。比如小数->整数:直接将小数部分丢掉

运算符

基本运算符

  • + :加法 | 连接符 ; 能算就算,不能算就连在一起
    int a = 9;
    int b = 2;
    System.out.println("abc" + a);//"abc9" ,不能算
    System.out.println(a + b);//10,能算
    System.out.println("y" + a + 'a');//y9a,不能算
    System.out.println(a + 'a' + "y");//106y,前面能做ASCII码运算,后面不能算
    
  • - *
  • /:两个整数相除结果还是整数
     int a = 9;
     int b = 2;
     System.out.println(a/b); //输出:4, 而不是 4.5
     System.out.println(1.0 * a/b); //如果一定要得到小数,在前面乘1.0
    
  • %:取余,获取的是两个数据做除法的余数

自增自减

  • ++ --:对变量加1或者减1,只能操作变量,不能操作字面量 | ++i先加后用,i++先用后加
    int m = 2;
    int n = 9;
    int result = ++m - --m + m++ - ++n - n-- + 1;
    System.out.println(result); // -16
    

赋值运算符

  • =
  • +=:数据累加,把别人的数据加给自己:a += b 等价于 a = a + b,且包含强制类型转换:a = (a的数据类型) a + b
  • -= *= /= %=:同上

关系运算符

  • > >= < <= == !=:判断某个数据是否符合条件,返回Boolean类型的值

逻辑运算符

  • &:与,都真才真
  • |:或,一真就真
  • !:非,取反,你真我假
    !(2>1)结果是false
  • ^:异或,前后条件相同为假,不同为真
    1>2^3>1结果是true

开发中更常用的,效率更高的方法:

  • &&:短路与(双与),与&的结果相同,但是执行速度较快,因为它的底层逻辑是判断左边的表达式如果为false就直接返回false了,不再判断右边的语句
  • ||:短路或(双或),与|的结果相同,但是过程也不同,左边为true就直接返回true
int m = 2;
int n = 9;
//双与
System.out.println(m > 9 && ++n > 2); //false
System.out.println(n); //9,因为右边表达式没执行
//单与
System.out.println(m > 9 & ++n > 2);//false
System.out.println(n);//10,因为右边表达式执行了

三元运算符

  • 条件表达式 ? 值1:值2 :判断条件表达式的真假,如果为true返回值1false返回值2

    //应用1 寻找2个数中的最大值
    int a = 2;
    int b = 1;
    System.out.println( a > b ? a : b); //2
    //应用2 寻找3个数中的最大值
    int c = 9;
    int temp = a > b ? a : b;
    System.out.println(c > temp ? c : temp); //9
    

优先级

在表达式中,运算符存在优先级,要是自己开发,不确定优先级的话就加个(),括号内的内容肯定是优先执行的
注意:&&的高于||

流程控制

顺序结构,一步一步

分支结构,有选择地

if

根据条件来选择执行某段程序,有三种写法

// 需求1:测量用户体温,发现体温高于37度就报警。
double t = 36.9;
if(t > 37){
    System.out.println("这个人的温度异常,把他赶紧带走~~");
}

// 需求2:发红包,你的钱包余额是99元,现在要发出90元, 如果钱够触发发红包的动作,如果钱不够,则提示:余额不足。
double money = 19;
if(money >= 90){
    System.out.println("发红包成功了~");
}else {
    System.out.println("余额不足~~");
}

// 需求3:某个公司有一个绩效系统,根据员工的打分输出对应的绩效级别。[0,60) D  [60,80) C [80,90) B [90,100] A
int score = 298;
if(score >= 0 && score < 60) {
    System.out.println("您的绩效级别是: D");
}else if(score >= 60 && score < 80){
    System.out.println("您的绩效级别是: C");
}else if(score >= 80 && score < 90){
    System.out.println("您的绩效级别是: B");
}else if(score >= 90 && score <= 100){
    System.out.println("您的绩效级别是: A");
}else {
    System.out.println("您录入的分数有毛病~~");
}

switch

比较表达式与case中的哪个值一样,就执行哪段程序

//        周一:埋头苦干,解决bug              周五:今晚吃鸡
//        周二:	请求大牛程序员帮忙             周六:与王婆介绍的小芳相亲
//        周三:今晚啤酒、龙虾、小烧烤      周日:郁郁寡欢、准备上班。
//        周四:主动帮助新来的女程序解决bug
String week = "周三";
switch (week){
    case "周一":
        System.out.println("埋头苦干,解决bug");
        break;
    case "周二":
        System.out.println("请求大牛程序员帮忙");
        break;
    case "周三":
        System.out.println("今晚啤酒、龙虾、小烧烤");
        break;
    case "周四":
        System.out.println("主动帮助新来的女程序解决bug");
        break;
    case "周五":
        System.out.println("今晚吃鸡");
        break;
    default: //其他情况
        System.out.println("您输入的星期信息不存在~~~");
}
//输出:今晚啤酒、龙虾、小烧烤
  • 注意事项:
  1. 表达式类型只能是byte short int char,JDK5后开始支持枚举,JDK7后支持String,不支持double (运算时不精确)float long

  2. case给出的值不允许重复,且只能是字面量,不能是变量。

  3. 正常使用switch的时候,不要忘记写break,否则会出现穿透现象。但有时穿透性可以简化代码

    String week = "周三";
    switch (week){
        case "周一":
            System.out.println("埋头苦干,解决bug");
            break;
        case "周二":
            System.out.println("请求大牛程序员帮忙");
            break;
        case "周三":
            System.out.println("今晚啤酒、龙虾、小烧烤");
        case "周四":
            System.out.println("主动帮助新来的女程序解决bug");
            break;
        default: //其他情况
            System.out.println("您输入的星期信息不存在~~~");
    }
    /*输出:今晚啤酒、龙虾、小烧烤
    主动帮助新来的女程序解决bug
    原因:周三语句没有写break,所以穿透到下一条语句了
    */
    

    但有时穿透性可以简化代码:当存在多个case分支的代码是一样时,可以把代码写到一个case块

    String week = "周三";
    switch (week){
        case "周一":
            System.out.println("埋头苦干,解决bug");
            break;
        case "周二":
        case "周三":
        case "周四":
            System.out.println("请求大牛程序员帮忙");
            break;
        default:
            System.out.println("您输入的星期信息不存在~~~");
    }
    输出:今晚啤酒、龙虾、小烧烤
    

比较与开发建议

if在功能上比switch强大:switch能做的if一定能做

区间使用if,一个一个值比较使用switch(格式良好,性能优雅)

循环结构,不断重复

for

for(int i = 0; i < 3; i++) {   // i = 0  1  2
    System.out.println("Hello World");
}

while

int i = 0;
while (i < 5) {  // i = 0 1 2 3 4
    System.out.println("Hello World");
    i++;
}

example - 需求:世界最高山峰珠穆朗玛峰高度是: 8848.86米=8848860毫米,假如我有一张足够大的纸,它的厚度是0.1毫米。请问:该纸张折叠多少次,可以折成珠穆朗玛峰的高度?

double paper = 0.1; // 纸张的厚度
int count = 0; // 折叠的次数
while (paper < 8848860){
    paper = paper * 2;
    count ++;
}

do-while

特点:先执行后判断,比如抢票

int i = 0;
do{
    System.out.println("Hello World");
    i++;
}while (i < 3);

比较与开发建议

  • for while功能是完全一样的,是先判断后执行;而do while是先执行后判断
  • 知道循环几次:使用for;不知道循环几次:使用while
  • for循环中,控制循环的变量只在for内部使用,在while循环中,控制循环的变量在循环后还可以使用

死循环

应用场景:服务器程序,比如打开一个百度服务器,它会一直运行等待用户操作

for ( ; ; ){
    System.out.println("Hello World1");
}
while (true) {
    System.out.println("Hello World2");
}
do {
    System.out.println("Hello World3");
}while (true);

循环嵌套

// 打印星星4行40列星星
for (int i = 1; i <= 4; i++) {
    // i = 1  2  3
    // 定义一个循环控制每行打印多少列星星。
    for (int j = 1; j <= 40; j++) {
        System.out.print("*"); // 不换行
    }
    System.out.println(); // 换行
}

跳转关键字

break

跳出并结束当前所在循环的执行;只能用于结束所在循环,或者结束所在switch分支的执行

// 场景:假如你有老婆了,你犯错了,你老婆罚你说:5句我爱你
// 说到第三句的时候心软了,让你别再说了。
for (int i = 1; i <= 5; i++) {
    System.out.println("我爱你:" + i);
    if(i == 3){
        // 说明已经说完了第三句了,心软了。
        break; // 跳出并结束当前所在循环的执行。
    }
}

continue

跳出当前循环的当次执行,直接进入循环的下一次执行;只能在循环中使用

// 场景: 假如你有老婆,你犯错了,你老婆罚你洗碗5天。
// 第三天的时候,你表现很好,第三天不用洗碗,但是不解恨,第四天还是要继续的。
for (int i = 1; i <= 5; i++) {
    if(i == 3) {
        // 已经到了第三天,第三天不用洗的。
        continue;
    }
    System.out.println("洗碗:" + i);
}

数组

静态数组

完整格式:数据类型[] 数组名 = new 数据类型[]{元素1,元素2 ,元素3… };

简化格式:数据类型[] 数组名 = { 元素1,元素2 ,元素3,… }也可写成 数据类型 数组名[] = { 元素1,元素2 ,元素3,… }

int[] arr = new int[]{2,1,9}; // 完整
int[] arr = {2,1,9};// 简化
String[] names = {"victoria","kristal","amber"};
  • 访问数组的下标是从0开始的,比如names[0] = victoria

  • 什么类型的数组只能存放什么类型的数据。

  • 数组变量名中存储的是数组在内存中的地址,数组是一种引用数据类型

  • 数组的长度属性length:获取length:System.out.println(数组名.length);,那么数组的最大索引表示为**length -1 **(前提是元素个数大于0)

    如果超出数组索引,执行程序出bug:Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3

  • 遍历:一个一个数据的访问;在IDEA中的快捷键ages.fori回车补全

    int[] ages = {12, 24, 36};
    //            0   1    2
    for (int i = 0; i < ages.length; i++) {
        System.out.println(ages[i]);
    }
    

    应用:求和

    int[] arr = {2,1,9};
    int sum = 0;
    for (int i = 0; i < arr.length; i++){
        sum += arr[i];
    }
    System.out.println(sum);
    }
    

动态数组

定义数组时先不存入具体的元素值,只确定数组存储的数据类型和数组的长度

格式:数据类型[] 数组名 = new 数据类型[长度]; 注意:一定别跟静态数组混合了,比如int[] arr = new int [3]{3,1,7};是不对的

int[] dongtaiArr = new int[3]; // 先定义
dongtaiArr[0] = 2; // 后附值
  • 元素默认值规则
数据类型默认值
byte\short\char\int\long0
float\double0.0
booleanfalse
类\接口\数组\Stringnull
        char[] arr = new char[3];
        System.out.println(arr[0]);//一个奇怪的0
        System.out.println((int)arr[0]);//0
double[] scores = new double[6]; //应用:求录入评委评分的平均数
Scanner score = new Scanner(System.in);
double sum = 0.0;
for(int i = 0; i < scores.length; i++){
    System.out.println("请输入第" + (i+1) + "位评委的成绩");
    scores[i] = score.nextDouble();
    sum += scores[i];
}
System.out.println("你的平均得分是:" + sum/6);

执行原理

运行一个Java程序,主要是将.class放在内存中的JVM中运行,主要需要关注JVM的三个区域:方法区、栈内存、堆内存

  • 方法区

    1. 放程序编译后的.class文件

    2. main

  • 栈内存

    方法运行时所进入的内存(把main放在这里),变量也存储在这里。普通变量直接存储变量值,引用类型的变量存储地址

  • 堆内存

    new 出来的东西会在这块内存中开辟空间并产生地址

简单说说int a = 20; int[] arr = new int[3]这两行代码的原理

  1. a是普通变量,其值为20直接存储在栈内存中
  2. new int[3]是创建一个数组对象,在堆内存中开辟三个连续空间存储3个整数
  3. arr是变量,在栈内存中存储了arr的数组对象在堆内存中的地址

多个变量指向同一个数组

当多个变量指向同一个数组时(多个变量存储的是同一个数组对象的地址),如果其中一个变量修改了数组对象里的内容,那么其他的变量指向的值也更改了

int[] arr1 = {11, 22, 33};

int[] arr2 = arr1; // 把int类型的数组变量arr1赋值给int类型的数组变量arr2

System.out.println(arr1); //[I@4eec7777
System.out.println(arr2); //[I@4eec7777 , 同一个地址

arr2[1] = 99; // 修改其中一个变量的数组对象内容
System.out.println(arr1[1]); // 99 ; 对应的其他的变量指向的值也更改了

arr2 = null; // 拿到的数组变量中存储的值是null
System.out.println(arr2); // arr2的地址变成null,不指向任何数组对象;

System.out.println(arr2[0]); //此时再访问arr2中的元素报错:Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "arr2" is null

方法

定义

方法是一种语法结构,它可以把一段代码封装成一个功能,以便重复调用

格式:

修饰符(public static) 返回值类型 方法名形参列表){

方法体代码(需要执行的功能代码)

return 返回值

}

public static void main(String[] args) {
int x = sum(2,9);
System.out.println(x);
}
public static int sum(int a, int b){ //定义求和的方法
int c = a + b;
return c;
}

Tips:

  • 一个方法不能定义在另一个方法的里面,比如必须写在main函数的外面,放前放后无所谓。

  • 方法不调用就不会执行

  • return后面的代码无效,写了也没用

  • 方法如果申明了具体的返回值类型,内部必须用return返回对应类型的数据;如果类型的void则无需有返回值,且一定不能写返回值

  • 形参列表可以有多个,也可以没有。多个形参必须用逗号隔开;

  • 按照方法解决的实际业务需求不同,设计出合理的方法形式来解决问题:考虑两个问题

    • 是否需要接收数据处理(即是否需要有形参)?

    • 是否需要返回数据(即是否需要return一个具体返回值类型)?

      • 无–> 使用void,不写return;只能直接调用
      • 有–> 使用其他数据类型,必须写return;可以直接调用、定义变量接收结果、直接输出调用
      sum(2,9); //直接调用
      int x = sum(2,9); //定义变量接收结果
      System.out.println(sum(2,9)) //直接输出调用
      
    • 方法要处理的业务(编程能力)

执行原理

方法被调用的时候是进入到栈内存(先进后出)中运行的,保证一个方法调用完另一个方法后可以回来

参数传递机制

基本类型的参数传递

值传递:在传输实参给方法形参的时候,传输的是实参变量中存储的值的副本。

public static void main(String[] args) {
    int a = 10; // 实参,在方法内部定义的变量
    change(a); // change(10);
    System.out.println("main:" + a); // 10
}
public static void change(int a){ //这里的a是形参,是指方法定义时的变量
    System.out.println("change1:" + a); // 10
    a = 20; // 实参,在方法内部定义的变量
    System.out.println("change2:" + a); // 20
}

引用类型的参数传递

值传递:但是注意传进去的是地址的副本,如果改变会有影响

public static void main(String[] args) {
    int[] arrs = new int[]{10, 20, 30};
    change(arrs); //传进去的是地址 [I@58372a00
    System.out.println("main:" + arrs[1]); //222
}
public static void change(int[] arrs){
    System.out.println("方法内1:" + arrs[1]); //20
    arrs[1] = 222; //改变了[I@58372a00地址值指向的堆内存中的内容
    System.out.println("方法内2:" + arrs[1]); //222
}

补充知识

方法重载(Overload)

一个类中,出现多个方法的名称相同,但它们的形参列表是不同的(个数、类型、顺序有一个不同就算不同,形参的名称不同,但是这三个都相同也不行),就称为方法重载。只要名称相同就是方法重载,其他都不管(比如修饰符 返回值类型是否一样都无所谓)

单独使用return关键字

在无返回值的方法中,使用return可以立即跳出并结束当前方法方法的执行。

面向对象编程

面向对象编程(Object-Oriented Programming,简称 OOP)是一种编程范式,它使用 “对象” 来设计软件和创建可重用的代码,每个对象都是一个特定类的实例。
定义类:定义变量 | 定义方法

public class Student {
    // 成员变量(对象的属性)
    String name;
    double chinese;
    double math;

    // 成员方法(对象的行为)
    public void printTotalScore(){
        System.out.println(name + "的总成绩是:" + (chinese + math));
    }

    public void printAverageScore(){
        System.out.println(name + "的平均成绩是:" + (chinese + math) / 2.0);
    }
}

创建类:输入变量 | 调用方法

public static void main(String[] args) {
    Student s1 = new Student();// 1、创建一个学生对象,封装播妞的数据
    s1.name = "播妞";
    s1.chinese = 100;
    s1.math = 100;
    s1.printTotalScore();
    s1.printAverageScore();
}
  • 为什么要用面向对象编程:符合人类思维习惯,编程更简单

  • 对象到底是什么:特殊的数据结构,就是一张表。

  • class也就是类,是对象的设计图(或者对象的模板):1. 变量-说明对象可以处理哪些数据,即表中有哪些字段 | 2.方法-描述对象有说明功能,即可以对这些数据进行什么样的处理

执行原理

在这里插入图片描述

注意事项

  • 类名建议英文首字母大写
  • 成员变量存在默认值,定义时一般不需要赋初始值
  • 一个代码文件中,可以写多个class类,但只有一个能用public修饰,且用public修饰的类名必须成为代码文件名
  • 对象1和对象2之间的数据不会互相影响,但多个变量指向同一个对象时就会互相影响,比如Student s2 = s1后 修改s2.name = 李四,再打印s1.name的结果会是:李四
  • 如果某个对象(在堆内存中)没有一个变量(在栈内存中)引用它,则该对象就是垃圾对象,无法操作。Java存在自动垃圾回收机制,会自动清除掉垃圾对象

this 关键字

  • this是一个引用变量,可以用在方法中,来拿到当前对象。谁调用它,里面存的就是谁的地址。当类的成员变量与局部变量重名时,我们可以使用 this 来区分它们。此外,this 还可以用于在一个构造函数中调用另一个构造函数。

  • 用途:解决对象的成员变量与方法内部变量名称冲突问题

    public class Student {
        String name;
        double score;
        public void test(Student this){
            System.out.println(this);
        }
    
        public void printPass(double score){
            if(this.score >= score){ // 这里解决了score变量名称冲突的问题,this.score就是对象 不然的话score >= score就不对了
                System.out.println("恭喜您,您成功考入了哈佛大学~~");
            }else {
                System.out.println("很遗憾,您没有考过~~");
            }
        }
    }
    
    Student s3 = new Student();
    s3.name = "播仔";
    s3.score = 2;
    s3.printPass(256);
    

构造器

  • 方法的名称和对象的名称是一样的,这时候的方法就不叫方法了,而是叫构造器

  • 特点:创建对象的时候,对象就会调用构造器

  • 应用场景:创建对象时,同时完成对对象成员变量(属性)的初始化赋值

    public class Student {
        String name;
        double score;
        // 无参数构造器
        public Student(){
            System.out.println("无参数构造器被触发执行了~"); //在创建对象时如果是 Student s1 = new Student();就会执行这个方法
        }
    
        // 有参数构造器
        public Student(String name, double score){
            System.out.println("有参数构造器被触发执行了~~");  //在创建对象时如果是 Student s1 = new Student(小美,10.0);就会执行这个方法,类似于方法重构
            this.name = name; //完成初始化赋值
            this.score = score;
        }
    }
    

    注意事项:

    • 设计类时,不写构造器,Java会自动生成一个无参构造器
    • 如果定义了有参数构造器,Java就不会生成无参构造器了,若此时还需要就手写一个无参构造器 (最好是每次都写上!)

封装

  • 用类设计对象处理某一个事物的数据时,应该把要处理的数据,和处理这些数据的方法,设计到一个对象中去
  • 设计规范:合理隐藏(private) | 合理暴露(public
public class Student { // 让外部能够合理访问score并合理赋值,而不会赋一些奇怪的值,安全可靠
    private double score; //私有变量,外部无法访问
    public void setScore(double score){ //但可以通过这个公有方法间接赋值
        if(score >= 0 && score <= 100){
            this.score = score;
        }
        else {
            System.out.println("数据非法!你输入的成绩应该在0~100区间");
        }
    }
    public double getScore(){ //也可以通过这个公有方法间接取值
        return this.score;
    }
}

实体JavaBean(实体类)

  • 一种特殊的类
    • 成员变量都要私有,且向外提供相应的getXxx方法和setXxx方法
    • 类中必须要有一个公共的无参的构造器
public class Student {
    private String name;// 1、必须私有成员变量,并为每个成员变量都提供get set方法
    private double score;

    public Student() { // 2、必须为类提供一个公开的无参数构造器
    }

    public Student(String name, double score) { 
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}
  • IDEA快捷键:右键 --> 生成 --> 构造函数 or getter and setter
  • 特点:仅仅是一个用来保存数据的Java类,不能用它去干其他事情了
  • why do this? 将保存数据处理数据分割成两个Java类来保存,比如一个是Student,一个是StudentOperator
public class StudentOperator {
    private Student student;
    public StudentOperator(Student student){
        this.student = student;
    }

    public void printPass(){
        if(student.getScore() >= 60){
            System.out.println(student.getName() + "学生成绩及格");
        }else {
            System.out.println(student.getName() + "学生成绩不及格");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student();//保存数据
        s1.setName("播妞");
        s1.setScore(99);
        System.out.println(s1.getName());
        System.out.println(s1.getScore()); 

        StudentOperator operator = new StudentOperator(s1);//处理数据
        operator.printPass();
    }
}

对象数组

对象也可以用数组的方式操作,比如定义了一个Movie类

private int id;
private String name;
private double price;

若想要打印所有Movie对象的中的某个值,它的操作类应该这样写:

public class MovieOperator {
    private Movie[] movies;
    public MovieOperator(Movie[] movies){ //这里用到了[]
        this.movies = movies;
    }

    public void printAllMovies(){
        System.out.println("-----系统全部电影信息如下:-------");
        for (int i = 0; i < movies.length; i++) {
            Movie m = movies[i];
            System.out.println("编号:" + m.getId());
            System.out.println("名称:" + m.getName());
            System.out.println("价格:" + m.getPrice());
            System.out.println("------------------------");
        }
    }

而主程序中录入Movie类的方式也可以用[]形式

 public static void main(String[] args) {
        Movie[] movies = new Movie[4];//这里用到了[]
        movies[0] = new Movie(1,"水门桥", 38.9);
        movies[1] = new Movie(2, "出拳吧", 39);
        movies[2] = new Movie(3,"月球陨落", 42);
        movies[3] = new Movie(4,"一点就到家", 35);

        MovieOperator operator = new MovieOperator(movies);
        operator.printAllMovies();
 }

成员变量和局部变量的区别

成员变量局部变量
类中的位置不同类中,方法外方法中
初始值不同有默认值,无需初始化无默认值,使用之前必须赋值
内存位置不同堆内存栈内存
作用域不同整个对象在所归属的大括号中
生命周期不同与对象同生共死方法调用而生,方法结束而死

继承

👉extends关键字:子类能够继承父类的非私有成员(成员变量、成员方法)

👉public class B extends AA是父类(基类、超类),B是子类(派生类)

继承后对象的创建:子类的对象是由子类、父类两张设计图共同完成的(创建一个对象后,对象里面包含子类和父类的所有属性),但是子类只能调用公有的成员。

权限修饰符

public(公有的)、private(私有的),protected(受保护的)、缺省(不写任何修饰符)。

是用来限制类中的成员(成员变量、成员方法、构造器、代码块…)能够被访问的范围。

修饰符本类里同一个包中的类子孙类任意类
private
缺省
protected
public

单继承

一个子类可以继承多个父类吗? 答案是 :dengdengdeng~NO!

  • Java语言只支持单继承,不支持多继承,但是可以多层继承。就像家族里儿子、爸爸和爷爷的关系一样:一个儿子只能有一个爸爸,不能有多个爸爸,但是爸爸也是有爸爸的。
  • Object类是Java中所有类的祖宗。

方法重写

子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。一句话总结:声明不变,重新实现

  • 注意:重写后,方法的访问,Java会遵循就近原则

  • 重写小tips:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确(更安全),代码可读性也会更好。比如说如果想重写父类的方法print1,但是写成了pritn1,此时重写是不会成功的,会报错:Method does not override method from its superclass

  • 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限( public > protected > 缺省 )

    public class A { 
        protected void print1(){ //这里的权限修饰符是protected
            System.out.println("111");
        }
    }
    
    public class B extends A{ //A的子类B 
        @Override 
        public void print1(){  //那么这里的修饰符只能是public或者protected 不能是缺省
            System.out.println("666");
        }
    }
    
  • 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小(后面解释)

  • 私有方法静态方法不能被重写,如果重写会报错的。

就近原则

public class F { //定义一个父类
    String name = "父类名字";
    public void print1(){
        System.out.println("==父类的print1方法执行==");
    }
}
public class Z extends F { //子类有一个同名的name成员变量,有一个同名的print1成员方法;
    String name = "子类名称";
    public void showName(){
        String name = "局部名称";
        System.out.println(name); // 局部名称
    }

    @Override
    public void print1(){
        System.out.println("==子类的print1方法执行了=");
    }

    public void showMethod(){
        print1(); // 子类的
    }
}
public class Test {
    public static void main(String[] args) {
        Z z = new Z();
        z.showName(); // 局部名称
        z.showMethod(); // "==子类的print1方法执行了="
    }
}
  • 如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分
public class Z extends F {
    String name = "子类名称";

    public void showName(){
        String name = "局部名称";
        System.out.println(name); // 局部名称
        System.out.println(this.name); // 子类成员变量
        System.out.println(super.name); // 父类的成员变量
    }

    @Override
    public void print1(){
        System.out.println("==子类的print1方法执行了=");
    }

    public void showMethod(){
        print1(); // 子类的
        super.print1(); // 父类的
    }
}

顺序:先子类局部返回找(函数内部) --> 再子类成员范围找 --> 父类成员范围找

子类中访问构造器的特点

  • 子类全部构造器,都会先调用父类构造器,再执行自己。
class F {
    public F() {
        System.out.println("父类F  |  无参构造器");
    }
}
class Z extends F {
    public Z() {
        // super();
        System.out.println("子类Z  |  无参构造器");
    }
    public Z(int id){
        System.out.println("子类Z  |  有参构造器");
    }
}
public class Test {
    public static void main(String[] args) {
        Z z1 = new Z();
        System.out.println("------------------");
        Z z2 = new Z(20);
    }
}

结果:在这里插入图片描述

为什么会这样?在子类的构造器函数中,默认第一行有个super();代码

  • 如果想调用父类的有参构造器怎么办:在子类的super()中手写出父类有参构造器的参数

    class F {
        public F(String name) {
            System.out.println("父类F  |  有参构造器");}}
    class Z extends F {
        public Z() {
            super("yjy");
            System.out.println("子类Z  |  无参构造器"); }}
    
    public class Test {
        public static void main(String[] args) {
             Z z1 = new Z();}}  //父类F  |  有参构造器 (换行) 子类Z  |  无参构造器
    

👉应用场景

  • 完成两张设计图有参构造器的融合;具体来说,子类构造器可以通过调用父类构造器,把对象中包含父类这部分数据先初始化赋值,再回来把对象里包含子类这部分的数据也进行初始化赋值

    public class Test {
        public static void main(String[] args) {
            Teacher t = new Teacher("张艳",33,"语文");//三个参数的构造器
            System.out.println(t.getName()); //张艳
            System.out.println(t.getAge()); //33
            System.out.println(t.getSkill()); //语文
        }
    }
    
    class Teacher extends People{
        private String skill;
        public Teacher(String name, int age, String skill){
            super(name, age); //用这一行,调用了People类中2个参数的构造器
            this.skill = skill; //构造自己的skill参数
        }
    	//getter & setter }
    
    class People {
        private String name;
        private int age;
        public People() {
        }
        public People(String name, int age) {
            this.name = name;
            this.age = age;
        }
    	//getter & setter }
    
  • 通过this()调用兄弟构造器
    在这里插入图片描述

👉小tips:在一个构造函数里,不能既有this()又有super(),因为都需要放在构造器的第一行

👉总结

访问本类成员:
	this.成员变量	//访问本类成员变量
	this.成员方法	//调用本类成员方法
	this()		   //调用本类空参数构造器
    this(参数)	  //调用本类有参数构造器
	
访问父类成员:
	super.成员变量	//访问父类成员变量
	super.成员方法	//调用父类成员方法
	super()		   //调用父类空参数构造器
    super(参数)	  //调用父类有参数构造器

final 关键字

定义:最终的意思,可以修饰(类、方法、变量)

  1. 修饰类:该类被称为最终类,特点是不能被继承了。

    final class A{}
    class B extends A{} //报错 无法从final A继承
    
  2. 修饰方法:该方法被称为最终方法,特点是不能被重写了。

    class A{
        public final void test(){
            System.out.println("a");}
    }
    
    class B extends A{
        @Override
        public void test(){// 报错 无法重写A中的test(),因为该方法为final
            System.out.println("b");
        }}
    
  3. 修饰变量:该变量只能被赋值一次。

    • final修饰基本类型的变量,变量存储的数据不能被改变。
    • final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。
    final int id = 5;
    id = 4; //报错 无法将值赋给final变量id
    
    final int[] arr = {2,1,9};
    arr = null; //这里会报错,因为算是第二次给arr赋值
    arr[1] = 200; //这里不会报错,可以修改数组元素
    

常量

定义:使用了 static final 修饰的成员变量就被称为常量

  • 作用:通常用于记录系统的配置信息。

  • 命名规范:大写英文单词,多个单词使用下划线连接起来

    public class Constant {
        public static final String SCHOOL_NAME = "中南大学";
    }
    
  • 好处:代码可读性,可维护性更好

  • 原理:程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。

多态

定义:多态是在继承、实现情况下的一种现象,表现为:对象多态、行为多态

  • Tip:成员变量没有“多态”的说法:
    • 变量:编译看左边, 运行看左边
    • 对象和方法:编译看左边, 运行看右边
public class People {
    String name = "父类";  // 成员变量
    public void run(){
        System.out.println("人会跑"); // 行为
    }}
public class Teacher extends People{ //对象
    String name = "老师";// 成员变量
    @Override
    public void run() {
        System.out.println("老师跑的比较慢~~~"); // 行为
    }}
public class Student extends People{
    String name = "学生";
    @Override
    public void run() {
        System.out.println("学生跑的飞快~~~");
    }}
public class Test {
    public static void main(String[] args) {
        People p1 = new Teacher(); //父类变量接收了子类对象 -【对象多态】
        System.out.println(p1.name);
        p1.run();// p1 p2都可以调用run方法,但是方法表现的行为不一样 - 【行为多态】

        People p2 = new Student();
        System.out.println(p2.name);
        p2.run(); 
    }}

多态的好处

  • 在多态形式下,右边对象是解耦合的,更便于扩展和维护(紧耦合,牵一发而动全身)

    • 比如People p1 = new Student();右边的Student对象可以随时任意切换
  • 可以使用父类类型的变量作为形参,可以接收一切子类对象

    public class Test2 {
        public static void main(String[] args) {
            Teacher t = new Teacher();
            go(t);
            Student s = new Student();
            go(s);
        }
    
        //参数People p既可以接收Student对象,也能接收Teacher对象。
        public static void go(People p){
            System.out.println("开始------------------------");
            p.run();
            System.out.println("结束------------------------");
        }}
    

产生的问题:

  • 多态下不能使用子类的独有功能

    比如在Teacher类中多了一个teach方法,在Student类中多了一个study方法,这两个方法在多态形式下是不能直接调用的。
    在这里插入图片描述

  • 解决方法:类型转换 -> 把父类变量转换为子类类型。

类型转换

  • 自动类型转换:父类 变量名 = new 子类(); i.e. People p = new Teacher();,这就是上面用的方法

  • 强制类型转换:子类 变量名 = (子类) 父类变量; i.e.Teacher p = (Teacher) People

    注意:原来是什么类型,才能强制还原成什么类型

    小tips:在转换之前写一个if语句,加上一个instanceof关键字进行判断

    if(父类变量 instanceof 子类){
        子类 变量名 = (子类)父类变量;
    }
    
    // i.e.
    People p = new Student();
    p.study();//会报错
    // but
    if(p instanceof Student){
        Student s = (Student)p;
        s.study();//不会报错
    }
    

    用这个关键字还可以很好的防止类型转换错误的异常,比如

    People p = new Teacher();
    Student s = (Student)p; 
    s.study();// 会报错,ClassCastException。因为p原来的子类是teacher子类,将它强转为student子类会报错
    //但此时加上if 和 instanceof判断,不会报错
    if(p instanceof Student){
        Student s = (Student)p;
        s.study();//不会报错
    }else {
        Teacher t = (Student)p;
        t.teach();
    }
    

抽象类

abstract 关键字

可以用abstract修饰类或方法,格式:

修饰符 abstract class 类名{ 
	修饰符 abstract 返回值类型 方法名称(形参列表)}
public abstract class A {   
    public abstract void test();// 抽象方法:必须abstract修饰,只有方法签名,不能有方法体。
}
  • tips:

    • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

    • 类该有的成员(成员变量、方法、构造器)抽象类都可以有

      public abstract class A { // 抽象类
          //成员变量
          private String name;
          static String schoolName;
      
          //构造方法
          public A(){
          }
      
          //抽象方法
          public abstract void test();
      
          //实例方法
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
      }
      
    • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现

      A a = new A(); //会报错
      
    • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类

      public class B extends A {
          @Override
          public void test() { //B类继承A类,必须复写test方法
          }
      }
      
      // 子类继承父类如果不复写父类的抽象方法,要想不出错,这个子类也必须是抽象类
      // 当B类也是抽象类继承A时,就可以不重写A类的抽象方法
      public abstract class B extends A {
      }
      

好处

1.用抽象类可以把父类中相同的代码,包括方法声明都抽取到父类,这样能更好的支持多态,一提高代码的灵活性。
2.反过来用,我们不知道系统未来具体的业务实现时,我们可以先定义抽象类,将来让子类去实现,以方便系统的扩展。

案例:某宠物游戏,需要管理猫、狗的数据。猫的数据有:名字;行为是:喵喵喵的叫~ | 狗的数据有:名字;行为是:汪汪汪的叫~

分析:多个类中只要有重复代码(包括相同的方法签名),我们都应该抽取到父类中去,此时,父类中就有可能存在只有方法签名的方法,这时,父类必定是一个抽象类了,我们抽出这样的抽象类,就是为了更好的支持多态。

public abstract class Animal {
    private String name;
    public abstract void cry();//动物叫的行为:不具体,是抽象的
    
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}

接着写一个Animal的子类,Dog类。代码如下

public class Dog extends Animal{
    public void cry(){
        System.out.println(getName() + "汪汪汪的叫~~");
    }
}

然后,再写一个Animal的子类,Cat类。代码如下

public class Cat extends Animal{
    public void cry(){
        System.out.println(getName() + "喵喵喵的叫~~");
    }
}

最后,再写一个测试类,Test类。

public class Test2 {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.cry();	//这时执行的是Dog类的cry方法
    }
}

再学一招,假设现在系统有需要加一个Pig类,也有叫的行为,这时候也很容易原有功能扩展。只需要让Pig类继承Animal,复写cry方法就行。

public class Pig extends Animal{
    @Override
    public void cry() {
        System.out.println(getName() + "嚯嚯嚯~~~");
    }
}

此时,创建对象时,让Animal接收Pig,就可以执行Pig的cry方法

public class Test2 {
    public static void main(String[] args) {
        Animal a = new Pig();
        a.cry();	//这时执行的是Pig类的cry方法
    }
}

包是分门别类管理程序的,类似于文件夹,建包有利于程序的管理和维护,语法格式如下:

package com.vk.javapackage; //建包
public class Hello{
}

在自己程序中调用其他包的程序的注意事项:

  • 同一个包下的程序,可以直接访问
  • 访问其他包下的程序,必须导包才可以使用:
package com.vk.Test;
import com.vk.javapackage //导包
public static void main(String[], args){
    Hello h1 = new Hello();
}

IDEA自动导包过程:
在这里插入图片描述

  • 自己的程序中调用Java提供的程序,也需要导包才可以使用,不过Java.lang包下的程序是不需要我们导包的,可以直接使用,比如StringSystem就不用,但是Random
  • 如果有两个路径(AB)下,包名一样(比如都叫Pkg),程序名也一样(比如都叫print),在调用print程序的时候,只能在头部导入其中一个路径的Pkg包(比如说此时导入了A路径),那么如果我还想用B路径中的print程序的话,必须要写全名B.Pkg.print来使用。
    import A.Pkg;
    psvm{
        Pkg demo = new Pkg();
        demo.print(); //这里会用A路径下的Pkg包中的print方法
        
        B.Pkg demo2 = new B.Pkg();
        demo2. print();//这里会用B路径下的Pkg包中的print方法
    }
    

String

代表字符串,可以创建对象封装字符串数据,并对字符串的进行处理
应用场景:用户登录 | 屏蔽脏话 | 百度文字匹配搜索

创建对象封装字符串数据

* 直接双引号得到字符串对象,封装字符串数据 
* `new String`创建字符串对象,并调用构造器初始化字符串
String name = "victoria";   // 1、直接双引号得到字符串对象,封装字符串数据
System.out.println(name); // 这里会直接打印出来name的字符串内容"victoria",而不是name的地址
// 2、new String创建字符串对象,并调用构造器初始化字符串
String rs1 = new String();
System.out.println(rs1); // 创建一个空白字符串对象,不含有任何内容

String rs2 = new String("hello"); // 不推荐
System.out.println(rs2);//根据传入的字符串内容,创建字符串对象

char[] chars = {'a', '宋', '茜'};
String rs3 = new String(chars);
System.out.println(rs3);//根据字符数组的内容,创建字符串对象

byte[] bytes = {97, 98, 99}; // 对应ASCII码是 a b c
String rs4 = new String(bytes);
System.out.println(rs4);// 根据字节数组的内容,创建字符串对象

常用方法

方法名说明
public int length()获取字符串的长度返回(就是字符个数)
public char charAt(int index)获取某个索引位置处的字符返回
public char[] toCharArray():将当前字符串转换成字符数组返回
public boolean equals(Object anObject)判断当前字符串与另一个字符串的内容一样,一样返回true
public boolean equalsIgnoreCase(String anotherString)判断当前字符串与另一个字符串的内容是否一样(忽略大小写)
public String substring(int beginIndex, int endIndex)根据开始和结束索引进行截取,得到新的字符串(包前不包后)
public String substring(int beginIndex)从传入的索引处截取,截取到末尾,得到新的字符串返回
public String replace(CharSequence target, CharSequence replacement)使用新值,将字符串中的旧值替换,得到新的字符串
public boolean contains(CharSequence s)判断字符串中是否包含了某个字符串
public boolean startsWith(String prefix)判断字符串是否以某个字符串内容开头,开头返回true,反之
public String[] split(String regex)把字符串按照某个字符串内容分割,并返回字符串数组回来

注意事项(Tips)

String的对象是不可变字符串对象,每次试图改变字符串对象实际上是新产生了新的字符串对象了,变量每次都是指向了新的字符串对象,之前字符串对象的内容确实是没有改变的,因此说String的对象是不可变的。

  • 只要是以直接双引号方式写出的字符串对象,会存储得到字符串常量池,且相同内容的字符串只存储一份

  • 但是通过new方式创建字符串对象,每new一次都会产生一个新的对象放在堆内存

    String s2 = new String("hhh");//这里创建了两个对象,一个hhh放在堆内存中,一个hhh放在堆内存的字符串常量池中
    String s1 = "hhh"; //这里创建了0个对象,s1直接指向放在堆内存的字符串常量池中的hhh
    System.out.println(s1 == s2); //false: s2的地址是堆内存中hhh的地址
    
    String s1 = "abc"; // 常量池
    String s2 = "ab"; // 常量池
    String s3 = s2 + "c"; //不是由双引号创建的,而是通过运算得到的,也是放在堆内存里,而没有放在常量池中
    System.out.println(s1 == s3); //false
    
    String s1 = "abc"; // 常量池
    String s2 = "a" + "b" + "c"; //这样的运算是有编译优化机制的,会直接转成"a + b + c"
    System.out.println(s1 == s2); // true
    
  • 案例:随机产生n位验证码,每位可能是数字、大写字母、小写字母

    public static void main(String[] args) {
        System.out.println(verifyCode(5));
    }
    public static String verifyCode(int n){
        String code = "";
        String allWord = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        Random rd = new Random();
        for (int i = 0; i < n; i++) {
            int index = rd.nextInt(allWord.length());
            code += allWord.charAt(index);
        }
        return code;
    }
    

ArrayList

创建

  • 集合:大小可变的容器,关键:创建 + +++,它存储的是每个对象在堆内存中的地址
  • 泛型编程:public class ArrayList<E>,其中<E>代表可以指定存储哪种类型的,如果不指定就是什么类型都有,推荐这样写:ArrayList<Object> list = new ArrayList<>();。但是注意不支持基本数据类型,只能支持引用数据类型,比如ArrayList<int> list = new ArrayList<>();int不行,得替换成Interger
  • 构造器:1. 无参:初始容量为10(基本上是用这个) | 2. ArrayList(int n):具有指定初始容量的空列表
// 无约束
ArrayList list = new ArrayList(); //无参构造
list.add(219);
System.out.println(list); //数组打印的是地址值,但是集合打印的是内容(因为重写了 toString)

//有约束 泛型形式
ArrayList<String> list2 = new ArrayList<>();// 约束为String了,其他类型不能放进去
list2.add("219"); 
System.out.println(list2);

常用方法

常用方法名说明
public boolean add(E e)将指定的元素添加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
ArrayList<String> list = new ArrayList<>();
list.add("吸引力");
list.add("情绪稳定");
list.add(1,"and");//向指定索引位置添加数据
list.add("精神独立");
System.out.println(list); //[吸引力, and, 情绪稳定, 精神独立]

System.out.println(list.get(2));//获取索引位置的数据:情绪稳定
System.out.println(list.size());//获取集合中元素的个数:4

list.remove(1);//删除集合中的元素
System.out.println(list);//[吸引力, 情绪稳定, 精神独立]

System.out.println(list.remove("吸引力"));//删除集合中指定的元素,返回是否删除成功
System.out.println(list);//[情绪稳定, 精神独立]

list.set(1,"坚持");
System.out.println(list);//[情绪稳定, 坚持]

案例

  • 从容器中找出某些数据并成功删除:现在假如购物车中存储了如下这些商品: 宁夏枸杞,黑枸杞,人字拖,特级枸杞,枸杞子。现在用户不想买枸杞了,选择了批量删除
    注意集合中边遍历边删除的一些坑

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("宁夏枸杞");
        list.add("黑枸杞");
        list.add("人字拖");
        list.add("特级枸杞");
        list.add("枸杞子");
        System.out.println(list);
        batchDelete2(list, "枸杞");
        System.out.println(list);
    }
    
    public static void batchDelete1(ArrayList<String> list, String product) {
        for (int i = 0; i < list.size(); i++) {
            String ele = list.get(i);
            if (ele.contains(product)) {
                list.remove(i);
                i--; //方法一: i 回退
            }
        }
    }
    
    public static void batchDelete2(ArrayList<String> list, String product) {
        for (int i = list.size() - 1; i >= 0; i--) { //方法二:倒序遍历,可以避免漏掉元素
            String ele = list.get(i);
            if (ele.contains(product)) {
                list.remove(i);
            }
        }
    }
    

static关键字

修饰成员变量

public class Student {
    static String name; //类变量
    int age; //实例变量
}

类变量

static修饰的变量,属于类,在计算机内存里只有一份,会被类的全部对象共享

  • 通过类.静态变量的方式调用。ps:也可以通过对象.静态变量的方式访问,但是不推荐
  • 与类一起执行,只会加载一次
  • 应用场景:如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。
    • 案例:系统启动后,要求用于类可以记住自己创建了多少个用户对象
      public class User{
          public static int number;//一般用public修饰
          public User(){
              User.number++;//使用构造器每次创建对象时,number自增一下
              //也可以直接写`number++;`  ,在同一个类中访问自己类的类变量可以省略类名
          }
      }
      
      public class Test{
          public static void main(String[] args){
              //创建4个对象
              new User();
              new User();
              new User();
              new User(); 
              System.out.println("创建User对象个数:"+User.number);//查看系统创建了多少个User对象
          }
      }
      

实例变量(对象的变量)

static修饰的变量,属于每个对象各自的变量

  • 只能通过对象.实例变量的方式调用
  • 应用场景:每个对象都要有一份,且数据各不同(如:name\score\age)

修饰成员方法

实例方法

无static修饰的方法,是属于对象的;

  • 调用时,需要创建对象后,使用对象调用(ps:不能用类访问。因为实例方法中可能会访问实例变量,而实例变量需要创建对象后才存在。

类方法

有static修饰的方法,是属于类的;

  • 它是随着类的加载而加载的;调用时直接用类名调用即可(ps:也可以使用对象名访问类方法,但不推荐。
public class Student{
    double score;
    //类方法:
    public static void printHelloWorld{
        System.out.println("Hello World!");
    }
    //实例方法(对象的方法)
    public void printPass(){
        System.out.println(score>=60?"成绩合格":"成绩不合格");
    }
}
public class Test{
    public static void main(String[] args){
        Student.printHelloWorld();//1.调用Student类中的类方法
        
        Student s = new Student();        
        s.printPass();//2.调用Student类中的实例方法
        
        s.printHelloWorld(); //使用对象也能调用类方法【不推荐,IDEA连提示都不给你,你就别这么用了】
    }
}

main方法

public static void main(String[] args)

  • 本质上就是类方法,通过类.main直接跑起来

  • 如何将数据传给main方法的参数args:执行的时候在后面填参数,但在实际开发中没啥用在这里插入图片描述

工具类

如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一下,所以把这样的类就叫做工具类。(典型工具类:math

  • 一般命名:xxUtils
  • 工具类中的方法都是类方法,每个类方法都是用来完成一个功能的
    案例:生成n位随机验证码
public class MyUtils{ 
    public static String createCode(int n){
        String code = ""; 
        String data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ";
        Random r = new Random();
        for(int i=0; i<n; i++){
            int index = r.nextInt(data.length());
            char ch = data.charAt(index);
            code+=ch;
        }
        return code;
    }
}
  • 接着可以在任何位置调用MyUtilscreateCode()方法产生任意个数的验证码
public class LoginDemo{//比如这是一个登录界面
    public static void main(String[] args){
        System.out.println(MyUtils.createCode());
    }
}
public class registerDemo{//比如这是一个注册界面
    public static void main(String[] args){
        System.out.println(MyUtils.createCode());
    }
}

小tips:工具类里的方法全都是静态的,推荐用类名调用为了防止使用者用对象调用。我们可以把工具类的构造方法私有化。

public class MyUtils{
    private MyUtils(){//私有化构造方法:这样别人就不能使用构造方法new对象了
    }
   
    public static String createCode(int n){ //公有化类方法
       ...
    }}
  • 为什么不直接使用实例方法:实例方法需要创建对象来调用,如果只是为了调用方法,没必要创一个会浪费内存的变量出来,而类方法直接使用类名调用,方便省内存

注意事项

  • ①类方法中可以直接访问类的成员(类变量或类方法),不可以直接访问实例成员(实例变量和实例方法)

    static String schoolName; // 类变量
    double score; // 实例变量
    
    public static void printHelloWorld(){
        // 注意:同一个类中,访问类成员,可以省略类名不写。
        schoolName = "黑马";  //可以直接访问类变量
        printHelloWorld2();  // 可以直接访问类方法
    
        System.out.println(score); // 报错,不可以直接访问实例变量
        printPass(); // 报错,不可以直接访问实例方法
        
        system.out.println(this); // 报错:见第三条注意事项
    }
    
    public static void printHelloWorld2(){   // 类方法 }
    public void printPass2(){   // 实例方法 }
    
  • ②实例方法中可以直接访问类成员和实例成员

    public void printPass(){
        schoolName = "黑马2"; //对的
        printHelloWorld2(); //对的
    
        System.out.println(score); //对的
        printPass2(); //对的
        system.out.println(this); // 对的:见第三条注意事项
    }
    
  • ③实例方法中可以出现this关键字,但是类方法中不可以出现this关键字

代码块

是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)

静态代码块

  • 格式:static{这里面放类初始化要执行的语句}
  • 特点:
    • 类加载时自动执行(不需要创建对象就能够执行)
    • 由于类只会加载一次,所以静态代码块也只会执行一次
  • 作用:完成类的初始化。比如:对类变量进行初始化赋值
public class Student {
    static int number = 80;
    static String schoolName = "一中";
    // 静态代码块
    static {
        System.out.println("静态代码块执行了~~");
        schoolName = "一中"; //对类变量进行初始化赋值
    }
}
public class Test {
    public static void main(String[] args) {
        System.out.println(Student.number);// 先输出"静态代码块执行了~~",才输出80
        System.out.println(Student.number);//80
        System.out.println(Student.number);//80
        System.out.println(Student.schoolName); // 一中
    }
}

实例代码块

  • 格式:{}
  • 特点
    • 每次创建对象时,执行实例代码块,并且在构造器前执行
    • 实例代码块每次创建对象之前都会执行一次
  • 作用:跟构造器一样,都是用来完成对象的初始化的。比如:对实例变量进行初始化赋值(但不建议);还可以减少有参构造器和无参构造器中出现的重复代码率(就是如果这两个构造器有重复代码就移到动态代码块中去)
public class Student{
	int age;//实例变量
    //实例代码块:实例代码块会执行在每一个构造方法之前
    {
        System.out.println("实例代码块执行了~~");
        age = 18;
        System.out.println("有人创建了对象:" + this);
    }

    public Student(){
        System.out.println("无参数构造器执行了~~");
    }

    public Student(String name){
        System.out.println("有参数构造器执行了~~");
    }
}
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student();
        Student s2 = new Student("张三"); 
        System.out.println(s1.age);  // 18
        System.out.println(s2.age); // 18 这样的话所有对象的age都只会是18
    }
}

实例代码块

  • 格式:{}
  • 特点
    • 每次创建对象时,执行实例代码块,并且在构造器前执行
    • 实例代码块每次创建对象之前都会执行一次
  • 作用:跟构造器一样,都是用来完成对象的初始化的。比如:对实例变量进行初始化赋值(但不建议);还可以减少有参构造器和无参构造器中出现的重复代码率(就是如果这两个构造器有重复代码就移到动态代码块中去)
public class Student{
	int age;//实例变量
    //实例代码块:实例代码块会执行在每一个构造方法之前
    {
        System.out.println("实例代码块执行了~~");
        age = 18;
        System.out.println("有人创建了对象:" + this);
    }

    public Student(){
        System.out.println("无参数构造器执行了~~");
    }

    public Student(String name){
        System.out.println("有参数构造器执行了~~");
    }
}
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student();
        Student s2 = new Student("张三"); 
        System.out.println(s1.age);  // 18
        System.out.println(s2.age); // 18 这样的话所有对象的age都只会是18
    }
}

接口

interface关键字

  • 格式:
public interface 接口名 {
       // 成员变量(常量)
       // 成员方法(抽象方法)
} 

案例:

public interface A{
    //这里public static final可以加,可以不加。
    public static final String SCHOOL_NAME = "黑马程序员";
    
    //这里的public abstract可以加,可以不加。
    public abstract void test();
}
public class Test{
    public static void main(String[] args){
        System.out.println(A.SCHOOL_NAME);//打印A接口中的常量
        A a = new A();//报错,接口是不能创建对象的
    }
}

implements关键字

  • 接口不能创建对象;接口是用来被类实现(implements)的,实现接口的类称为实现类
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {
} 
  • 一个可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类

比如,再定义一个B接口,里面有两个方法testb1()testb2()

public interface B {
    void testb1();
    void testb2();
}

接着,再定义一个C接口,里面有两个方法testc1(), testc2()

public interface C {
    void testc1();
    void testc2();
}

然后,再写一个实现类D,同时实现B接口和C接口,此时就需要复写四个方法,如下代码

public class D implements B, C{// 实现类
    @Override
    public void testb1() {
    }
    @Override
    public void testb2() {
    }
    @Override
    public void testc1() {
    }
    @Override
    public void testc2() {
    }
}

最后,定义一个测试类Test

public class Test {
    public static void main(String[] args) {
        System.out.println(A.SCHOOL_NAME);
        D d = new D();
    }
}

接口的好处

  • 弥补了类单继承的不足,一个类同时可以实现多个接口。通过接口,我们可以让一个类有一个亲爹的同时,还可以找多个干爹去扩展自己的功能。通过接口去找干爹,别人通过implements的接口,就可以显性的知道你是谁,从而也就可以放心的把你当作谁来用了。
  • 一个类我们说可以实现多个接口,同样,一个接口也可以被多个类实现的。让程序可以面向接口编程,这样程序员可以灵活方便的切换各种业务实现。
    案例:假设有一个Student学生类,还有一个Driver司机的接口,还有一个Singer歌手的接口。
    现在要写一个A类,想让他既是学生,偶然也是司机能够开车,偶尔也是歌手能够唱歌。那我们代码就可以这样设计,如下:
class Student{ //亲爹
}

interface Driver{ //干爹
    void drive();
}

interface Singer{ //干爹
    void sing();
}

//A类是Student的子类,同时也实现了Dirver接口和Singer接口
class A extends Student implements Driver, Singer{
    @Override
    public void drive() {
    }

    @Override
    public void sing() {
    }
}

public class Test {
    public static void main(String[] args) {
        Singer s = new A();//想唱歌的时候,A类对象就表现为Singer类型
        s.sing();
	
        Driver d = new A();//想开车的时候,A类对象就表现为Driver类型
        d.drive();
    }
}

接口中新增的方法(JDK8+)

好处:增强了接口的能力(原来都是一些抽象方法,但是新增的这些方法可以写方法体了),更便于项目的扩展和维护。

  • 默认方法:使用default修饰,使用实现类对象调用。

    public interface A{ //接口
        default void test1(){ //default修饰实例方法test1,必须调用实现类的对象来访问
            System.out.println("默认方法");
        }}
    
    public class B implements A{ //实现类
    }//由于没有A里抽象方法所以不需要重写
    
    psvm{
        B b = new B();
        b.test1();// 输出:默认方法 ; 通过创建对象来调用默认方法
    }
    
  • 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。

    public interface A{ //接口
        private void test2(){ //private修饰实例方法test2,在接口外面是访问不了的
            System.out.println("私有方法");
        }
        default void test1(){ //在接口内部可以访问
           test2();
        }}
    
  • 静态方法:static修饰,必须用当前接口名调用

    public interface A{ //接口
        static void test3(){ //static修饰实例方法test3,在接口外面用接口名调用
            System.out.println("静态方法");
        }}
    
    psvm{
        A.test3();// 直接使用接口名A来调用
    }
    
  • 他们都会默认被public修饰(接口就是供别人调用的,是要暴露出去的),所以可以不用带public

内部类

定义:如果一个类定义在另一个类的内部,这个类就是内部类,是类的五大成分之一(成员变量、方法、构造器、内部类、代码块)

场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类

成员内部类

类中的普通成员,类似于普通的成员变量、成员方法

public class Outer {
    private int age = 99;
    public static String a="lim";
    // 成员内部类
    public class Inner{
        private String name;
        private int age = 88;
        //在内部类中既可以访问自己类的成员,也可以访问外部类的成员
        public void test(){
            System.out.println(age); //88
            System.out.println(a);   //lim

            int age = 77;
            System.out.println(age); //77
            System.out.println(this.age); //88
            System.out.println(Outer.this.age); //99 拿到当前外部类的对象
        }
        // getter&setter
    }}
  • 成员内部类创建对象的格式:
//外部类.内部类 变量名 = new 外部类().new 内部类();
Outer.Inner in = new Outer().new Inner();
//调用内部类的方法
in.test();
  • 既可以访问内部类成员、也可以访问外部类成员(拿到当前外部类的对象:外部类名.this
  • 如果内部类成员和外部类成员同名,可以使用**类名.this.成员**区分

静态内部类

在成员内部类的前面加了一个static关键字。静态内部类属于外部类自己持有

静态内部类可以看作外部类的一个静态方法,因此是不能访问外部类的实例变量的。

public class Outer {
    private int age = 99;
    public static String schoolName="lim";

    // 静态内部类
    public static class Inner{
        //静态内部类访问外部类的静态变量,是可以的;
        //静态内部类访问外部类的实例变量,是不行的
        public void test(){
            System.out.println(schoolName); //lim
            //System.out.println(age);   //报错
        }}}
  • 静态内部类创建对象的格式:
//格式:外部类.内部类 变量名 = new 外部类.内部类();
Outer.Inner in = new Outer.Inner();
in.test();

匿名内部类

  • 匿名内部类是一种特殊的局部内部类;所谓匿名,指的是程序员不需要为这个类声明名字。

  • 本质上是一个没有名字的子类对象、或者接口的实现类对象

  • 作用:简化了创建子类对象、实现类对象的书写格式。核心就是四个字:简化代码

  • 格式:

new 父类/接口(参数值){
    @Override
    重写父类/接口的方法;
}
public abstract class Animal{ //定义一个Animal抽象类
    public abstract void cry();
}
public class Test{
    public static void main(String[] args){
        //这里隐含的有多态的特性: Animal a = Animal子类对象;
        Animal a = new Animal(){ //new Animal()就创建了一个Animal的子类
            @Override //子类进行方法重写
            public void cry(){
                System.out.println("猫喵喵喵的叫~~~");
            }
        }
        a.cry(); //猫喵喵喵的叫~~~
    }
}

需要注意的是,匿名内部类在编写代码时没有名字,编译后系统会为自动为匿名内部类生产字节码,字节码的名称会以外部类$1.class的方法命名

  • 使用场景:作为一个参数直接传给方法 简化了代码的书写
public interface Swimming{ //有一个游泳的接口
    public void swim();
}
public class Test{
    public static void main(String[] args){
        Swimming s1 = new Swimming(){ //创建了一个匿名类的对象s1
            public void swim(){
                System.out.println("狗刨飞快");
            }
        }; 
        go(s1); //将s1传递到go方法中完成游泳行为
        
        Swimming s1 = new Swimming(){//创建了一个匿名类的对象s1
            public void swim(){
                System.out.println("猴子游泳也还行");
            }
        };
        go(s1);//将s1传递到go方法中完成游泳行为
    }
    //形参是Swimming接口,实参可以接收任意Swimming接口的实现类对象
    public static void go(Swimming s){
        System.out.println("开始~~~~~~~~");
        s.swim();
        System.out.println("结束~~~~~~~~");
    }
}

上述代码也可以不创建s1对象,而是直接把匿名类传递到go方法中,如下所示:

public class Test{
    public static void main(String[] args){
        go(new Swimming(){
            @Override
            public void swim() {
                System.out.println("狗🏊‍飞快~~~~");
            }
        }); //直接把匿名类传到go中去了
        
        public static go (Swimming s){
            System.out.println("开始~~~~~~~~");
            s.swim();
            System.out.println("结束~~~~~~~~");
        }
    }
}

枚举

  • 定义:是一种特殊类,用enum关键字修饰

  • 第一行只能写一些名称,这些名称都是常量,且每个常量记住的都是枚举类的一个对象

  • 格式:

    public enum 枚举类名{
        枚举项1,枚举项2,枚举项3; //枚举项就表示枚举类的对象,这些对象在定义枚举类时就预先写好了,以后就只能用这几个固定的对象。
    }
    
    public enum A{
        X,Y,Z;
    }
    

    想要获取枚举类中的枚举项,只需要用类名调用就可以了

    public class Test{
        public static void main(String[] args){
            //获取枚举A类的,枚举项
            A a1 = A.X;
            sout(a1); //输出:X
            A a2 = A.Y;
            A a3 = A.Z;
        }}
    

    反编译后发现,枚举类A是用class定义的,说明枚举确实是一个类,而且X,Y,Z都是A类的对象;而且每一个枚举项都是被public static final 修饰,所以被可以类名调用,而且不能更改

正则表达式

正则表达式

作用:校验 | 查找 | 替换 | 分割

  • 校验字符串数据是否合法:matches(String regex) 匹配一个字符串是否匹配正则表达式的规则
  • 可以从一段文本中查找满足要求的内容
    在这里插入图片描述

案例:检查qq号是否合法

public static boolean checkQQ(String qq){
    return qq != null && qq.matches("[1-9]\\d{5,19}");
}

规则:

public class Regex {
    public static void main(String[] args) {
        // 1、字符类(只能匹配单个字符)
        System.out.println("a".matches("[abc]"));    // [abc]只能匹配a、b、c
        System.out.println("e".matches("[abcd]")); // false

        System.out.println("d".matches("[^abc]"));   // [^abc] 不能是abc
        System.out.println("a".matches("[^abc]"));  // false

        System.out.println("b".matches("[a-zA-Z]")); // [a-zA-Z] 只能是a-z A-Z的字符
        System.out.println("2".matches("[a-zA-Z]")); // false

        System.out.println("k".matches("[a-z&&[^bc]]")); // : a到z,除了b和c
        System.out.println("b".matches("[a-z&&[^bc]]")); // false

        System.out.println("ab".matches("[a-zA-Z0-9]")); // false 注意:以上带 [内容] 的规则都只能用于匹配单个字符

        // 2、预定义字符(只能匹配单个字符)  .  \d  \D   \s  \S  \w  \W
        System.out.println("徐".matches(".")); // .可以匹配任意字符
        System.out.println("徐徐".matches(".")); // false

        // \转义
        System.out.println("\"");
        // \n \t
        System.out.println("3".matches("\\d"));  // \d: 0-9
        System.out.println("a".matches("\\d"));  //false

        System.out.println(" ".matches("\\s"));   // \s: 代表一个空白字符
        System.out.println("a".matches("\\s")); // false

        System.out.println("a".matches("\\S"));  // \S: 代表一个非空白字符
        System.out.println(" ".matches("\\S")); // false

        System.out.println("a".matches("\\w"));  // \w: [a-zA-Z_0-9]
        System.out.println("_".matches("\\w")); // true
        System.out.println("徐".matches("\\w")); // false

        System.out.println("徐".matches("\\W"));  // [^\w]不能是a-zA-Z_0-9 
        System.out.println("a".matches("\\W"));  // false

        System.out.println("23232".matches("\\d")); // false 注意:以上预定义字符都只能匹配单个字符。

        // 3、数量词: ?   *   +   {n}   {n, }  {n, m}
        System.out.println("a".matches("\\w?"));   // ? 代表0次或1次
        System.out.println("".matches("\\w?"));    // true
        System.out.println("abc".matches("\\w?")); // false

        System.out.println("abc12".matches("\\w*"));   // * 代表0次或多次
        System.out.println("".matches("\\w*"));        // true
        System.out.println("abc12张".matches("\\w*")); // false

        System.out.println("abc12".matches("\\w+"));   // + 代表1次或多次
        System.out.println("".matches("\\w+"));       // false
        System.out.println("abc12张".matches("\\w+")); // false

        System.out.println("a3c".matches("\\w{3}"));   // {3} 代表要正好是n次
        System.out.println("abcd".matches("\\w{3}"));  // false
        System.out.println("abcd".matches("\\w{3,}"));     // {3,} 代表是>=3次
        System.out.println("ab".matches("\\w{3,}"));     // false
        System.out.println("abcde徐".matches("\\w{3,}"));     // false
        System.out.println("abc232d".matches("\\w{3,9}"));     // {3, 9} 代表是  大于等于3次,小于等于9次

        // 4、其他几个常用的符号:(?i)忽略大小写 、 或:| 、  分组:()
        System.out.println("abc".matches("(?i)abc")); // true
        System.out.println("ABC".matches("(?i)abc")); // true
        System.out.println("aBc".matches("a((?i)b)c")); // true
        System.out.println("ABc".matches("a((?i)b)c")); // false

        // 需求1:要求要么是3个小写字母,要么是3个数字。
        System.out.println("abc".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("ABC".matches("[a-z]{3}|\\d{3}")); // false
        System.out.println("123".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("A12".matches("[a-z]{3}|\\d{3}")); // false

        // 需求2:必须是”我爱“开头,中间可以是至少一个”编程“,最后至少是1个”666“
        System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));
        System.out.println("我爱编程编程66666".matches("我爱(编程)+(666)+"));
    }}

校验

  • 校验手机号码:18676769999、010-3424242424、0104644535,phone.matches("(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})"
public class RegexTest3 {
    public static void main(String[] args) {
        checkPhone();
    }

    public static void checkPhone(){
        while (true) {
            System.out.println("请您输入您的电话号码(手机|座机): ");
            Scanner sc = new Scanner(System.in);
            String phone = sc.nextLine();
            if(phone.matches("(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})")){
                System.out.println("您输入的号码格式正确~~~");
                break;
            }else {
                System.out.println("您输入的号码格式不正确~~~");
            }}}}
  • 校验邮箱:email.matches("\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}")

    /**正确案例
     * dlei0009@163.com
     * 25143242@qq.com
     * itheima@itcast.com.cn
     */
    
public class RegexTest3 {
    public static void main(String[] args) {
        checkEmail();
    }

    public static void checkEmail(){
        while (true) {
            System.out.println("请您输入您的邮箱: ");
            Scanner sc = new Scanner(System.in);
            String email = sc.nextLine();

            if(email.matches("\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}")){
                System.out.println("您输入的邮箱格式正确~~~");
                break;
            }else {
                System.out.println("您输入的邮箱格式不正确~~~");
            }}}}

查找

  • 需求1:从以下内容中爬取出,手机,邮箱,座机、400电话等信息。
public class RegexTest4 {
    public static void main(String[] args) {
        method1();
    }
   
    public static void method1(){
        String data = " 来黑马程序员学习Java,\n" +
                "        电话:1866668888,18699997777\n" +
                "        或者联系邮箱:boniu@itcast.cn,\n" +
                "        座机电话:01036517895,010-98951256\n" +
                "        邮箱:bozai@itcast.cn,\n" +
                "        邮箱:dlei0009@163.com,\n" +
                "        热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";
        // 1、定义爬取规则
        String regex = "(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})|(\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2})"
                + "|(400-?\\d{3,7}-?\\d{3,7})";
        // 2、把正则表达式封装成一个Pattern对象
        Pattern pattern = Pattern.compile(regex);
        // 3、通过pattern对象去获取查找内容的匹配器对象。
        Matcher matcher = pattern.matcher(data);
        // 4、定义一个循环开始爬取信息
        while (matcher.find()){
            String rs = matcher.group(); // 获取到了找到的内容了。
            System.out.println(rs);
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值