JavaSE总结以及所有练习笔记(2022.4.28)

Java基础及面向对象笔记

目录:JavaSE总结

一、基本数据类型

Java中的 JDK 和 JRE 的区别?

JDK:它是提供了Java的开发环境,提供了编译器javac的工具,包含了JRE(也就是Java的运行环境)也称虚拟机

JRE:它是Java中的运行环境,Java之所以能够实现跨平台都是因为有JDK(里面包含JRE虚拟机JVM)从而实现跨平台

  • 首先呢,Java是一门强类型语言,使用时要求变量要严格符合规定,所有变量都必须先定义后才能使用。
  • Java的数据类型分为两大类:基本数据类型(primitive type)、引用数据类型(refernce type).
  • 基本数据类型分为:数值类型boolean类型
  • 整数类型分为:byte、short、int、long.
  • 浮点类型分为:folat、double.
  • 字符类型char:只能存储单个字符.
  • boolean类型:只能存储两个值true、false.
  • 引用数据类型**分为三种分别是:(class)、接口(interface)、数组([]).

二、拓展知识

​ 1. 整数拓展:进制、二进制(0b开头)、十进制、八进制(0开头)、十六进制(0x开头). 如:

  public static void main(String[] args) {
        int i = 10;
        int i2=010;       // 八进制用0开头
        int i3=0x10;      // 十六进制用0x开头

        System.out.println(i);
        System.out.println(i2);
        System.out.println(i3);
    }
输出:
	10
	8
	16

​ 2. 浮点型拓展:注意:尽量不要使用浮点型的数据进行比较,因为总会有误差,比出来的值会是大约的数字,不够精准,因为会有四舍五入,所以要用的话就用BigDecimal(数学工具类)如:

   public static void main(String[] args) {  
    // 拓展浮点数类型
        float f = 0.1f;
        double d = 1.0/10;
        System.out.println(f==d); // == 相当于判断两个变量是否相等
    }
输出:
	false

​ 3. 字符拓展:可以在输出字符的变量名前加一个int进行强制类型转换,所有的字符本质还是数值,只不过底层有Unicode(表)编码进行编译,如: (a=表中的97)

  public static void main(String[] args) {
     //  拓展字符类型
        char c1 = 'A';
        char c2 = '好';
        System.out.println(c1);
        System.out.println((int)c1);
        System.out.println(c2);
        System.out.println((int)c2);
    }
输出:
	A
	6522909

​ 4. 转义字符拓展:

  public static void main(String[] args) {
        // 拓展转义字符
        // /n:表示换行    /t:表示空格
        System.out.println("每天进步一点点!\n冰冻三尺非一日之寒!");
        System.out.println("保持自律就是最好的天赋!\tJava你好!");
    }
输出:
	每天进步一点点!
	冰冻三尺非一日之寒!
	保持自律就是最好的天赋!	Java你好!

三、类型转换

  • 由于Java是强类型语言,所以要进行有些运算的类型不匹配问题,这时候就需要强制类型转换。
  • 由低------>>高 byte–>short–>int–>long–>float–>double
  • 运算中,不同类型的数据先转化为同一种类型,然后在进行计算
  • 类型转换分为两种:强制类型转换自动类型转换
  • 强制类型转换:是由高(容量)到低(容量) 比如:让int转换为byte

强制类型转换如:

    public static void main(String[] args) {
        int i = 129;           
        byte b = (byte)i;
        System.out.println(i);
        System.out.println(b);
    }
输出:
	129
	-127

​ 首先呢,int为高容量的数据类型要想转换为低容量类型的,就必须要强制类型转换。

语法:(需要强转的数据类型)变量名

疑问:根据这个输出来看,这个已经强转过了,怎么会出现负数呢

​ 这样的情况属于内存溢出,因为byte的最大值才是127,我们存了个129,所以就会产生内存溢出的问题,因此就会出现负数的情况; 所以解决这个内存溢出的办法就是赋的值不能超过数据类型的长度就可以了。

自动类型转换:是由低(容量类型)到高(容量类型),是不需要写任何语法的

自动类型转换如:

    public static void main(String[] args) {
        byte b = 12;
        int i = b;
        System.out.println(b);
        System.out.println(i);
    }
输出:
	12
	12

注意点:1. 不能对布尔值进行转换 2. 不能把对象类型转化为不相干的类型 3.再把高容量转换到低容量的时候,需要强制转换 4. 转换的时候可能会存在内存溢出或者精度问题!

精度问题:

 public static void main(String[] args) {
        // 精度问题
        System.out.println((int)31.2);   // 将double类型转换为int类型
        System.out.println((int)3.12f);  // 将float类型转换为int类型
    }
输出:
	31
	3

这个根据输出结果可以完全看出强制转换后发现小数点后面的都没了,所以这就体现出了精度问题。

注意:转换过程中存在溢出的问题如:

    public static void main(String[] args) {
        // 操作比较大的书的时候,注意溢出问题
        // JDK7 新特性,数字之间可以用下划线进行分割
        int money = 1_0000_00000;
        int years = 20;
        int tall = money*years;
        System.out.println(tall);    // 出来的结果是 -1474836480,这就是在计算的过程中溢出了
        // 首先需要把一个树转换为Long
        long tall1 = money*(long)years;
        System.out.println(tall1);    // 这样转换完之后才是正确的  20000000000
    }
输出:
	-1474836480
     20000000000

1、变量、常量

01. 变量

简而言之,就是可以变化的量!

​ 在Java中每个变量都必须声明其类型,Java中的变量是程序中最基本的存储单元,其要素包括(变量名、变量类型、以及作用域)。

注意事项:

  • 每个变量都有类型,类型可以是基本类型,也可以是引用类型。
  • 变量名必须是合法的标识符。
  • 变量声明是一条完整的语句,因此每一个声明都必须以分号结束

变量作用域:分为三种类变量、实例变量、局部变量。如:

public class Demo4 {
  
    // 类变量也叫全局变量   // 在类型前面必须要加static关键字
    static int i1 = 100000;
    // 属性变量
    // 实例变量;从属于对象 如果不自行初始化,这个类型的默认值 0  null
    String name;
    int age;
    // main方法
    public static void main(String[] args) {
        // 局部变量,必须声明和初始化值  只能在此方法中使用
        int i = 100;
        System.out.println(i);
        Demo4 demo4 = new Demo4();     // 实例化对象
        System.out.println(demo4.age);
        System.out.println(demo4.name);
        System.out.println(i1);   // 这里的i1输出的是类变量
    }
  输出:
    	100
		0
		null
		100000

可以明显看出三中变量的用法,注意的是类变量在声明之前必须在数据类型前面加上static关键字,就可以使用。

02. 常量

初始化后就不能再次修改,不会变动的值。

  • 所谓常量可以理解为一种特殊的变量,它的值设定后,在程序运行过程中不允许被修改。

    语法:final 常量名=值;final int PI = 312;

  • 常量名一般用大写字符

 // static为访问修饰符,不存在先后顺序
    static final int PI1 = 312;
    final static int PI2 = 312;
    public static void main(String[] args) {
        System.out.println(PI1);
        System.out.println(PI2);
    }
输出:
	312
	312

变量的命名规范:

  • 所有变量、方法、类名等都要见名知意
  • 类成员变量:首字母小写和驼峰式命名原则:monthSalary 第二单词首字母往往大写会比较好如:lastName
  • 局部变量:大写字母和下划线:MAX_VALUE
  • 类名:首字母大写和驼峰命名原则:Man,GoodMan
  • 方法名:首字母小写和驼峰命名原则:run(),runRun()

2、运算符

Java语言支持如下运算符:

  • 算术运算符:+,-,*,/,%,++,–
  • 赋值运算符:=
  • 关系运算符:>,<,<=,==,!=instanceof
  • 逻辑运算符:&&,||,!
  • 位运算符:&,|,^,~,>>,<<,>>>(了解!)
  • 条件运算符:?:
  • 扩展赋值运算符:+=,-=,*=,/=
01. 关系运算符

关系运算符进行比较时返回只会有两种:正确/错误 所以它会自动使用boolean进行返回,以及“%”取余。如:

  public static void main(String[] args) {
        // 关系运算符返回的结果只有两个 要么是正确要么是错误  所以返回类型为 boolean 类型
        int a = 1;
        int b = 2;
        int c = 3;
        System.out.println(a>b);    // false
        System.out.println(a<b);    // true
        System.out.println(a<c);    // true
        System.out.println(c%b);    // 相当于 c / b = 3 / 2  余数为 1 所以会返回 1;%取余也叫模运算
    }
输出:
	false
	true
	true
	1

**01.1: ++ – 分别为自增和自减(每次执行一次都会增加1或者减1),但是会有个++在前和++在后的问题.**如

    public static void main(String[] args) {
        int a1 = 1;
        int a2 = a1++; 
    // 这个是它先把 a1++(也就是初始值1)赋值给a2然后在进行自增所以a2还是等于1;因为它是先赋值的再自增
        System.out.println("先赋值后自增的样子:"+a1);
        System.out.println("此时的a2就是刚才a++的初始值:"+a2);   // 所以此时 a2 还是等于 1
        int a3 = ++a1;    
    // 这个 ++a1 相当于先是把 a1+加上一个1 然后在进行赋值  所以此时 a1 就会等于 3
        System.out.println("先自增后赋值的样子:"+a3);
    }
输出:
	先赋值后自增的样子:2
	此时的a2就是刚才a++的初始值:1
	先自增后赋值的样子:3

代码就是说:++如果在前的话先自增后赋值,相反++要是在后的话就是先赋值后自增;–也同上述如此

01.2: 初识Math类:这个类是Java中自带的一个用于操作数字运算的一个工具类,从而让处理数字的操作变得简单易懂。如:

   public static void main(String[] args) {
    // 初识 Math 类
        double pow = Math.pow(2, 3);   // 属性 pow 是幂次方的计算 也就是说2的3次方是几?
        System.out.println(pow);    // 输出为8
    }
输出:
	8.0
02.逻辑运算符(或与非)
   public static void main(String[] args) {
        // 与(and)  或(or)   非(取反)
        boolean a = true;
        boolean b = false;
        System.out.println("a和b:"+(a&&b));   
     //(&&)与:比较的两个变量中其中一个为假,则结果全部为假,会存在短路问题;那么a与b肯定为false(假)
        System.out.println("a||b:"+(a||b));  
     //(||)或:比较的两个变量中其中一个为真,则结果全部为真,那么a或b应该输出为true(真)
        System.out.println("!(a&&b):"+!(a&&b));
     // 非:如果变量结果为真则变为假,如果是假,则变为真;结果本来为假的则变为真,所以会输出true(真)
    }
输出:
	a和b:false
	a||b:true
	!(a&&b)true

短路运算如:

   public static void main(String[] args) {
    // 短路运算
        int b = 5;
        boolean d = (b<4)&&(b++<6);   
     // 就是在比较的过程中这个b小于4肯定是不对的,所以他不会继续执行后面的b++,
     // 虽然b++小于6是正确的但是依然不会执行,这就是短路问题
        System.out.println(b);  // 结果肯定会输出之前没有做过处理的数值5
        System.out.println(d);  // 这个等式不能成立所以会输出false
    }
输出:
	5
	false
03位运算符:
 public static void main(String[] args) {
        // 如果快速计算 2*8  << 左移相当与给数字*2   右移>>相当于给数字/2
        System.out.println(2<<3);  // 也就是等于3个2相乘
        System.out.println(2<<4);  // 相当于4个2相乘
        // 3*5
        System.out.println(30>>1);
        // 5*5
        System.out.println(50>>1);  // 相当于50/2
        // 7*7
        System.out.println(98>>1);
    }
输出:
	16
	32
	15
	25
	49

根据上述得知:<<左移是给当前的数字*2,右移是给当前的数字/2;据总结得知,左移的数字都是偶数,如2,4,6,8,10……;右移是奇数如:1,3,5,7,9……

04.三元运算符

语法:“? :” 如:“x ? y : z” 意思就是说:如果x的值为true则返回结果为y,否则返回z.如下:

  public static void main(String[] args) {
        // 语法:x ? y : z      如果x的值为 true 则结果是 y 否则结果就是z
        int i = 100;
        String tall = i==10?"聪明,答对了!": "笨蛋,错误!";   
        System.out.println(tall);   // 1

        int age = 19;       // 2
        String nl = age<=19?"是帅哥!":"太丑了!";   
    // 解析:age如果等于19或者小于19就会返回"是帅哥!”否则返回"太丑了!"
        System.out.println(nl);

        int nob1 = 20;
        int nob2 = 22;
        String nb1 = nob1<=19?"奥里给!":"正能量!";
        String nb2 = nob2>=100?"对!":"错!";
        String nb3 = nob1<30||nob1>30?"正确!":"错误!";  // 解析:nob1小于30或者nob1大于30
        System.out.println(nb1);
        System.out.println(nb2);
        System.out.println(nb3);

    }

解析:

  • 1.可以明显的看出程序中的 i 等于10一定是错误的,所以它这个等式就会不成立,返回结果才为"笨蛋,错误!".
  • 2.也就是说age如果等于19或者小于19就会返回"是帅哥!”否则返回"太丑了!"
  • 3.先看第一个等式:nob1如果小于或者等于19则返回“正能量”,因为nob1已经比19大了所以等式是错误的就会返回冒号后面的结果,否则就会返回冒号后面的结果!再看第二个等式:如果nob2>=100会返回"错",因为它nob2没有大于100,所以等式还是没有成立; 第三个等式: 这个就是说nob1小于30或者nob1大于30它先会看第一个nob1是不是小于30,如果小于则输出正确,否则错误!

3、Java Doc(Api文档)

Javadoc命令是用来生成自己的API文档

参数信息:

  • @author 作者名
  • @version 版本号
  • @since 指明需要最早使用的jdk版本
  • @param 参数名
  • @return 返回值情况
  • @throws 异常抛出情况

01.先找到项目在本地存放的位置:如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KfYA5L7q-1651160221091)(G:\日复一日的学习\学习图片\docApi\doc1.jpg)]

02.然后在地址最前面输入cmd+空格进入到dos命令窗口并且执行:

javadoc -encoding UTF-8 -charset UTF-8 Doc.java这行命令就是生成Doc这个类的API文档并且汉化.

如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tFQEc8yu-1651160221093)(G:\日复一日的学习\学习图片\docApi\doc2.jpg)]

03.生成后刚才最开始的项目文件夹会多的很多东西其中最重要的就是这个index.html:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ztfUozZ7-1651160221094)(G:\日复一日的学习\学习图片\docApi\doc3.jpg)]

04.这个时候呢就已经成功的生成了一个Doc类的API文档:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XezmVjYT-1651160221095)(G:\日复一日的学习\学习图片\docApi\doc4.jpg)]

二、流程控制

1.Scanner对象

  • jdk5新特性,工具类主要用于用户的输入
  • 基本语法:Scanner s = new Scanner(System.in);
  • 通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取我们一般需要,使用hasNext与hasNextLine()判断是否还有输入的数据。

注意:相当于在Scanner类中hasNext()和hasNextLine()是输入数据,而next()和nextLine()是输出。

两者区别:使用next()方法进行读取的用户输入的内容时,不会输出空格后的字符;相反nextLine()方法可以

  • next()方法:以空格为结束符,不能得到带空格后的字符串
    public static void main(String[] args) {
        // 创建一个扫描对象,对于接收键盘的数据
        Scanner scanner = new Scanner(System.in);
        System.out.println("使用next方式接收:");
        if(scanner.hasNext()){       // 判断用户有没有输入
            String s = scanner.next();    // 使用next进行获取用户输入的信息  也叫做输出
            System.out.println("用户输入的数据为:"+s);
        }
    scanner.close();
    }
输出:
	使用next方式接收:
	你好 Java!
	用户输入的数据为:你好       // 不能输出空格后的字符串
  • nextLine()方法:以回车键为结束符,回车之前的所有数据都能输出可以带有空格
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("使用nextLine方式:");
//        System.out.println(scanner.hasNextLine());
        if (scanner.hasNextLine()){
            String next = scanner.nextLine();
            System.out.println("用户输入的数据为:"+next);
        }
            scanner.close();
    }
输出:
	使用nextLine方式:
	你好 Java!
	用户输入的数据为:你好 Java// 这个nextLine()方法可以输出空格后的字符串

小练习:

01.男生女生向前冲报名网

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("================男生女生向前冲报名网================");
        System.out.println("请输入您的姓名:");
        String a= scanner.next();
        System.out.println("请输入您的年龄:");
        int b = scanner.nextInt();
        System.out.println("请输入您的性别:");
        String c = scanner.next();
        System.out.println("请输入您的联系方式:");
        String d = scanner.next();
        System.out.println("恭喜您报名成功!");
        System.out.println("您的报名信息为:姓名:"+a+"年龄:"+b+"性别:"+c+"联系方式:"+d);
        scanner.close();
    }
输出:
	================男生女生向前冲报名网================
	请输入您的姓名:
	张三
	请输入您的年龄:
	18
	请输入您的性别:
	男
	请输入您的联系方式:
	18791510258
	恭喜您报名成功!
	您的报名信息为:姓名:张三年龄:18性别:男联系方式:18791510258

02.计算你每次在控制台输入的次数,求这些数字的和以及平均数:

 public static void main(String[] args) {
        // 数字总和
        double sum = 0;   // 默认会带小数点
        // 计算一共输入了多少个次数
        int i = 0;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请您输入数据:");
        while (scanner.hasNextFloat()){     
          // 判断还有没有double数据 如果有的话会继续执行下去,要输没有就会跳出循环
            float x = scanner.nextDouble();  // 如果有数据就那相对类型的next来接受并且赋值
            i++; // i = i + 1;
            sum = sum + x; // 总和等于总和加你刚才输入的数据
            System.out.println("你输入了第:"+i+"次,当前的和为:"+sum);
        }
        System.out.println(i+"个数的和为:"+sum);
        System.out.println(i+"个数的平均值为:"+(sum / i));
        scanner.close();
    }
输出:
	请您输入数据:
	10
	你输入了第:1次,当前的和为:10.0
	20
	你输入了第:2次,当前的和为:30.0
	30
	你输入了第:3次,当前的和为:60.0
	40
	你输入了第:4次,当前的和为:100.0
	m
	4个数的和为:100.0
	4个数的平均值为:25.0

2. 顺序结构

​ Java的基本结构就是顺序结构,除非特别知名,否则就会按照顺序一句一句执行。A----->>B----->>C----->>…

​ 语句与语句之间,框与框之间是按从上到下的顺序执行的,它是由若干个依次执行的处理步骤组成的,它是任何一个算法都离不开的一种基本算法结构.

   public static void main(String[] args) {
        System.out.println("hello1");
        System.out.println("hello2");
        System.out.println("hello3");
        System.out.println("hello4");
        System.out.println("hello5");
    }
输出:
	hello1
	hello2
	hello3
	hello4
	hello5

​ 这个也就是比较简单的顺序结构,根据输出结果来看,这几行代码执行时都是按照从上往下的顺序依次执行。

3. 选择结构

  • if 单选择结构
  • if 双选择结构
  • if 多选择结构
  • 嵌套的if结构
  • switch多选择结构
01. if 单选则结构:

我们很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示.语法:

if(布尔表达式){    // 布尔表达式如果等于true则进入if结构,否则就不进
  // 如果布尔表达式为true将执行的语句
}

测试:equals:比较两个字符串是否相等

   public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入内容:");
        String s = scanner.nextLine();
        if (s.equals("保持自律才是最好的天赋!")){
            System.out.println("你输入的内容是:"+s);
        }else {
            System.out.println("加油!");
        }
        scanner.close();
    }
输出:
	请输入内容:
	保持自律才是最好的天赋!
	你输入的内容是:保持自律才是最好的天赋!
02. if 双选择结构:

那现在有个需求,公司要收购一个软件,成功了,给人支付100万元,失败了,自己找人开发。这样的需求用一个if就搞不定了,我们需要两个判断,需要一个选择结构,所以就有了if-else的结构。语法:

if(布尔表达式){
 		// 如果布尔表达式的值为true
}else{
  		// 如果布尔表达式的值为false
}
    public static void main(String[] args) {
        // 考试分数大于60分的及格,小于60分的不及格
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入考试成绩");
         int i = scanner.nextInt();
         if(i<60){
             System.out.println("您的考试成绩不及格,加油下次一定及格!");
         }else{
             System.out.println("恭喜!您的考试成绩及格,请保持!");
         }
        scanner.close();
    }

上述的代码中是可以把小于60分的输出为不及格,但是不符合实际情况,真实情况可能存在ABCD,存在区间多级判断;比如:90-100就是A,80-90就是B等等……,在生活中我们很多时候的选择也不仅仅只有两个,所以我们要需要一个多选择结构来处理这类问题!语法:

if(布尔表达式1){
  	// 如果布尔表达式 1 的值为true则执行的代码段
}else if(布尔表达式2){
  	// 如果布尔表达式 2 的值为true则执行的代码段
}else if(布尔表达式3){
	// 如果布尔表达式 3 的值为true则执行的代码段
}else if(布尔表达式4){
 	// 如果布尔表达式 4 的值为true则执行的代码段
}else if(布尔表达式5){
  	// 如果布尔表达式 5 的值为true则执行的代码段
}

代码案例:

 public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // if(){}else if(){}... 实现
        System.out.println("请输入你的语文成绩:");
        int i = scanner.nextInt();
        if(i==100){
            System.out.println("很优秀");
        }else if(i>=90 && i<100){
            System.out.println("中,还得加油啊");
        }else if(i>=80 && i<90){
            System.out.println("优秀");
        }else if(i>=70 && i<80){
            System.out.println("中,还得加油啊");
        }else if(i<60 && i>=0){
            System.out.println("不及格");
        }else{   // 注意:流程控制中只能由一个else,并且它要在最后执行
            System.out.println("你输入的语文成绩不合法!");
        }
        scanner.close();
    }
输出:
	请输入你的语文成绩: 	请输入你的语文成绩:	请输入你的语文成绩:	请输入你的语文成绩:
	100					95				  82				 76
	很优秀				  A					B				   C							
    请输入你的语文成绩:	请输入你的语文成绩:			请输入你的语文成绩:		
	59				   -1						  112
	不及格 			 你输入的语文成绩不合法!		你输入的语文成绩不合法!

解析:根据你所设置的数字进行范围型判断!

注意:1. if 语句只能由1个 else 语句,else 语句在所有的 else if 之后。2. if 语句可以有若干个 else if 语句,它们必须在 else 之前,一旦其中一个 else if 语句检测为 true ,其他的 else if 以及 else if 语句都将跳过执行。

03. 嵌套的if…结构

扩展知识:在代码中优化大量的if…else使用 return 或者 策略模式。

解释:嵌套的 if…else 语句是合法的,也就是说你可以在**另一个 if 或者 else if 语句中使用 if 或者 else if 语句;你可以像 if 语句一样嵌套 else if … else。**语法:

if(布尔表达式1){
  	// 如果布尔表达式 1 的值为 true 执行代码
  if(布尔表达式2){
    // 如果表达式 2 是真的话执行代码
  }
}

上练习:一个用嵌套if做的数字比较系统

 public static void main(String[] args) {
        System.out.println("==欢迎来到数字比较系统,请您按照提示进行输入!==");
        Scanner scanner = new Scanner(System.in);//实例化一个 Scanner 对象 提供用户在控制台进行输入
        System.out.println("请你输入第一个数字:");    // 输出第一个请求
        int i1 = scanner.nextInt();
        // 用户输入的格式如果是数字就是nextInt汉字的话是next
        // 并且接受用户输入的数字然后用相对应的类型进行接收
        System.out.println("请你输入第二个数字:");   // 输出第二个请求
        int i2 = scanner.nextInt();               // 第二次进行接收
        if (i1!=i2){          
          // 1. 通过 嵌套 if 来判断用户输入的两次数字中是否相等  如果不相等则执行以下代码块
            System.out.println("a和b不相等;a的值为:"+i1+"\t b的值为:"+i2);
            System.out.println("请你输入第三个数字:");   // 输出第三次请求
            int i3 = scanner.nextInt();         // 进行接收
            System.out.println("请你输入第四个数字:");  // 输出第四次请求
            int i4 = scanner.nextInt();      // 进行接收
            if (i3<i4){     // 判断用户输入的第三次和第四次来进行比较
                // 如果第三次的数字小于第四次输入的数字,那么就会执行下面代码
                System.out.println("a<b所以a的值为:"+i3+"所以b的值为:"+i4+"所以就相当于:"+i3+"<"+i4);//输出结果
            }else if (i3>i4){
                System.out.println("a>b a的值为:"+i3+"所以b的值为:"+i4+"所以就相当于:"+i3+">"+i4);
            }
        }else if(i1==i2){  // 2. 如果相等执行以下代码块
            System.out.println("你输入的两个值相等,a的值为:\n "+i1+"\t b的值为:"+i2+"\n恭喜您输入获得数字奖品全新iphone13一台");
        }else{
            System.out.println("你输入的数字不合法,请您退出!");
        }
        scanner.close();
    }
输出:
	==============欢迎来到数字比较系统,请您按照提示进行输入!==============
	请你输入第一个数字:
	889
    请你输入第二个数字:
    989
    a和b不相等;a的值为:889	 b的值为:989
    请你输入第三个数字:
    888
    请你输入第四个数字:
    999
    a<b a的值为:888所以b的值为:999所以就相当于:888<999
  ==============欢迎来到数字比较系统,请您按照提示进行输入!==============
	请你输入第一个数字:
     999
     请你输入第二个数字:
     999
     你输入的两个值相等,a的值为:
     999	 b的值为:999
     恭喜您输入获得数字奖品全新iphone13一台

解析:就是很简单的一个数字比较,用户最多可以输入6次数字

04. Switch 多选择结构
  • 多选择结构还有一个实现方式就是switch case 语句。
  • switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
  • switch 语句中的变量类型可以是:从 Java SE7 开始,switch 支持字符串String类型了同时case标签必须为字符串常量或者字面量。语法以及解析:
switch(expression){
  case value:  // 固定格式 如:case 1 :
    // 语句
    break; // 可选    // 跳出当前执行
  case value:
    // 语句
    break; // 可选
    // 你可以由任意数量的case语句
    default : // 可选
    // 语句
}

案例:

    public static void main(String[] args) {
        // case 穿透!
        char A = 'A';    
      // 如: A = n 底下相对应的case就会输出,如果没有break则继续输出n以下的内容,否则就不输出!
        switch (A){
            case 'A':
                System.out.println("日复一日的学习打卡!");
//                break;
            case 'B' :
                System.out.println("冰冻三尺非一日之寒!");
                break;
            case 'C' :
                System.out.println("每天进步一点点!");
                break;
        }
    }
输出:
	日复一日的学习打卡!
	冰冻三尺非一日之寒!

解析:很明显可以看见代码中case ‘A’: 中的break被注销了,所以没有break的话程序就会继续往下执行,形成case穿透!break的作用就是让程序跳出,不在执行下面语句

扩展知识:~ 反编译: Java能运行都是靠着程序中自动生成的class文件,然后也有class也有底层的编译。

1.第一步:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8LLZOC4z-1651160221096)(G:\日复一日的学习\学习图片\Fby\01.jpg)]2.第二步:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xyCtYrwd-1651160221096)(G:\日复一日的学习\学习图片\Fby\02.jpg)]3.第三步:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DLgoSNbw-1651160221097)(G:\日复一日的学习\学习图片\Fby\03.jpg)]4.第四步:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ub04IkB2-1651160221098)(G:\日复一日的学习\学习图片\Fby\04.jpg)]

第五步:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJl61vHZ-1651160221098)(G:\日复一日的学习\学习图片\Fby\06.jpg)]

4. 循环结构

循环:简而言之就是让程序循环的执行,可以根据条件让程序执行多少次!分别有三种分别是:

  • while循环
  • do…while 循环
  • for 循环

在Java5中引入了一种主要用于数组的增强型for循环。

01. while循环
  • while是最基本的循环,它的结构为:

    • while(布尔表达式){
        // 循环内容....
      }
      
  • 只要布尔表达式为true,循环会一直执行下去。**我们大多数情况是让循环停止下来的,我们需要一个让表达式失效的方式来结束循环。**少部分情况需要循环一直执行,比如服务器响应监听等…当循环条件一直为true会造成无限循环【死循环】,我们正常的业务编程中应尽量避免死循环,会影响程序行性能或者造成程序卡死崩溃情况!思考:计算1+1+2+3+4+5+6+…+100=? 练习如下:

  public static void main(String[] args) {
        // 使用 while 来计算 1+2+3+...+100=?
        int i = 0;
        int sum = 0;   // 和
        while(i<=100){   // 先判断条件,最后执行
            sum = sum + i;   // 和等于和加++后的内容 
            i++;  
        }
        System.out.println(sum);   // 输出和
    }
输出:
	5050
02. do…while循环
  • 对于while循环语句而言,如果不满足条件,则不能进入循环,但有时我们需要即使不满足条件,也至少执行一次。
  • do…while 循环和while循环相似,不同的是,do…while 循环至少会执行一次。语法:
do {
  // 代码语句,先执行
}while(布尔表达式);   // 后判断

while和do…while的区别:1. while先判断后执行,do…while是先执行后判断!2. do…while总是保证循环体被至少执行一次,只是它们的主要差别。 如:

  public static void main(String[] args) {
        int i = 0;
        int sum = 0;
        do {
            sum = sum+i;    //  先执行一次
            i++;
        }while (i<=100);   // 最后才执行条件
        System.out.println(sum);
    }
输出:
	5050
	
     // while和do...while区别
    public static void main(String[] args) {
        int i = 0;
        while(i<0){   // while 循环是先判断布尔表达式在进行执行
            System.out.println(i);
        }
        System.out.println("*******************");

        do {
            System.out.println(i);   // do...while 是先执行一次在进行判断
        }while (i<0);
    }
输出:
	*******************
	0     // 这个0就是do...while所输出的
03. for循环
  • 虽然所有循环结构都可以用while或者do…while 表示,但 Java 提供了另一种语句——for循环,是一些循环结构变得更加简单。
  • for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。
  • for循环执行的次数实在执行前就确定的。语法如下:
for(初始化;布尔表达式;更新){
  // 代码语句
}

练习:

  1. 计算0到100之间的奇数和偶数的和;
  2. 用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个;
  3. 打印九九乘法表。
 1. public static void main(String[] args) {

        int a = 0;    // 奇数
        int b = 0;    // 偶数
        for (int i=0;i<=100;i++){
            if(i%2!=0){   // 偶数取余2之后都会等于0 因为偶数的话最低位是0
                a+=i;
            }else{    // 相反奇数取余2之后都会是1不等于0 奇数最低位是1
                b+=i;
            }
        }
        System.out.println("奇数的和是:"+a);
        System.out.println("偶数的和是:"+b);
    }
输出:
	奇数的和是:2500
	偶数的和是:2550
2.   // 用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个;
    public static void main(String[] args) {

        for (int i = 1; i <=1000; i++) {
            if(i%5==0){
                System.out.print(i+"\t");
            }
            if(i%(3*5)==0){
                System.out.println();
            }
        }
    }
输出:
	5	10	15	
	20	25	30	
	...
  	1000
3.   // 打印九九乘法表
    public static void main(String[] args) {
        for (int i = 1; i <= 9; i++) {        
            for (int i1 = 1; i1 <=i; i1++) {
                System.out.print(i1+"*"+i+"="+i1*i+" ");
            }
            System.out.println();
        }
    }
输出:
	1*1=1 
    1*2=2 2*2=4 
    1*3=3 2*3=6 3*3=9 
    1*4=4 2*4=8 3*4=12 4*4=16 
    1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 
    1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 
    1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 
    1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 
    1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 
04. 增强for循环
  • Java5之后引入了一种用于数组或集合的增强型for循环
  • Java增强for循环格式如下:
for(声明语句 : 表达式){
  // 循环语句
}
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
  • 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
   public static void main(String[] args) {
        int[] num = {1,2,3,4,5};
        // 使用普通的for循环
        for (int i = 0; i < 5; i++) {
            System.out.println(num[i]);
        }
        System.out.println("=============");
        // 使用增强for循环
        for (int x : num){
            System.out.println(x);   // 代码简化
        }
    }
输出:
	1
    2
    3
    4
    5
    =============
    1
    2
    3
    4
    5
5. 关键字:
01. break和continue
  • break:break在任何循环语句的主题部分,均可用break控制循环的流程;break用于强行退出循环,不执行循环中剩余的语句。(break语句也switch语句中使用)
  • continue:用于循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的盘底那个
break:它是直接跳出当前循环,执行循环下面的语句。
    public static void main(String[] args) {
        int i=0;
        while (i<1000){   // 本来是1000
            i++;
            System.out.println(i);
          if (i==5){   // 当i==5的时候
              break;    // break直接中断,不在执行循环,执行循环下面的语句
          }
        }
        System.out.println("break支持跳出循环,但是程序依旧执行!");
    }
    输出:
    1
    2
    3
    4
    5
    break支持跳出循环,但是程序依旧执行!
2. continue: 只要碰到continue就会返回上一级继续循环不会先执行continue下面的语句,直到条件不成立时,才会结束循环,执行下面
   public static void main(String[] args) {
        int i=0;
        while (i<10){
            i++;
            System.out.println(i);
          if (i==5){
             continue;   
          }
        }
   	System.out.println("continue支持返回上一级循环中继续执行,直到条件不成立结束,才会结束");
    }
    输出:
    	1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        continue支持返回上一级循环中继续执行,直到条件不成立结束,不会直接结束循环!
02. goto关键字
  • goto关键字很早在程序设计语言中出现,尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子—带标签的break和continue。
  • "标签"是指后面跟一个冒号的标识符,例如:label:
  • 对Java来说唯一用到标签的地方是在循环语句之前,而在循环之前设置变迁的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
03、return关键字
  • return作用:用于结束整个方法体,当程序遇见return时就会立刻停止不会在执行return后面的语句

    package com.zhai.array;
    
    public class Test {
        // 测试 return 返回值有用的时候需要进行返回
        public static void main(String[] args) {
            int[] a = new int[2];
            a[0]=1;
            a[1]=2;
            System.out.println(BD(a));
        }
        // return 的作用:跳出整个方法体 return后面的语句统一不在执行
        public static String BD(int[] a){
            for (int i = 0; i < a.length; i++) {
               if (a[i]==1){
                   return "return返回结果:"+a[i];
               }
            }
            return "我已经跳出整个方法啦!";
        }
    }
    输出:
    	return返回结果:1
    
    package com.oop;
    
    public class Demo3 {
    
        public static void main(String[] args) {
            int a = Demo3.A(1, 2);
            System.out.println(a);
        }
        public static int A(int a,int b){
          return a+b;
        }
    }
    输出:
    	3
    

    ​ 注意:使用return的时候就要把方法定义为有返回值类型的,并且定义形参;接收的时候使用相同的数据类型或者相同的引用类型,并且传入实参进行调用。

    解析:首先,return作用是中断方法,不会让方法在继续执行;还有一个就是return可以进行返回值,根据需求哪里需要用到返回值的时候就可以进行返回。

    • 使用for循环打印出三角形

    • public static void main(String[] args) {
      
            for (int i = 1; i <= 5; i++) {
                for (int i1 = 5; i1 >= i; i1--) {    // 这个循环是从5~1这样的空格
                    System.out.print(" ");
                }
                for (int i1 = 1; i1 <=i; i1++) {     // 一半的三角形
                    System.out.print("-");
                }
                for (int i1 = 1; i1 <i; i1++) {   // 另一半的三角形 i1 <i 的原因就是上一半的第一行已经打印出来了,所以不需要等于
                    System.out.print("-");
                }
                System.out.println();
            }
        输出:
             	 -
        		---
       	   -----
      	  -------
      	 ---------
      

三、方法

1、方法的概念
  • System.out.println(),它就是我们平时使用的输出语句,println()就是Java中的一个方法.
  • Java方法是语句的集合,它们在一起执行一个功能。
    • 方法是解决一类问题的步骤,是一种有序集合。
    • 方法包含于类或对象中
    • 方法在程序中被创建,在其他地方被引用。
  • 设计方法的原则:方法的本意就是功能块,就是实现某个功能的语句块的集合;我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成一个功能,这样有益于我们后期的扩展。
   public static void main(String[] args) {
        int sum = add(1,2);		// 这里add方法里的1,2 就是所谓的实参是调用方法后传入的参数
        System.out.println(sum);  
    }
    // 定义一个运算加法的方法  // 后面的参数就是自定义形参
    public static int  add(int a,int b){    // 方法加上static关键字后前面的主方法才可以调用
        return a+b;
    }
输出:
	3
2、方法的定义以及调用
  • 01、Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含一下语法:

  • 方法包含一个方法头和一个方法体,下面是一个方法的所有部分:

    • 修饰符:这是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。

    • 返回值类型:方法可能会有返回值;returnValueType是方法返回值的数据类型,有些方法执行所需的操作,但没有返回值;在这种情况下,returnValueType的关键字是void

    • 方法名:是方法的实际名称,方法名和参数表共同构成方法签名

    • 参数类型:参数像是一个占位符,当方法被调用时,传递值给参数,这个值被称为实参或变量;参数列表是指方法的参数类型顺序和参数的个数参数是可选的,方法可以不包含任何参数。

      • 形参(形式参数):就是在方法被调用时用于接收外界输入的数据,也就是定义方法时的参数。
      • 实参:调用方法时实际传为方法的数据。
    • 方法体:方法体包含具体的语句,定义该方法的功能。语法如下:

      修饰符 返回值类型 方法名(参数类型 参数名){
        
          ...
          方法体
          ...
            return 返回值;
      }
      
      
  • 02、方法的调用

    • 调用方法:对象名.方法名(实参列表);
    • Java支持两种调用方法的方式,根据方法是否有返回值来进行选择。
    • 当方法的返回值有一个的时候,方法调用通常被当作一个值,例如:
int larger = max(20,30);
  • 如果方法返回值是void,方法调用一定是一条语句。
System.out.println("Hello,xiaozhai");

小练习:

    // 比较两个数字的大小  // 哪个数字大就输出哪个数字
    public static void main(String[] args) {
        int max = zhai(3, 123);
        System.out.println(max);
    }
    public static int zhai(int a,int b){
        int add = 0;
        if (a==b){
            System.out.println("两个数字相等");
            return 111;  // 注意:return不仅仅有返回值的作用,还有终止方法的作用。
        }
        if(a<b){
            add = b;
        }else {
            add = a;
        }
        return add;
    }
输出:
	123

解析: 就是先自定义一个方法,然后自定义两个参数(这个参数也就是所谓的形参,因为里面没有值,只是个形式),然后在自定义一个变量并且赋值用于接收a,b中的值;然后通过 if 进行判断这个两个数值的大小如果a小于b的话,则就把b的值赋给刚才自定义的变量名中,如果a大于b的话就把a的值赋给变量,这是就是实参因为里面已经传值了;还有就是如果a等于b的话我们让它输出两个数字相等并且返回111;

3、方法的重载
  • 01、方法就是在一个类中,有相同的函数名称,但形参不同的函数

  • 方法重载的规则:

    • 方法名称必须相同;
    • 参数列表必须不同;(个数不同、类型不同、参数排列顺序不同等…);
    • 方法的返回类型可以相同也可以不同;
    • 仅仅返回类型不同不足以称为方法的重载。
  • 实现理论:

    • 方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配以选择对应的方法,如果匹配失败,则编译器报错。

练习:

    public static void main(String[] args) {
        add(1,2);
        add(5,2,1);
    }
    // 计算加法
    public static void add(int a,int b){
       System.out.println(a+b);
    }
    // 计算减法
    public static void add(int c,int d,int e){
        System.out.println(c-d-e);
    }
输出:
	3
	2

解析:练习后发现一个类中有两个名称一样的方法,此时就已经实现了方法的重载,两个方法的名称都叫add但是计算加法中的参数有两个分别是:a,b;但是计算减法中的参数有三个分别是c,d,e;经过上面的main调用进行传入实际参数则会输出结果。

  • 命令行传参
  • 有时候希望运行一个程序时在传递给它消息,这要靠传递命令行参数给main()函数实现。
   public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args:["+i+"]"+args[i]);
        }
    }
  • 命令行使用:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-haijlX8M-1651160221099)(G:\日复一日的学习\学习图片\dosCC\1.png)]

  • 可变参数(如:int …i)

  • JDK1.5开始,Java支持床底同类型的可变参数给一个方法;

  • 在方法声明中,在指定参数类型后加一个省略号(…);也称不定向参数。

  • 一个方法中只能指定一个可变的参数,而且它必须是方法的最后一个参数;任何普通的参数必须在它之前声明。

  • package com.zhai.method;
    
    public class Demo5 {
        /**
         * 可变的参数
         * @param args
         */
        public static void main(String[] args) {
            Demo5 demo5 = new Demo5();    // 实例化对象,通过new一个当前类进行调用该类的方法
            demo5.method(1,2,3,4,5);   // 利用对象名进行调用该类的方法,并且进行传递参数(实参)
        }
        public void method(int ...i){   // 定义的方法以及形参
            System.out.println(i[0]);   //  获取参数从0开始
            System.out.println(i[1]);
            System.out.println(i[2]);
            System.out.println(i[3]);
            System.out.println(i[4]);
        }
    }
    输出:
    	1
    	2
    	3
    	4
    	5
    

    解析:可以看见我定义方法的时候写的形参是int …i,这个就是写可变的参数的语法;这样定义的方法可以传入多个参数。

    比较大小:

    package com.zhai.method;
    
    public class Demo6 {
        public static void main(String[] args) {
           method(1,2,3,4,5.0);   // 可以使用static进行调用method方法
        }
    	// 先定义一个 method 方法,并且写入可变参数类型为 double
        public static void method(double ...doubles){  
            if (doubles.length == 0){       // 如果参数长度等于0的话
                System.out.println("没有传递参数...");  // 就会输出没有传递参数
                return;     // 然后返回
            }
            double result = doubles[0];   
          // 定义个容器,用于接收上面的多个参数所以这里就会用到数组
            // 排序       // 因为它不是一个数字,所以想要输出必须通过循环
            for (int i = 0; i < doubles.length; i++) { 
              // 就是先定义一个变量,然后让这个变量小于参数的长度,然后 ++自增让他继续循环
                if (doubles[i]>result){   // 如果参数的第i个数据大于第1个参数
                    result = doubles[i];   // 那么就把这个值赋给result
                }
            }
            System.out.println("这是最大值:"+result);  // 然后进行输出
        }
    }
    输出:
    	这是最大值:5.0
    

四、递归

  • A方法调B方法,我们很容易理解!

  • 递归就是:A方法调用A方法,就是自己调用自己。

  • 利用递归可以用简单的程序来解决一些复杂的问题,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来进行求解,递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量;递归的能力在于用有效的语句来定义对象的无线集合。

  • 递归的结构包括两个部分:

    • 递归头:什么时候不调用自身的方法,如果没有头,将陷入死循环。
    • 递归体:什么时候需要调用自身的方法。
  • package com.zhai.method;
    
    public class DG1 {
        public static void main(String[] args) {
            System.out.println(Dg(6));  // 输出:递归方法并且传入参数进行递归
        }
    
        // 递归:如:5*4*3*2*1
        public static int Dg(int x){    // 自定义一个方法用于递归Dg
            if (x==1){     // 如果 参数要是等于1的话就直接返回1,因为1的递归后还是1就相当于1*1
                return 1;
            }else{
                return x*Dg(x-1); // 否则就返回参数*方法Dg参数减1、
                // 就是它每次递归时,要乘比它本身小于1的数字 所以这里就是x-1
            }
        }
    }
    输出:
    	720
    
  • 使用基础实现计算器(小练习)

  • package com.zhai;
    import java.util.Scanner;
    public class CalcDemo {
        public static void main(String[] args) {
            System.out.println("====================计算器====================");
            Scanner scanner = new Scanner(System.in);
            System.out.println("请您输入第一个数字");
            int v1 = scanner.nextInt();
            System.out.println("请您输入一个运算符...");
            String next = scanner.next();
            System.out.println("请您输入第二个数字");
            int v2 = scanner.nextInt();
            switch(next){
                case "+":
                    System.out.println("您输入的结果是:"+v1 + next + v2 + "=" +(v1+v2));
                    break;
                case "-":
                    System.out.println("您输入的结果是:"+v1 + next + v2 + "=" +(v1-v2));
                    break;
                case "✖":
                    System.out.println("您输入的结果是:"+v1 + next + v2 + "=" +(v1*v2));
                    break;
                case "/":
                    System.out.println("您输入的结果是:"+v1 + next + v2 + "=" +(v1/v2));
                    break;
            }
        }
    }
    输出:
          ====================计算器====================
          请您输入第一个数字
          23
          请您输入一个运算符...
          +
          请您输入第二个数字
          17
          您输入的结果是:23+17=40
    

解析:使用Scanner对象可以让用户在控制台进行输入,然后在利用switch进行根据用户输入的运算符来选择,然后在进行计算将结果返回!

五、数组

1、数组的定义
  • 数组是相同类型数据的有序结合。
  • 数组存储的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
  • 其中,每一个数据称做一个数组元素,每个数组元素可以通过一个下标来访问它们。
2、数组的声明与创建
  • 首先必须声明数组变量,才能在程序中使用数组。声明数组变量的语法:

    int[] i;   // 首选的方法  Java中使用这种
    
    int i[];  // 效果相同,但不是首选方法
    
    
  • Java中语言使用new关键字来创建数组,语法如下:

    int[] i = new int[arraySize];   // 就是说定义什么类型的数组,就要new什么类型的数组并且给予值
    
    int[] i = new int[5];  // 意思就是这个int中可以存储5个数字
    
  • 数组的元素是通过索引访问的,数组索引从0开始

    package com.zhai.array;
    
    public class Array {
        public static void main(String[] args) {
    //        int[] i;   // 第一种方法:定义一个数组
    //        i = new int[3];  // 创建一个数组
            int [] i = new int[2];   // 第二种方法:一次性创建数组
            i[0]=3;
            i[1]=12;
            System.out.println("我是数组中第一个数据:"+i[0]);
            System.out.println("我是数组中第二个数据:"+i[1]);
        }
    }
    输出:
    	我是数组中第一个数据:3
    	我是数组中第二个数据:12
    
  • 获取数组长度时候array.length

package com.zhai.array;
public class ArrayDemo2 {
    public static void main(String[] args) {
        int[] i = new int[10];
        i[0]=1;
        i[1]=2;
        i[2]=3;
        i[3]=4;
        i[4]=5;
        i[5]=6;
        i[6]=7;
        i[7]=8;
        i[8]=9;
        i[9]=10;
        int sum = 0;
        for (int j = 0; j <i.length ; j++) {
           sum = sum + i[j];   // i:就表示数组的长度,j:就是循环的次数最后把和赋给sum
        }
        System.out.println(sum);  // 进行输出
    }
}
输出:
	55
3、数组中的内存分析
01、堆内存
  • 就是存放new对象和数组
  • 可以被所有线程共享,不会存放别的对象引用。
如:int[] i = new int[2];

解析:=左边的就是栈内存,在计算机中有块内存名为 i 没有数据的时候并无意义;相反当=右边有关键字new出现的时候,在计算机中会开辟一块内存里面并且可以存储2个数据都是相同类型的数据,这些数据的编号(也就是所谓的下标)是从0开始的,就是取出数据的时候会用到也就是堆内存(存储行同类型的数据)。

02、栈内存
  • 存放基本变量类型(包含这个进本类型的具体数值)
  • 引用对象的变量(会存放这个引用在堆里面的具体地址)
03、方法区
  • 可以被所有线程共享
  • 包含了所有class和static变量
4、数组的三种初始化
  • 静态初始化
int[] a = {1,2,3}    // 这个创建数组的含义就是直接赋值为1,2,3 不灵活
Man[] mas = {new Man(1,1),new Man(2,2);   // 引用类型,把自定义类通过new进行初始化
  • 动态初始化
int[] i = new int[2];  // 这个数组就是在堆内存中开辟一块大小为2的内存,只能存储两个数据类型统一是int
i[0]=3;     // 给开辟好的内存进行赋值  0就是第一个数据为3
i[1]=12;    // 第二个数据为12
  • 数组的默认初始化
    • 数组是引用类型,它的元素相当于类的实例变量,一次数组经分配空间,其中的每个元素也被按照实力变量同样的方式被隐式初始化。
package com.zhai.array;

public class ArrayDemo3 {
    public static void main(String[] args) {
        // 静态数组
        int[] i = {2,3,4,5,6,7,8,9};
        System.out.println("我是静态数组:"+i[5]);
        // 动态数组
        int[] i1 = new int[8];
        i1[0]=312;    // 赋值为第一个数据为312
        System.out.println("我是动态数组:"+i1[0]);
        // 默认初始化值
        System.out.println("我是动态数组的默认初始化值:"+i1[1]);
    }
}
输出:
	我是静态数组:7
	我是动态数组:312
	我是动态数组的默认初始化值:0
01、数组的四个基本特点
  • 长度是确定的,数组一旦被创建,它的大小就是不可改变的;
  • 其元素必须是相同类型,不能出现混合类型;
  • 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
  • 数组变量是属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量;数组本身就是对象,Java中对象是存储在堆内存中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
02、数组边界
  • 下标的合法区间:[0,length-1],如果越界就会报错;
package com.zhai.array;

public class ArrayDemo3 {
    public static void main(String[] args) {
		// 数组越界异常
        int[] i2 = new int[2];
        i2[1]=2;
        System.out.println("我是正常的边界:"+i2[1]);   // 这个是可以正常输出的
        System.out.println(i2[2]);   
  // 因为下标是从0开始所以这个已经越界了,因为在初始化数组时已经给过容量大小,所以不能超过最大数值
    }
}
输出:
	我是正常的边界:2
	java.lang.ArrayIndexOutOfBoundsException    // 数组越界异常
  • 小结:
    • 数组是相同的数据类型(数据类型可以为任意类型)的有序集合
    • 数组也是对象,数组元素与对象的成员变量
    • 数组长度是确定的,不可变的;如果越界,则报:java.lang.ArrayIndexOutOfBoundsException
03、数组使用
  • For-Each 循环
  • 数组作为方法传入参数
  • 数组作为返回值

小练习:

package com.zhai.array;
public class ArrayDemo4 {
    public static void main(String[] args) {
        // 输出所有的数组数据
        int[] a = {12,23,34,56,67,78,89};
        for (int i = 0; i <a.length ; i++) {
            System.out.println(a[i]);
        }
        System.out.println("======================================");
        // 输出所有数据的和
        int b = 0;
        for (int i = 0; i <a.length ; i++) {
            b+=a[i];
        }
        System.out.println("总和为:"+b);
        System.out.println("======================================");
        // 抽取这些数字中最大的数字 
        int c = a[0];   // 这个就是把那些数字的第一个数字赋值给c
        for (int i = 0; i <a.length ; i++) {
            if (c<a[i]){      // 然后拿c跟后面的数字进行比较,谁比第一个数字大
                c = a[i];    // 就把谁赋值给c
            }
        }
        System.out.println("最大数为:"+c);  // 然后进行输出
    }
}
输出:
    12
    23
    34
    56
    67
    78
    89
    ======================================
    总和为:359
    ======================================
    最大数为:89

数组的使用:

package com.zhai.array;

public class ArrayDemo5 {
    public static void main(String[] args) {
        // 定义静态数组
        int[] a = {1,2,3,4,5};

        // 调用普通for循环的输出方法
        printArrays(a);
        // 调用forEach循环的输出方法
        Each(a);
        // 比较大小
        int b = a[0];
        for (int i = 0; i <a.length ; i++) {
            if (b<a[i]){
                b = a[i];
            }
        }
        System.out.println("最大的数为:"+b);
        // 调用反转的方法
        int[] res = fanZhuan(a);
        printArrays(res);
    }

    // 普通的输出数组中的所有数据
    public static void printArrays(int[] b){
        for (int i = 0; i < b.length; i++) {
            System.out.println(b[i]);
        }
    }
    // 使用foreach进行输出所有的数据
    public static void Each(int[] c){
        for (int x:c) {   // forEach 有个弊端就是没有下标
            System.out.println("使用ForEach进行输出的结果:"+x);
        }
    }
    // 反转数组
    public static int[] fanZhuan(int[] arrays){
        int[] ints = new int[arrays.length];
        // 反转操作
        for (int i = 0, j=ints.length-1; i <arrays.length; i++,j--) {
             ints[j] = arrays[i];
        }
        return ints;
    }
}

以上就是Java中数组使用的方式!

5、多维数组
  • 多维数组可以看成是数组的的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
  • 二维数组
int a[][] = new int[2][5];
  • 解析:以上二维数组a可以看成一个两行五列的数组。

二维数组使用1:

package com.zhai.array;

public class Arrays1 {
    //二维数组
    public static void main(String[] args) {
 // 定义一个二维数组{}为一组,
        int[][] a = {{1,2,3},{4,5,6},{7,8,9}};
      	//              0 ,   1 ,     2
        System.out.println(a[0][0]);
        System.out.println(a[1][0]);
        System.out.println(a[1][1]);
        System.out.println(a[2][0]);

        //输出所有数组
        for (int i = 0; i < a.length ; i++) {
            for (int j = 0; j <a[i].length ; j++) {
                System.out.print(a[i][j]+" ");
            }
        }
    }
}
输出:
	1
    4
    5
    7
    1 2 3 4 5 6 7 8 9 

二维数组使用2:

package com.zhai.array;

public class Arrays2 {
    public static void main(String[] args) {
        int[][] arrays = new int[2][2];   
      // 在这个数组中一共有2个一维数组,并且每一个一维数组的元素有2个
        /**
         * int[][]  第一个[]是一共有几个一位数组 ;  第二个[]是数组中元素的个数,比如你写个5,就说明这个数组的元素最多能写5个
         * 就相当于这样:
         *      1,2,3,4,5
         *      6,7,8,9,10
         */
        arrays[0][0] = 1;    // 第一个一维数组的第一个数为1
        arrays[0][2] = 2;
        arrays[1][0] = 6;     // 第二个一维数组的第一个数为6
        arrays[1][1] = 7;
//        arrays[2][1]=1;    // 数组越界异常
        System.out.println("第一个数组中的第二个数为:"+arrays[0][2]);
        for (int i = 0; i < arrays.length ; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                System.err.print(arrays[i][j]+" ");
            }
        }

    }
}
输出:
	第一个数组中的第二个数为:2
	1 2 6 7 

注意:其实在二维数组中第一个[]是一共有几个一维数组,第二个[]是这个一维数组中有几个元素;二维数组也就相当于一维数组的两层嵌套,切记:取值一定是从下标0开始,否则就会报出数组越界异常!

6、数组中的Arrays类
  • 数组中的工具类 java.util.Arrays。
  • 由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
  • 查看JDK帮助文档。
  • Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而”不用“对象来调用(注意:是”不用“不是”不能“)。
  • 具有以下常用功能:
    • 给数组赋值:通过 fill 方法。
    • 对数组进行排序:通过 sort 方法,按升序。
    • 比较数组:通过 equals 方法比较数组中元素值是否相等。
    • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法进行操作。

Arrayys类的使用:

package com.zhai.array;
import java.util.Arrays;
public class Arrays3 {
    // 使用 Arrays 类
    public static void main(String[] args) {
        int[] a = {2,6,7,9,4,3,1,5};
        // Arrays类中都是静态方法所以直接使用类名.方法名
        Arrays.sort(a);    // sort是排序方法,排序列表是升序
        Arrays.fill(a,2,4,0);   // 填充方法,可以把数组中的数据进行覆盖; 也可以进行选择型的覆盖
        System.out.println(Arrays.toString(a));   // toString()方法可以进行打印输出
        printA(a);
    }
// 不使用工具类的情况下我们需要自己动手编写方法
    public static void printA(int[] a){
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] +" ");
        }
    }
}

输出:
	[1, 2, 0, 0, 5, 6, 7, 9]
	1 2 0 0 5 6 7 9 

解析:这里使用到了Arrays中的两个方法,第一个是:toString()方法,可以进行打印输出;第二个是:sort()方法,可以将数据进行升序的列表进行排序;fill方法是把数组中的数据进行覆盖,也可以尽心选择性的覆盖如:2,4,0意思就是把数组中下标为2~4之间的数据用0覆盖。

7、冒泡排序
  • 在排序中共有八大排序,且用的最多以及最出名的就是冒泡排序。
package com.zhai.array;
import java.util.Arrays;
/*
        冒泡排序
        1、比较数组中,两个相邻的元素,如果第一个数比第二个数大,就要交换它们的位置
        2、每一次比较,都会产生出一个最大,或者最小的数字;
        3、下一轮测可以少一次排序
        4、依次执行,直到结束
     */
public class Arrays4 {
    public static void main(String[] args) {
        int[] i = {5, 1, 3, 4, 6, 7, 8, 94, 2, 234};
        int[] sort = sort(i);
        System.out.println(Arrays.toString(i));
    }

    public static int[] sort(int[] a) {
        int c = 0;
        boolean b = false;
        for (int i = 0; i < a.length - 1; i++) {   // 让外层循环长度减1是为了不溢出
            for (int j = 0; j < a.length - 1 - i; j++) {   
          // 减i的原因是每一次比较出来的总会有一个大,这个大的就不在进行这次循环下次循环将会继续
                if (a[j+1] < a[j]) {   // 也就是如果a数组中的后一个数字小于当前这个数字
                    c = a[j];     // 那么就将当前这个数字赋值给c
                    a[j] = a[j+1];  // 将后一个数字赋值给当前这个数字
                    a[j+1] = c;   // 然后将当前的数字(c)的值赋值给后一个数字
                    b = true;   // 优化排序
                }
            }
            if (b == false){  // 当已经排好的数字就不再进入循环继续执行
                break;
            }
        }
        return a;
    }
}
输出:
	[1, 2, 3, 4, 5, 6, 7, 8, 94, 234]

解析:其实就相当于两个杯子现在都装满水,但是现在想把它们两个进行交换,就需要一个空的杯子,就如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IW2SHchI-1651160221100)(G:\日复一日的学习\学习图片\sort\sort.png)]

注意:冒泡排序的时间复杂度为:o(n2);

8、稀疏数组(拓展)
  • 稀疏数组它是一种数据结构(思想)。
  • 当一个数组中大部分元素为0或者为同一值的数据时,可以使用稀疏数组来保存该数组。
  • 稀疏数组的处理方式:
    • 记录数组一共有几行几列,有多少个不同值;
    • 把具有不同值的元素和行列几值记录在一个小规模的数组中,从而缩小程序的规模;
  • 记录有效的坐标值。

稀疏数组的使用:

package com.zhai.array;
/*
    稀疏数组
 */
public class Arrays5 {
    public static void main(String[] args) {
        int[][] arrays = new int[11][11];
        arrays[1][2]=1;
        arrays[2][3]=2;
        for (int[] array : arrays) {
            for (int a : array) {
                System.out.print(a+ "\t");
            }
            System.out.println();
        }
        // 转换为稀疏数组保存
        // 获取有效值的个数
        int sum = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11 ; j++) {
                if (arrays[i][j]!=0){
                    sum++;
                }
            }
        }
        System.out.println("有效值的个数:"+sum);
        // 创建一个稀疏数组
        int[][] arrays1 = new int[sum+1][3];
        arrays1[0][0] = 11;
        arrays1[0][1] = 11;
        arrays1[0][2] = sum;
        // 遍历二维数组,将非零的值,存放在稀疏数组中
        int count = 0;
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                if (arrays[i][j]!=0){
                    count++;
                    arrays1[count][0] = i;  //
                    arrays1[count][1] = j;
                    arrays1[count][2] = arrays[i][j];
                }
            }
        }
        // 输出
        System.out.println("输出稀疏数组:");
        for (int[] ints : arrays1) {
            System.out.println(ints[0] + "\t"
                    + ints[1] + "\t"
                    + ints[2] + "\t");

        }
        // 还原原始数组
        int[][] arrays2 = new int[arrays1[0][0]][arrays1[0][1]];
        // 给其中的元素进行还原
        for (int i = 1; i < arrays1.length; i++) {
           arrays2[arrays1[i][0]][arrays1[i][1]] = arrays1[i][2];
        }
        // 输出
        for (int[] array : arrays2) {
            for (int a : array) {
                System.out.print(a+ "\t");
            }
            System.out.println();
        }
    }
}
输出:
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	1	0	0	0	0	0	0	0	0	
        0	0	0	2	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        有效值的个数:2
        输出稀疏数组:
        11	11	2	
        1	2	1	
        2	3	2	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	1	0	0	0	0	0	0	0	0	
        0	0	0	2	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	
        0	0	0	0	0	0	0	0	0	0	0	

含义:其实呢,稀疏数组的作用就是从一堆数据中把有值的取出来,变成一个小数组。

面向对象(OOP)

一、初识面向对象

  • 面向对象&面向过程
    • 步骤简单清晰,第一步做什么、第二步做什么…
    • 面向过程适合处理一些较为简单的问题
  • 面向对象思想
    • 物以类聚,分类的思维模式,思考问题首先会解决问题需要的那些分类,然后对这些分类进行单独思考;最后,才对某个分类下的细节进行面向过程的思索。
    • 面向的对象适合处理复杂的问题,适合处理需要多人协作的问题!
  • 对于描述复杂的十五,为了从宏观上把握、从整体上合理的分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
  • 面向对象编程(Object-Oriented Programming,OOP)
  • 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
  • 抽象
  • 三大特性:
    • 封装
    • 继承
    • 多态
  • 从认识论角考虑是先有对象后有类;对象:是具体的事物;类:是抽象的,是对对象的抽象.
  • 从代码运行角度考虑是先有类后有对象;类是对象的模板。

对象传参:

package com.oop;
public class Demo1 {
    public static void main(String[] args) {
        String s = Demo1.sayHello("只要持之以恒,即使再小的船也能远航!");   
      // 这边给它传入一个实际参数并且参数类型为String
        System.out.println(s);  // 输出
    }
    public static String sayHello(String a){   // 这边定义一个返回值为String的方法以及传入一个形参类型也为String
        return a;    // 返回当前参数值
    }
    public  void say(){
        System.out.println("冰冻三尺非一日之寒!");
    }
}
输出:
	只要持之以恒,即使再小的船也能远航!

类与类之间使用对象进行调用

package com.oop;
public class Demo2 {
    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();  // 实例化
        demo1.say();   // 使用对象.方法名就可以进行调用
    }
}
输出:
	冰冻三尺非一日之寒!

使用return进行返回结果集

package com.oop;
public class Demo3 {
    public static void main(String[] args) {
        int a = Demo3.A(1, 2);    // 进行传入参数(实参)
        System.out.println(a);    // 进行输出
    }
    public static int A(int a,int b){  // 定义的方法带返回结果并且定义两个形参
      return a+b;   // 返回这两个数字的操作
    }
}
输出:
	3

在不同类之间的调用以及返回

package com.oop;

public class Demo4 {
    public static void main(String[] args) {
        int a = 100;      
        System.out.println(a);
        Student.A(a);
        System.out.println(a);

        int b = Return.B(a);
        System.out.println(b);
        String name = "张三";
        String c = Return1.C(name);
        System.out.println(c);
    }
}
class Student{
    public static void A(int a){
        a = 10;
    }
}
class Return{
    public static int B(int a){
        return a = 20;
    }
}
class Return1{
    public static String C(String b){
        String name;
        return b;
    }
}
输出:
	100    // 正常输出,没问题
  	100    // 没有return的时候不能进行第二次赋值的操作
	20    // return之后可以成功赋值(值传递)
	张三   // 也可以进行引用传递,因为定义的返回值类型为String并且用String进行接收(引用传递)

解析:如果不进行return返回话,那么不管怎么操作都是无意义的,然而,如果你编写了有返回值的方法,并且定义形参以及传入实参时,这时候你的操作就可以输出,从而体现return的作用。引用传递的本质也是值传递。

注意:一旦定义有返回值的方法时就需要拿对应的类型来进行接收,就可以输出.

1、类与对象的关系

  • 类是一种抽象的数据类型,它是对某一类事物真题描述/定义,但是不能代表某一个具体的事物。
    • 动物、植物、手机、电脑…
    • Preson类、Pet、Car类等,这些类都是用来描述/定义某一类的具体的事物应该具备的特点和行为。
  • 对象是抽象概念的具体实例
    • 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例…
    • 能够体现出特点,展现功能的是具体的实例,而不是一个抽象的概念.

2、创建与初始化对象

  • 使用new关键字创建对象.
  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中的构造器进行调用。
  • 类中的构造器也称构造方法,是在进行创建对象的时候必须要调用的;并且构造器有一下两个特点:
    • 1、必须和类的名字相同;
    • 2、必须没有返回类型,也不能写void.
  • 构造器必须要掌握.

使用:

package com.oop1;
/*
       学生类
 */
public class Student {
    // 属性
    String name;
    int age;
    String sex;
    int money;
    // 方法
    public void study(){
        System.out.println(this.name+this.age+sex+money);
    }
}
package com.oop1;
public class Application {
    // 主测试方法
    public static void main(String[] args) {
        Student student = new Student();
        student.name="张三";
        student.age=19;
        student.sex="男";
        student.money=1000000000;
        System.out.println("姓名:"+student.name+" "+"年龄:"+student.age+" "+"性别:"+student.sex+" "+"存款:"+student.money+"万元");
        Student student1 = new Student();
        student1.name = "李四";
        student1.age = 20;
        student1.sex = "男";
        student1.money=1;
        System.out.println("姓名:"+student1.name+" "+"年龄:"+student1.age+" "+"性别:"+student1.sex+" "+"存款:"+student1.money+"块");
    }
}
输出:
      姓名:张三 年龄:19 性别:男 存款:1000000000万元
      姓名:李四 年龄:20 性别:男 存款:1

注意:在Java类中一个类可以new无数次并且每new一次分配的内存都是独立的,第一个new的对象赋的值不会影响第二次new的对象。其实就是new的是构造方法。

  • 构造器:创建类的时候一旦出现new关键字的时候,Java中就会默认的执行一个跟该类名称相同的一个方法并且没有任何返回值方法;该方法就是所谓的构造器(也称构造方法)。
  • 构造方法的作用:可以给该类中的属性进行实例化初始值;new本质就是在初始化无参构造。

3、构造器的使用

类1

package com.oop1;
public class Person {
    String name;
    // 默认自动执行无参构造方法;这边我们让他显示出来
    public Person(){
        // 实例化初始值
        this.name = "张三"; // 这个this指的是当前的这个类.就可以调用当前类的所有属性以及所有方法.
    }
     public Person(String s){   // 有参构造函数
        this.name = s;  // 当new的时候一旦传入参数就会调用有参构造
    }
}

类2:

package com.oop1;
import jdk.nashorn.internal.ir.CallNode;
public class Application2 {
    public static void main(String[] args) {
        // 使用new关键字实例化了一个对象
        Person person = new Person("李四"); // 传入参数的时候会调用有参构造
        System.out.println(person.name);
    }
}
输出:
	// 张三
	李四

解析:由上述代码可见,我们把构造方法写出来之后利用this关键字进行给属性初始化值,在另一个类中进行实例化对象以及调用输出。快捷键:Alt+Insert

回顾:

  • 类与对象:类是一个模板,抽象;对象是一个具体的实例。
  • 方法:定义、调用。
  • 对象的引用:
    • 基本数据类型:8种
    • 引用类型:对象是通过引用来操作的;栈----->>堆。
  • 属性:也叫字段,也称成员变量;
  • 默认初始化:
    • 数字:0,0.0;
    • char:u0000;
    • boolean:false;
    • 引用:null.
  • 修饰符 属性类型 属性名 = 属性值!
  • 对象的创建和使用:
    • 必须使用new关键字创建对象,构造器 如:Cat c = new Cat();
    • 对象的属性使用:cat.name
    • 对象的方法使用:cat.sleep();
  • 类:
    • 静态的属性 属性
    • 动态的行为 方法

二、封装、继承、多态

1、封装(private)

  • 所谓封装就是把属性私有化,如:我在Cat这个类中定义了一个属性name,注意这个name只能在Cat这个类中进行使用。
  • 通俗一点就是:该露的露,该藏的藏
    • 我们程序设计要求"高内聚,低耦合";
    • 高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉;
    • 低耦合:仅暴露少量的方法给外部使用。
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息的隐藏。
  • 记住这就话就够了:属性私有,get/set

代码实现:

package com.oop2;

/**
 * 封装的作用:
 *  1、提高程序的安全性
 *  2、隐藏代码的实现细节
 *  3、统一接口
 *  4、提高系统的维护
 */
// 启动类
public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("张三");
        System.out.println(student.getName());
        student.setAge(-1);
        System.out.println(student.getAge());
    }
}
// 学生类
package com.oop2;
public class Student {
    // 属性私有化
    private String name;
    private int id;
    private char sex;
    private int age;
// 提供一些可以操作这些属性的方法
    // 提供一些 public 的 get/set 方法
    // get:获取数据
    // set:给数据设置值
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    public int getAge() {
        return age;
    }
    // 利用set方法设置年龄的规范性
    public void setAge(int age) {
        if (age<0||age>150){
            this.age = 2;
        }else {
            this.age = age;
        }
    }
}
输出:
	张三
	2    // 这个就是操作属性后所输出的值,可以大大的提高代码的安全性,不能随便操作...

解析:**private一般会用在类中的属性前面,不能让其他类直接操作该类的属性;必须要通过该类中的get/set方法去操作该类的属性,并且还能在该类的属性中使用set方法可以给属性设置一些安全隐患;**就如上述代码,人的年龄不可能小于0岁或者超过150(极其少见),所以咱们就判断一下,如果传入的值小于0或者大于150那就给他传入2,如果不在这个范围之内就可以正常传参。

2、继承(extends)

  • 继承的本质就是对某一批类的抽象,从而实对显示世界更好的建模(父子).
  • 关键字为 extends :意思是"扩展",子类是父类的扩展。
  • Java中只有单继承,没有多继承。
  • 继承是类和类之间的一种关系;除此之外,类和类之间的关系还有依赖、组合、聚合等…
  • 继承关系的两个类,一个为父类(基类);子类继承父类,使用关键字extends来表示。
  • 子类和父类之间,从意义上讲应该具有"is a"的关系。
  • Java中所有的类统一都有一个总父类,就是Object.
package com.oop3;
/*
    继承:extend 
 */
// 父类
public class Person {
    // 一旦子类继承父类后,父类的所有属性以及属性子类都可使用
  private  int money = 1_000_000_000;   // 父类属性私有化之后就要使用get/set方法进行调用
    // 父类方法
    public void sleep(){
        System.out.println("键盘敲烂,工资过万!");
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}

package com.oop3;
// 学生类去继承父类
public class Student extends Person{
}

// 老师类也去继承父类
package com.oop3;
public class Teacher extends Person{
}
package com.oop3;

/*
 * 测试类,用来专门测试继承
 */
public class Application {
    public static void main(String[] args) {
        Person p = new Person();
        p.sleep();
        p.setMoney(123);
        System.out.println(p.getMoney());
        System.out.println("===========================================");
        Teacher t = new Teacher();
        t.sleep();
        t.setMoney(111);
        System.out.println(t.getMoney());
    }
}
输出:
      键盘敲烂,工资过万!
      123
      ===========================================
      键盘敲烂,工资过万!
      111

注意:可以看到父类中有一个变量int已经赋值了,但是在输出的时候只要使用set进行设置值时,就会把之前的值个覆盖掉。

01. 继承中的super,this使用

this 和 super 的区别

注意点:

  • super调用父类的构造方法,必须在构造方法的第一个
  • super必须只能出现在子类的方法或者构造方法中。
  • super和this不能同时调用构造方法

区别:

  • 首先,this和super代表的对象不同
    • this:本身调用本类的对象
    • super:代表父类兑现的应用
  • 前提:
    • this:没有继承可以使用,因为指表示this在该类的对象
    • super:必须出现在继承后的子类中。
  • 构造:
    • this():本类的构造
    • super():父类的构造

super的使用

package com.Test;
/*
    父类
 */
public class One {

    public One(){
        System.out.println("One父类的无参构造!");
    }
    protected String name = "张三";   // 父类属性

    public void test1(){
        System.out.println(name+":学Java使我快乐!");  // 父类方法
    }

}

package com.Test;
/*
    子类
    继承父类
 */
public class Two extends One {
  // 隐藏代码
    public Two(){
     	super();   // 直接默认调用父类的无参构造
        System.out.println("Two子类的无参构造!");
    }
    public void print(){
        System.out.println(super.name);  
      // 使用 super 拿到父类的属性 注意:要定义方法将属性输出调用
    }
    public void test2(){
        this.print();   // 调用本类的print()方法
        super.test1();      // 使用super拿到父类方法 (也是自定义方法进行调用)
    }
}

package com.Test;
/*
    测试类
 */
public class Test {
    public static void main(String[] args) {
        Two t = new Two();   // 通过new关键字进行初始化子类利用继承可以拿到父类的所有属性以及方法
        t.print();
        t.test2();
    }
}
输出:
  One父类的无参构造!
  Two子类的无参构造!
  张三
  张三
  张三:学Java使我快乐!

注意:很显然上面的测试类中new的是Two(子类)但是程序先输出的是One(父类的构造)这其中把一行代码隐藏了,就是在Two类中的构造方法里面默认执行了一个super();所以就调用了父类中的无参构造。

protected:可以被子类使用,以及同包下的类使用,不公开(只是仅限于子类和同包下的类使用)。

02、方法的重写
  • 重写:都是方法的重写,和属性无关。
  • 必须建立在extends(继承)关系上,子类重写父类的方法。
    • 方法名必须相同
    • 参数列表必须相同
  • 子类的方法必须和父类的必须要一直,方法体不同!

为什么要重写方法?

因为父类的功能子类不一定需要,或者不一定满足!

代码实现

package com.oop5;
/*
    父类
 */
public class A {
    public void test1(){
        System.out.println("A类!");
    }
}
package com.oop5;
/*
    子类,继承父类
 */
public class B extends A{
    @Override
    public void test1() {
        System.out.println("B类");
    }
}
package com.oop5;
/*
    重写测试类
 */
public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.test1();
        A a = new B();
        a.test1();  // 子类重写父类方法之后,数据类型就不会指向B
    }
}
输出:
    BB

3、多态

  • 即同一方法可以根据发送对象的不同而采取用多种不同的行为方式。
  • 一个对象的实际类型是确定的,但可以指向对象引用的类型有很多。
  • 多态存在的条件
    • 有继承关系
    • 子类重写父类的方法
    • 父类引用指向子类对象
  • 注意:多态是方法的多态,属性是没有多态性的。
  • instanceof
  • 只要子类没有重写父类,子类就调用父类的方法;
  • 如果子类重写了父类的方法,它就会调用子类的方法。

代码实现:

package com.oop6;
/*
    父类
 */
public class Person {
    public void sleep(){
        System.out.println("父类");
    }

}
package com.oop6;

public class Student extends Person{
   // 子类重写父类方法
    @Override
    public void sleep() {
        super.sleep();
    }
    public void eat(){
        System.out.println("父类中的方法:吃");
    }

}

package com.oop6;
/*
    多态测试类
 */
public class Application {
    public static void main(String[] args) {
        // Student 能调用的方法都是自己的或者继承父类的
        Student s1 = new Student();// 使用子类引用指向子类对象
        // Person 父类型,可以指向子类,但是不能调用子类的独有方法
        Person s2 = new Student();  // 对象能执行那些方法看左边 // 使用父类引用指向子类对象
        // 使用祖父类引用指向子类对象
        Object s3 = new Student();
        s1.sleep();   // 子类重写了父类的方法,执行了父类的方法
        s1.eat();
//        s2.eat();  // 父类不能调用子类中独有的方法
    }
}

输出:
       父类
       父类
       父类中的方法:吃
       父类中的方法:吃

多态注意事项:

  • 1、多态是方法的多态

  • 2、父类和子类,有联系 类型转换异常(ClassCastException)

  • 3、存在条件:继承关系,方法需要重写;父类的引用指向的是子类对象! A a = new B();

    • 4、static 方法,属于类,不属于实例
    • 5、final 常量,被它修饰的变量在程序运行时不会改变值;方法不能被重写;类不能被继承…等
    • 6、private:私有化,修饰的方法不能被重写。
  • instanceof:关键字,就是验证两个类是否是父子关系;两个类要是有关系就会返回true否则返回false

X instanceof Y:这个代码编译是否能通过

01、向上转型
  • 向上转型:把子类转换为父类,自动转换。
02、向下转型
  • 向下转型:把父类转换为子类,需要强制类型转换。

如:Person(人类) Student(学生类),把人类转换为学生类的时候自动转换。

Student中有一个方法eat();

// 父类的引用指向子类的对象 称为:向上转型
Person person = Student stdent();
// 父类调用子类中的eat方法,就需要强制转换,因为是高类型--->低类型。称为:向下转型
((Student)person).eat();

代码实现:

package com.oop7;
// 父类
public class Three {
    
}

package com.oop7;
// 子类
public class One extends Three {
    public void sleep(){
        System.out.println("冰冻三尺非一日之寒!");
    }
}

package com.oop7;
// 子类
public class Two extends Three {
    public void print(){
        System.out.println("我是子类中方法哦!");
    }
}

package com.oop7;
public class Test {
    public static void main(String[] args) {
        Object object = new Three();
        System.out.println(object instanceof Three);
        System.out.println(object instanceof Two);
        // 高          低
        // 低-->高是自动类型转换 相反是强制类型转换
        Three t = new One();
        // 强制类型转换 把需要转换的类型加个括号写在变量前面就行
        ((One)t).sleep();
        Three t1 = new Two();
        ((Two) t1).print();
    }
}

输出:
	true
	false
	冰冻三尺非一日之寒!
	我是子类中方法哦!

4、static关键字

static:静态的,它是跟类一块加载的;在同一个类中被static修饰的属性或者方发可以直接使用类名调用。

package com.oop8;

import com.oop5.Test;

public class TestStatic {
    private static String name;
    {
        this.name = "张三";  // 第二执行:使用匿名代码块进行赋初始值
        System.out.println("匿名代码块~");
    }
    // 最先执行:因为跟类一块加载,只执行一次
    static {
        System.out.println("静态代码块~");
    }
    // 最后执行:使用new创建对象时会默认执行
    public TestStatic() {
        System.out.println("构造器~");
    }

    public static void main(String[] args) {
        TestStatic testStatic = new TestStatic();
        System.out.println(testStatic.name);
        System.out.println("=============================");
        TestStatic testStatic1 = new TestStatic();
    }
}
输出:
      静态代码块~
      匿名代码块~
      构造器~
      张三
      =============================
      匿名代码块~
      构造器~
- 静态导入包
  • 静态导入
package com.oop8;
// 静态导入包
import static java.lang.Math.*;

public class Test {
    public static void main(String[] args) {
        // random 是Math类中的一个随机数方法
        System.out.println(random());
        System.out.println(PI);
    }
}
输出:
	0.6448610144307986
	3.141592653589793

在Java中可以使用静态导入包,方便使用方法。

5、抽象类

  • 修饰抽象类的关键字是(abstract)修饰符可以用来修饰方法也可以修饰类,如果修饰方法就是抽象方法,如果修饰类就是抽象类。
  • 抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类。
  • 抽象类:不能使用new关键字来创建对象,它是用来让子类继承的。
  • 抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的。
  • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
package com.oop9;
/*
    抽象类 关键字:abstract
 */
public abstract class Action {

    // 抽象方法,只有方法名字,没有方法的实现 称为约束~
    // 抽象方法必须在抽象类中 
    public abstract void add();
    
    // 抽象类中可以存在普通方法
    public void insert(){
        // 方法体
    }
}
package com.oop9;
/*
    子类继承父类(抽象类)就必须要实现父类中的所有方法 不能 new
 */
public class A extends Action{
    @Override
    public void add() {
    }
}

抽象类存在的意义是什么?

解析:比如写个购物系统它里面的功能很复杂,这时候就可以使用抽象类把公用的代码抽象出来继承后实现抽象类的方法就可以进行使用,提高代码的效率。扩展性高。

6、接口

  • 普通类:只有具体实现
  • 抽象类:具体实现和规范(抽象方法)都有!
  • 接口:只有规范!
  • 接口就是规范,定义的是一组规则,体现了显示i世界中"如果你是…则必须能…"的思想;如果你是小鸟,就必须能飞;如果你是汽车,则必须能跑…
  • 接口的本质是契约,就像我们人间的法律一样,制定好之后大家必须遵守。
  • OO的精髓,是对对象的抽象,最能体现这一点的就是接口;为什么我们讨论设计模式都只针对具备了抽象能力的语言(如:C++、Java、C#…等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

注意:声明类的关键字是 class,声明接口的关键字是 interface

接口的作用:

  • 约束
  • 定义一些方法,让不同的类实现
  • 方法定义默认修饰符:public abstract
  • 属性:接口中的属性,默认修饰 public static final
  • 接口不能new,因为接口中没有构造方法
  • 实现类中使用implemants关键字可以实现多个接口
  • 实现类中必须要重写接口中的所有方法。
package com.oop10;

// 定义一个接口使用关键字 interface 修饰
// 接口中都是写(方法名)约束并没有方法体 默认都是抽象的 如: public abstract 开头的
public interface UserService {

    int age = 10;  // 接口中的属性都是常量 默认开头是 public static final,一般都不会在接口里写属性
    void add(String name);  
 // 默认都是 public abstract 开头的所以这里就不需要写访问修饰符,都是自定义的接口参数也可以根据需求加
    void delete();
    void update();
    void select();
}
  
package com.oop10;
// 接口必须要有实现类,必须要重写接口中的方法;编写方法体
// 接口可以实现多个,继承只义继承一个
public class UserServiceImpl implements UserService,TimeService{
    @Override
    public void add(String name) {
        System.out.println("添加方法1:"+name);
    }
    @Override
    public void delete() {
    }
    @Override
    public void update() {
    }
    @Override
    public void select() {
    }
    @Override
    public void time() {
    }
}

package com.oop10;
// 接口必须要有实现类,必须要重写接口中的方法;编写方法体
// 接口可以实现多个,继承只义继承一个
public class UserServiceTowImpl implements UserService,TimeService{
    @Override
    public void add(String name) {
        System.out.println("添加方法2:"+name);
    }
    @Override
    public void delete() {
    }
    @Override
    public void update() {
    }
    @Override
    public void select() {
    }
    @Override
    public void time() {
    }
}

扩展知识:一个接口对应多个实现类,怎么定义某个实现类去实现?

1、使用new创建实现类对象。2、使用类名注入

public class UserMain {
    public static void main(String[] args) throws Exception {
        接口 对象名 = new 实现类();
        UserService userService=new UserServiceImpl();  // 这里只是用了new去创建
        UserService userService1 = new UserServiceTowImpl();
        userService.add("张三");
        userService1.add("李四");
    }
}
输出:
      添加方法1:张三
      添加方法2:李四

注意:使用new关键字创建接口实现子类,可以实现某个类的具体实现类,然后可以调用该实现类中重写的方法。

7、内部类

  • 内部类就是在一个类的内部自定义一个类,如:A类中定义一个B类,那么B类相对A类来说就称为内部类,二A类相对B类来说就是外部类了。
  • 1、成员内部类
  • 2、静态内部类
  • 3、局部内部类
  • 4、匿名内部类
package com.oop11;
/*
    内部类
 */
public class Outer {
   private int id = 111;

   public void out(){
       System.out.println("这是外部类的方法");
   }
   class A{
       public void print(){
           System.out.println("这是内部类的方法");
       }
       public void getID(){
           System.out.println(id);  // 内部类可以直接调用外部类中的私有属性以及方法
       }
       // 局部内部类 在方法中定义的类
       public void method(){
           class B{       
           }
       }
   }
}

package com.oop11;
public class Application {
    public static void main(String[] args) {
        Outer outer = new Outer();    // 先实例化外部类
        Outer.A o = outer.new A();   // 使用外部类实例化内部类
        o.print();   // 调用内部类中的方法
        o.getID();
    }
}
输出:
	这是内部类的方法
	111

8、异常机制

什么是异常?(Exception)

  • 实际工作中,遇到的情况不可能是非常完美的。如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你需要读取数据库的数据,数据可能是空的等…我们的程序或硬盘满了,等等…
  • 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们称为异常,英文是:Exception,意思是例外,这些,例外情况,或者叫异常,怎么让我们的程序做出合理的处理;而不至于程序崩溃。
  • 异常指程序运行过程中出现不期而至的各种状况,如:文件找不到、网络链接失败、非法参数等等…
  • 异常发生在程序运行期间,它影响了正常的程序执行流程。

​ 分类

要理解Java异常处理是如何工作的,需要掌握以下三种类型的异常:

  • 检查行异常:最具有代表的检查性异常使用胡错误或问题引起的异常,这是程序员无法预见的。

如:要打开一个文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略。

  • 运行时异常:运行时异常是可能被程序员避免的异常,与检查性异常相反,运行时异常,运行时异常可以在编译时被忽略。
  • 错误(Error):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。

如:当栈溢出时,一个错误就发生了,它们在编译时也检查不到的。

注意:Java把异常当作对象来处理,并定义一个基类 Java.lang.Throwable 作为所有异常的超类。

在Java Api中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。

01、错误 (Error)
  • Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
  • Java 虚拟机运行错误,当 JVM 不再有继续执行操作所需要的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)一般会选择线程种植;
  • 还有发生在虚拟机试图执行应用时,如类定义错误、链接错误。这些错误都是不可查的,因为它们在应用程序的 控制和处理能力之外,而且绝大数是程序运行时不允许出现的状况。
02、异常(Exception)
  • 在 Exception 分支中有一个重要的子类 RuntimeException (运行时异常)
    • ArrayIndexOutOfBoundsException(数组下标越界)
    • NullPointerException(空指针异常)
    • AeithmeticException(算数异常)
    • MissingResourceException(丢失资源)
    • ClassNotFoundException(找不到类)等…异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
  • 这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
  • Error 和 Exception 的区别:Error 通常是灾难性致命的错误,是程序无法控制和处理的,当出现这些异常时,Java 虚拟机(Jvm)一般会选择终止线程;Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
03、异常处理机制
  • 抛出异常
  • 捕获异常
  • 处理异常的五个关键字:
    • try、catch、finally、throw、throws

注意:捕获异常一定是从小到大的去捕获;如:Error ---->Exception---->Throwable.使用快捷键:选中代码ctrl+alt+t

package com.exception;
public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        try{  // 监控区域
            System.out.println(a/b);
        }catch (Throwable e){  // catch(想要捕获的异常类型)是捕获异常 Throwable:
            System.out.println("小翟的程序出现异常啦!别灰心每一次的异常只会让你变得更强!");
        }
        finally {   // 处理善后工作
            System.out.println("finally:不管出不出异常都会执行!");
        }
        try {
            System.out.println(a/b);
        } catch (Exception e) {
            System.exit(1);  // 结束程序
        }
    }
}
输出:
      小翟的程序出现异常啦!别灰心每一次的异常只会让你变得更强!
      finally:不管出不出异常都会执行!

package com.exception;
public class Test2 {
    public static void main(String[] args) throws Exception { //throws使用在方法上,抛出异常
        new Test2().add(1,0);
    }

    public int add(int a,int b){
        if(b==0){
            throw new ArithmeticException();    // throw用在方法中,主动抛出异常
        }
        return a/b;
    }
}
输出:
    Exception in thread "main" java.lang.ArithmeticException
        at com.exception.Test2.add(Test2.java:10)
        at com.exception.Test2.main(Test2.java:5)

try:监控要出错的代码

catch():参数:要抛出异常的类型,进行抛出异常

finally:始终的,不管上面执行的怎么样,finally中的代码必须执行。

throw:用在方法中主动抛出异常。

throws:用在方法上抛出异常

04、自定义异常
  • 使用Java内置的异常类是可以描述在编程时出现大部分异常情况;除此之外,用户还可以自定义异常。用户自定义异常类,只需要继承Exception即可。

在程序中使用自定义异常类,大体可分为一下几个步骤:

1、创建自定义异常类.

2、在方法中通过throw关键字抛出异常对象.

3、如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作.

4、再出现异常方法的调用这中捕获并处理异常。

package com.exception;
// 自定义异常类:需要继承 Exception
public class MyException extends Exception {
    // 定义变量,用于传数字
    private final int exe;
    // 使用有参构造进行传值
    public MyException(int exe) {
        this.exe = exe;
    }
    // 使用 toString方法进行打印输出
    @Override
    public String toString() {
        return "MyException{" + "您的分数为:" + exe + '}';
    }
}

package com.exception;
// 测试类
public class TestException { 
    public static void exception(int a) throws MyException {
        // 判断传入的参数是否小于60
        if (a<60){  // 如果小于60
            System.out.println("你传递的分数为:"+a);  // 则输出当前的成绩
            throw new MyException(a);  // 主动抛出异常
        }
        System.out.println("您的分数合格,请进入下一轮考试!");
    }

    public static void main(String[] args) {
        try {
            exception(59);
        } catch (MyException e) {
            System.out.println("您的分数不合格!即将取消考试资格:"+e);
         	System.exit(0);  // 结束程序
        }
    }
}
输出1:
      你传递的分数为:59
      您的分数不合格!即将取消考试资格:MyException{您的分数为:59}
输出2:
	 您的分数合格,请进入下一轮考试!
05、异常总结
  • 处理运行时异常时,采取逻辑去合理规避同时辅助 try-catch 来处理
  • 在多重 catch 块后面,可以加一个 catch (Exception) 来处理可能会被遗漏的异常
  • 对于不确定的代码,也可以加上 try-catch ,处理潜在的异常
  • 尽量去处理异常,切记只是简单的调用 prinkStackTrace()去打印输出
  • 具体如何处理异常,要根据不同的业务需求和异常类型去决定
  • 尽量添加 finally 语句块去释放占用资源
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值