KuangStudy 狂神说 JavaSE 学习笔记

JavaSE

基础

基础 01 注释

  1. 单行注释

    • // 单行注释
      
  2. 多行注释

    • /*
      	多行注释
      */
      
  3. 文档注释

    • /**
      *@Description Hello World
      *@Author 
      */
      

基础 02 标识符、关键字

  1. 关键字

    • abstract; boolean; break; byte; case; catch; char; class; continue; default; do; double; else; extends; false; final; finally; float; for; if; implements; import; instanceof; int; interface; long; native; new; null; package; private; protected; public; return; short; static; super; switch; synchronized; this; throw; throws; transient; true; try; void; volatile; while
      
    • KuangStudy 狂神说 JavaSE 学习笔记_第1张图片

  2. 标识符

    • 规则见[Java开发手册(黄山版).pdf]

基础 03 数据类型

  1. 基本数据类型

    • 数值类型

      • 整数类型

        • byte占 1 个字节,min:-128(-27)max:127(27-1)
        • short占 2 个字节,min:-32768(-215)max:32767(215-1)
        • int占 4 个字节,默认值 0,min:-2,147,483,648(-231)max:2,147,483,647(231-1)
        • long占 8 个字节,min:-9,233,372,036,854,775,808(-263)max:9,223,372,036,854,775,807(263-1)
      • 浮点类型

        • float占 4 个字节
        • double占 8 个字节
      • 字符类型

        • char占 2 个字节,默认值null
    • boollean类型:占 1 位,其值只有truefalse两个,默认值false

  2. 引用数据类型

    • 接口
    • 数组

基础 04 数据类型拓展

  1. 整数

    • 二进制 0b
    • 十进制
    • 八进制 0
    • 十六进制 0x
  2. 浮点数

    • 最好完全避免使用浮点数进行比较
    • 银行业务怎么表示? ==> BigDecimal 数学工具类
  3. 字符

    • 所有字符的本质还是数字
    • 编码 Unicode 表:(97 = a 65 = A)
    • 2 字节 65536
    • U0000 UFFFF
  4. 转义字符

    • \t 制表符
    • \n 换行符
    • \a 响铃
    • \b 退格
    • \f 换页
    • \r 回车
    • \v 垂直制表
    • \\ 反斜线字符
    • \’ 单引号(撇号)字符
    • \" 双引号字符
    • \0 空字符

基础 05 类型转换

低 -----------------------------------------------------------------------> 高
byte -> short, char -> int -> long -> float -> double

  1. 强制类型转换

    • (类型)变量名 高 --> 低
  2. 自动类型转换

    • 低 --> 高
  3. 注意点

    • 不能对布尔值进行转换

    • 不能把对象类型转换为不相干的类型

    • 在把高容量转换到低容量的时候,强制转换

    • 转换的时候可能存在内存溢出,或者精度问题

    • //JDK7新特性,数字之间可以用下划线分割
      int money = 10_0000_0000;
      int years = 20;
      int total = money*years; // -1474836480,计算的时候溢出了
      long total2 = money*years; // 还是-1474836480,因为右边还是int类型,转换之前已经存在问题了
      long total3 = money*((long)years); // 2000000000,先把一个数转换为long,则全部转换为long
      

基础 06 变量、常量、作用域

  1. 变量

    • 类变量

    • 实例变量(从属于对象)

    • 局部变量(从属于方法,必须声明和初始化值)

    • public class Variable {
          static int allClick = 0;	//类变量
          String str = "Hello world";	//实例变量
          public void method() {
              int i = 0;	//局部变量
          }
      }
      
  2. 常量(Constant)

    • 初始化(initialize)后不能再改变值

    • 常量名一般使用大写字符

    • final double PI = 3.14;
      
  3. 命名规范

    • 所有变量、方法、类名:见名知意
    • **类成员变量:**首字母小写和驼峰原则:monthSalary
    • **局部变量:**首字母小写和驼峰原则
    • **常量:**大写字母和下划线:MAX_VALUE
    • **类名:**首字母大写和驼峰原则:Man,GoodMan
    • **方法名:**首字母小写和驼峰原则:run(),runRun()

基础 07 运算符

  1. 算术运算符 +, -, *, /, %, ++, –

    • int以下精度运算(shortbyte),结果自动转化为int

    • 幂运算 2^3

    • double pow = Math.pow(2,3);
      
  2. 赋值运算符 =

  3. 关系运算符 >, <, >=, <=, ==, !=, instanceof

  4. 逻辑运算符 &&, ||, !

    • 注意短路运算

    • boollean a = true;
      boollean b = false;
      System.out.println(a && b);	//false,先执行判断a,后执行判断b
      System.out.println(b && a);	//false,先执行判断b,直接短路,不执行判断a
      
  5. 位运算符 &, |, ^, ~, >>, <<, >>>

    • &, |, ~, ^:位运算的与,或,非,异或

    • A = 0011 1100
      B = 0000 1101
      A & B	//与,0000 1100
      A | B	//或,0011 1101
      A ^ B	//异或,0011 0001
      ~ B    //非,1111 0010
      
    • 2 × 8 最快运算方法?

      • <<左移,相当于 × 2

      • >>右移,相当于 ÷ 2

      • System.out.println(2 << 3);
        
  6. 条件运算符 ? :

  7. 扩展赋值运算符 +=, -=, *=, /=

基础 08 包机制

  • 为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

  • 包语句的语法格式为:

    • package pkg1[. pkg2[. pkg3...]];
      
  • 一般利用公司域名倒置作为包名;

  • 为了能够使用某一个包的成员,需要在Java程序中明确导入该包,使用import语句可完成此功能:

    • import package1[. package2...].(classname|*);
      

基础 09 javadoc生成文档

  • javadoc命令是用来生成自己 API 文档的
  • 参数信息
    • @author作者名
    • @version版本号
    • @since指明需要最早使用的jdk版本
    • @param参数名
    • @return返回值情况
    • @throws异常抛出情况

流程控制

流程控制 01 用户交互Scanner

  • 基本语法

    • Scanner scanner = new Scanner(System.in);
      
  • 通过Scanner类的next()nextLine()方法获取输入的字符串,在读取前一般需要使用hasNext()hasNextLine()判断是否还有输入的数据。

    • import java.util.Scanner;
      public class Hello {
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              System.out.println("使用next方式接收:");
              if(scanner.hasNext()){
                  String str = scanner.next();
                  System.out.println("输入的内容为:"+str);
              }
              // 凡是属于IO流的类如果不关闭会一直占用资源,应该使用完就关掉。
              scanner.close();
          }
      }
      
  • next()

    • 一定要读取到有效字符后才可以结束输入
    • 对输入有效字符之前遇到的空白,next()方法会自动将其去掉
    • 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符
    • next()不能得到带有空格的字符串
  • nextLine()

    • 以 Enter 为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符
    • 可以获得空格

流程控制 02 Scanner进阶使用

  1. 输入整数

    • import java.util.Scanner;
      public class Hello {
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              int i;
              if (scanner.hasNextInt()) {
                  i = scanner.nextInt();
                  System.out.println(i);
              } else {
                  System.out.println("输入的不是整数。");
              }
              scanner.close();
          }
      }
      
  2. 输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果

    • import java.util.Scanner;
      public class Hello {
          public static void main(String[] args) {
              double sum = 0.0D;
              int m = 0;
              Scanner scanner = new Scanner(System.in);
              while (scanner.hasNextDouble()) {
                  sum += scanner.nextDouble();
                  m++;
              }
              System.out.println("总和为:" + sum + "\n平均数为:" + sum / m);
              scanner.close();
          }
      }
      

流程控制 03 顺序结构

  • Java 的基本结构就是顺序结构,除非特别指明,否则就按顺序逐句执行

流程控制 04 if选择结构

  1. if单选择结构

    • if (布尔表达式) {
          // 如果布尔表达式的值为 true 执行代码
      }
      
  2. if双选择结构

    • if (布尔表达式) {
          // 如果布尔表达式的值为 true 执行代码
      } esle {
          // 如果布尔表达式的值为 false 执行代码
      }
      
  3. if多选择结构

    • // 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行
      if (布尔表达式 1) {
          // 如果布尔表达式 1 的值为 true 执行代码
      } esle if (布尔表达式 2) {
          // 如果布尔表达式 2 的值为 true 执行代码
      } esle if (布尔表达式 3) {
          // 如果布尔表达式 3 的值为 true 执行代码
      } esle {
          // 如果以上布尔表达式的值均为 false 执行代码
      } 
      
  4. 嵌套的if结构

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

流程控制 05 switch多选择结构

  • switch语句中的变量类型可以是byteshortint或者char

  • 从 Java SE 7 开始,switch支持字符串String类型了,同时case标签必须为字符串常量或字面量

    • switch (expression) {
      	case value:
      	// 语句
      	break; // 可选
      	case value:
      	// 语句
      	break; // 可选
      	default: // 可选
      	// 语句
      }
      
  • case穿透现象:从满足条件的case value开始,执行后面所有的case,直到遇到 break停止

流程控制 06 while循环

  • while是最基本的循环,其结构为:

    • while (布尔表达式) {
      	// 循环内容
      }
      
  • 只要布尔表达式为true循环就会一直执行下去

  • 大多数情况会让循环停止下来,需要一个让表达式失效的方式来结束循环

  • 少部分情况需要循环一直执行,比如服务器的请求响应监听等

流程控制 07 dowhile 循环

  • 对于while语句而言,如果不满足条件,则不能进入循环

  • dowhile循环和while循环相似,不同的是,dowhile循环至少会执行一次

    • do {
      	// 代码语句
      } while (布尔表达式);
      

流程控制 08 for循环

  • for循环执行的次数在执行前就确定,语法格式如下:

    • for (初始化;布尔表达式;更新) {
          // 代码语句
      }
      
  • IDEA 快捷键:100.for后回车,自动生成:

    • for (int i = 0; i < 100; i++) {
      
      }
      
  • 练习1:计算0到100之间的奇数和偶数的和

    • public static void main(String[] args) {
          int oddsum = 0;
          int evensum = 0;
          for (int i = 0; i < 100; i++) {
              if (i % 2 != 0) {
                  oddsum += i;
              } else {
                  evensum += i;
              }
          }
          System.out.println("奇数的和:" + oddsum);
          System.out.println("偶数的和:" + evensum);
      }
      
  • 练习2:用whilefor循环输出 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 % 15 == 0) {
                  System.out.println();
              }
          }
      }
      
  • 练习3:打印九九乘法表

    • public static void main(String[] args) {
          for (int i = 1; i <= 9; i++) {
              for (int j = 1; j <= i; j++) {
                  System.out.print(j+"×"+i+"="+j*i+"\t");
              }
              System.out.println();
          }
      }
      

流程控制 09 增强for循环

  • Java 5 引入了一种主要用于数组或集合的增强型for循环

  • Java 增强for循环语法格式如下:

    • for (声明语句 : 表达式)
      {
          // 代码句子
      }
      
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

  • 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

    • public static void main(String[] args) {
          int[] numbers = {10, 20, 30, 40, 50};
          for (int x : numbers) {
              System.out.println(x);
          }
      }
      

流程控制 10breakcontinuegoto

  • 在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。

    • public static void main(String[] args) {
          int i = 0;
          while (i < 100) {
              i++;
              System.out.println(i);
              if (i == 30) {
                  break; // 打破 while 循环,至此只输出到 30
              }
          }
          System.out.println("123"); // 证明只是打破 while 循环,函数仍在执行。
      }
      
  • continue用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。

    • public static void main(String[] args) {
          int i = 0;
          while (i < 100) {
              i++;
              if (i % 10 == 0) {
                  System.out.println();
                  continue; // 后面输出 i 不执行,直接进入下一次 while 循环
              }
              System.out.print(i);
          }
      }
      
  • 关于goto关键字

    • goto关键字很早就在程序设计语言中出现。尽管goto仍是 Java 的一个保留字,但并未在语言中得到正式使用 —— Java 没有goto。然而,在breakcontinue这两个关键字的身上,我们仍能看出一些goto的影子 —— 带标签的breakcontinue。例如label:

    • // 输出 101 到 150 之间的质数
      public static void main(String[] args) {
          outer: for (int i = 101; i < 150; i++) {
              for (int j = 2; j < i/2; j++) {
                  if (i % j == 0) {
                      continue outer; // 直接跳转到 outer:
                  }
              }
              System.out.print(i + " ");
          }
      }
      
  • 对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于breakcontinue关键字通常只中断当前循环,但若随同标签使用,它们就会终端到存在标签的地方。

流程控制 12 打印三角形及菱形

  • 打印三角形

    • import java.util.Scanner;
      public class Hello {
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              int n = scanner.nextInt();
              for (int i = 0; i < n; i++) {
                  for (int j = 0; j < n-i-1; j++) {
                      System.out.print(" ");
                  }
                  for (int j = 0; j < 2*i+1; j++) {
                      System.out.print("*");
                  }
                  System.out.println();
              }
          }
      }
      
  • 打印菱形

    • import java.util.Scanner;
      public class Hello {
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              int n = scanner.nextInt();
              for (int i = 0; i < 2*n-1; i++) {
                  if (i < n) {
                      for (int j = 0; j < n-i-1; j++) {
                          System.out.print(" ");
                      }
                      for (int j = 0; j < 2*i+1; j++) {
                          System.out.print("*");
                      }
                  } else {
                      for (int j = 0; j < i-n+1; j++) {
                          System.out.print(" ");
                      }
                      for (int j = 0; j < 4*n-2*i-3; j++) {
                          System.out.print("*");
                      }
                  }
                  System.out.println();
              }
          }
      }
      

方法

方法 01 什么是方法

  1. Java 方法是语句的集合,它们在一起执行一个功能
    • 方法是解决一类问题的步骤的有序组合
    • 方法包含于类或对象中
    • 方法在程序中被创建,在其他地方被引用
  2. 设计方法的原则:
    • 方法的本意是功能块,即实现某个功能的语句块的集合
    • 最好保持方法的原子性, 即一个方法只完成一个功能,以利于后期扩展
  3. 方法的命名规则:
    • 首字母小写驼峰原则

方法 02 方法的定义及调用

  1. 方法的定义

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

    • 返回值类型:returnValueType是方法返回值的数据类型。有些方法没有返回值,在此情况下,returnValueType是关键字void

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

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

      • 形式参数

      • 实参

    • 方法体:包含具体的语句,定义该方法的功能。

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

    • 调用方法:对象名.方法名(实参列表)

    • Java 支持两种调用方法的方式,根据方法是否返回值来选择

    • 当方法返回一个值的时候,方法调用通常被当作一个值

      • int larger = max(30, 40);
        
    • 如果方法返回值是void,方法调用一定是一条语句

      • System.out.println("Hello, world!");
        
    • 值传递(Java) 和 引用传递

方法 03 方法的重载

  • 重载就是在一个类中,有相同的函数名称,但形参不同的函数
  • 方法的重载规则:
    • 方法名称必须相同
    • 参数列表必须不同(个数不同、类型不同、参数排列顺序(需要形参类型不同为前提)不同等)
    • 方法的返回类型可以相同也可以不同
    • 仅仅返回类型不同不足以成为方法的重载
  • 实现理论
    • 方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。

方法 04 命令行传递参数

  • 运行一个程序时候再传递给它消息,需要靠传递命令行参数给main()方法实现

    • package method;
      public class Demo03 {
          public static void main(String[] args) {
              for (int i = 0; i < args.length; i++) {
                  System.out.println("args[" + i + "]: " + args[i]);
              }
          }
      }
      
    • D:\Coding\Java\Learning>javac Demo03
      错误: 仅当显式请求注释处理时才接受类名称 'Demo03'
      1 个错误
      
      D:\Coding\Java\Learning>cd D:\Coding\Java\Learning\src\method
      
      D:\Coding\Java\Learning\src\method>javac Demo03.java
      
      D:\Coding\Java\Learning\src\method>java Demo03
      错误: 找不到或无法加载主类 Demo03
      
      D:\Coding\Java\Learning\src\method>cd..\
      
      D:\Coding\Java\Learning\src>java method.Demo03
      
      D:\Coding\Java\Learning\src>java method.Demo03 this is Enchant_sword
      args[0]: this
      args[1]: is
      args[2]: Enchant_sword
      
      D:\Coding\Java\Learning\src>
      

方法 05 可变参数(不定项参数)

  • JDK 1.5 开始,Java 支持传递同类型的可变参数给一个方法

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

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

    • package method;
      public class Demo04 {
          public static void main(String[] args) {
              // 调用可变参数的方法
              printMax(34,3,3,2,56.5);
              printMax(new double[] {1,2,3});
          }
          public static void printMax(double... numbers) {
              if (numbers.length == 0) {
                  System.out.println("No argument passed");
                  return;
              }
              double result = numbers[0];
              // 排序
              for (int i = 1; i < numbers.length; i++) {
                  if (numbers[i] > result) {
                      result = numbers[i];
                  }
              }
              System.out.println("The max value is " + result);
          }
      }
      

方法 06 递归

  • 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。

  • 递归体:什么时候需要调用自身方法。

    • package method;
      public class Demo06 {
          public static void main(String[] args) {
              System.out.println(f(5));
          }
          public static int f(int n) {
              if (n == 1) { //递归头
                  return 1;
              } else { //递归体,阶乘
                  return n * f(n-1);
              }
          }
      }
      

数组

数组 01 数组声明创建

  • 声明数组变量

    • dataTypep[] arrayRefVar; // 首选方法
      或
      dataTypep arrayRefVar[]; // 效果相同,但非首选方法
      
  • Java 使用new操作符来创建数组

    • dataTypep[] arrayRefVar = new dataType[arraySize];
      
  • 数组通过索引访问,数组索引从 0 开始

  • 获取数组长度:

    • arrays.length
      
    • package array;
      public class Demo01 {
          public static void main(String[] args) {
              int[] nums; // 1.声明1个数组
              nums = new int[10]; // 2.创建1个数组
              nums[0] = 1; // 3.给数组中元素赋值
              nums[1] = 2;
              nums[2] = 3;
              nums[3] = 4;
              nums[4] = 5;
              nums[5] = 6;
              nums[6] = 7;
              nums[7] = 8;
              nums[8] = 9;
              nums[9] = 10;
              int sum = 0;
              for (int i = 0; i < nums.length; i++) {
                  // 增强型 for 循环: for (int num : nums)
                  sum += nums[i];
              }
              System.out.println("总和为:" + sum);
          }
      }
      

数组 02 内存分析 及 三种初始化

  1. Java 内存分析:

      • 存放new的对象和数组
      • 可以被所有的线程共享,不会存放别的对象引用
      • 存放基本变量类型(会包含这个基本类型的具体数值)
      • 引用对象的变量(会存放这个引用在堆里面的具体地址)
    • 方法区

      • 可以被所有的线程共享
      • 包含了所有的classstatic变量
  2. 三种初始化

    • 静态初始化

      • int[] a = {1,2,3,4,5,6,7,8};
        Man[] men = {new Man(1,1), new Man(2,2)};
        
      • 动态初始化

        • int[] a = new int[2];
          a[0] = 1;
          a[1] = 2;
          
      • 数组的默认初始化

        • 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

数组 03 下标越界 及 小结

  • 数组的四个基本特点

    1. 长度确定。数组一旦被创建,其长度不可改变。
    2. 数组元素必须是相同类型,不允许出现混合类型。
    3. 数组元素可以是任何数据类型,包括基本类型和引用类型。
    4. 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java 中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的
  • 数组边界

    • 下标的合法区间:[0, length-1],如果越界就会报错
    • ArrayIndexOutOfBoundsException:数组下标越界异常!

数组 04 数组的使用

  • 普通的for循环

  • for - each循环 (增强型for循环)

  • 数组作方法入参

  • 数组作返回值

    • package array;
      public class Demo03 {
      	public static void main(String[] args) {
      		int[] arrays = {1,2,3,4,5};
      		printArray(arrays);
      		printArray(reverseArray(arrays));
      	}
      	// 打印数组元素(数组作方法入参)
      	public static void printArray(int[] arrays) {
      		// 普通的 for 循环
      		for (int i = 0; i < arrays.length; i++) {
      			// 增强的 for 循环:
      			for (int i : arrays) {
      				System.out.print(arrays[i] + " ");
      			}
      		}
      	}
      	// 反转数组(数组作返回值)
      	public static int[] reverseArray(int[] arrays) {
      		int[] result = new int[arrays.length];
      		for (int i = 0; i < arrays.length; i++) {
      			result[arrays.length - i - 1] = arrays[i];
      		}
      		return result;
      	}
      }
      

数组 05 多维数组

  • 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组

  • 二维数组

    • // 可视作两行五列的数组
      int a[][] = new int[2][5]
      
    • package array;
      public class Demo04 {
          public static void main(String[] args) {
              // [4][2]
              int[][] array = {{1,2},{2,3},{3,4},{4,5}};
              for (int i = 0; i < array.length; i++) {
                  for (int j = 0; j < array[i].length; j++) {
                      System.out.print(array[i][j] + "\t");
                  }
                  System.out.println();
              }
          }
      }
      

数组 06 Arrays

  • Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意:是“不用”而非“不能”)

  • 具有以下常用功能:

    • 给数组赋值:通过fill方法

    • 对数组排序:通过sort方法,按升序

    • 比较数组:通过equals方法比较数组中元素值是否相等

    • 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作

    • package array;
      import java.util.Arrays;
      public class Demo05 {
          public static void main(String[] args) {
              int[] a = {4,7,23,534,76,334,2,56,67,33,45};
              // 数组升序排序
              Arrays.sort(a);
              // 转成字符串输出
              System.out.println(Arrays.toString(a));
              // 将数组下标[2]到下标[8-1]的元素用 0 填充
              Arrays.fill(a,2,8,0);
              // 转成字符串输出
              System.out.println(Arrays.toString(a));
          }
      }
      

数组 07 冒泡排序

  • 嵌套循环,时间复杂度为 O(n^2)

    • package array;
      import java.util.Arrays;
      public class Demo06 {
      	public static void main(String[] args) {
              int[] array = {3,5,756,23,5,4334,65,445,6,4,3,56};
              int[] sort = sort(array);
              System.out.println(Arrays.toString(sort));
          }
          public static int[] sort(int[] a) {
              // 临时变量
              int temp;
              // 外层循环,判断循环执行次数
              for (int i = 0; i < a.length - 1; i++) {
                  // 内层循环,比较两个数大小,并互换位置
                  for (int j = 0; j < a.length - 1 - i ; j++) {
                      // 改为 < 则降序排序
                      if (a[j] > a[j+1]) {
                          /*
                                核心代码,若不引入临时变量 temp,也可简化为
                                a[j] += a[j+1];
                                a[j+1] = a[j] - a[j+1];
                                a[j] -= a[j+1];
                             */
                          temp = a[j];
                          a[j] = a[j+1];
                          a[j+1] = temp;
                      }
                  }
              }
              return a;
          }
      }
      
  • 优化算法

    • for (int i = 0; i < a.length - 1; i++) {
          // 放置标识位,减少无意义的比较
          boollean flag = false;
          for (int j = 0; j < a.length - 1 - i ; j++) {
              if (a[j] > a[j+1]) {
                  temp = a[j];
                  a[j] = a[j+1];
                  a[j+1] = temp;
                  // 只要发生过 1 次 if 交换,则 flag 赋值 true,后面继续比较
                  flag = true;
              }
          }
          if (!flag) {
              break;
          }
      }
      

数组 08 稀疏数组

  • 当一个数组中大部分元素为 0,或者为同一值时,可以使用稀疏数组来保存

  • 稀疏数组的处理方式是:

    • 记录数组一共有几行几列,有多少个不同值

    • 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模

    • 原始数组:
      [ 0 0 0 22 0 0 15 0 11 0 0 0 17 0 0 0 0 − 6 0 0 0 0 0 0 0 0 39 0 91 0 0 0 0 0 0 0 0 28 0 0 0 0 ] \left [ \begin{array}{c} 0 & 0 & 0 & 22 & 0 & 0 & 15 \\ 0 & 11 & 0 & 0 & 0 & 17 & 0 \\ 0 & 0 & 0 & -6 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 39 & 0 \\ 91 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 28 & 0 & 0 & 0 & 0 \\ \end{array} \right ] 0000910011000000000282206000000000017039001500000

    • 稀疏数组:
      [ 行 列 值 ( r o w ) ( c o l ) ( v a l u e ) [ 0 ] 6 7 8 [ 1 ] 0 3 22 [ 2 ] 0 6 15 [ 3 ] 1 1 11 [ 4 ] 1 5 17 [ 5 ] 2 3 − 6 [ 6 ] 3 5 39 [ 7 ] 4 0 91 [ 8 ] 5 2 28 ] \left [ \begin{array}{c|cc} & 行 & 列 & 值 \\ & (row) & (col) & (value) \\ \hline [0] & 6 & 7 & 8 \\ [1] & 0 & 3 & 22 \\ [2] & 0 & 6 & 15 \\ [3] & 1 & 1 & 11 \\ [4] & 1 & 5 & 17 \\ [5] & 2 & 3 & -6 \\ [6] & 3 & 5 & 39 \\ [7] & 4 & 0 & 91 \\ [8] & 5 & 2 & 28 \\ \end{array} \right ] [0][1][2][3][4][5][6][7][8](row)600112345(col)736153502(value)8221511176399128

    • 原始数组和稀疏数组互相转换

      • package array;
        public class Demo07 {
            public static void main(String[] args) {
                // 创建一个 11 * 11 的二维数组
                // 0:没有棋子  1:黑棋  2:白棋
                int[][] array1 = new int[11][11];
                array1[1][2] = 1;
                array1[2][3] = 2;
                // 输出原始的数组(①使用 2 层增强型 for 循环)
                System.out.println("输出原始的数组");
                for (int[] ints : array1) {
                    for (int anInt : ints) {
                        System.out.print(anInt + "\t");
                    }
                    System.out.println();
                }
        
                // 转换为稀疏数组保存
                // 1.获取有效值的个数
                int sum = 0;
                for (int i = 0; i < 11; i++) {
                    for (int j = 0; j < 11; j++) {
                        if (array1[i][j] != 0) {
                            sum ++;
                        }
                    }
                }
                System.out.println("有效值的个数为:" + sum);
                // 2.创建一个稀疏数组的数组
                // 因第 0 行存放数组信息,稀疏数组行数为 sum + 1
                int[][] array2 = new int[sum + 1][3];
                // 列数固定为 3,分别为行坐标、列坐标、值
                array2[0][0] = array1.length;
                array2[0][1] = array1.length;
                array2[0][2] = sum;
                // 3.遍历二维数组,将非零的值存放于稀疏数组中
                int count = 0;
                for (int i = 0; i < array1.length; i++) {
                    for (int j = 0; j < array1[i].length; j++) {
                        if (array1[i][j] != 0) {
                            count ++;
                            array2[count][0] = i;
                            array2[count][1] = j;
                            array2[count][2] = array1[i][j];
                        }
                    }
                }
                // 4.输出转换后的稀疏数组(②使用 1 层常规 for 循环)
                System.out.println("输出转换后的稀疏数组");
                for (int[] item : array2) {
                    System.out.println(item[0] + "\t"
                                       + item[1] + "\t"
                                       + item[2] + "\t");
                }
        
                // 还原为原始数组
                System.out.println("还原为原始数组");
                int rowArray3 = array2[0][0];
                int colArray3 = array2[0][1];
                int countArray3 = array2[0][2];
                int[][] array3 = new int[rowArray3][colArray3];
                for (int i = 0; i < countArray3; i++) {
                    int row = array2[i + 1][0];
                    int col = array2[i + 1][1];
                    int value = array2[i + 1][2];
                    array3[row][col] = value;
                }
                // 输出还原后的原始数组(③使用 2 层常规 for 循环)
                for (int i = 0; i < array3.length; i++) {
                    for (int j = 0; j < array3[i].length; j++) {
                        System.out.print(array3[i][j] + "\t");
                    }
                    System.out.println();
                }
            }
        }
        

面向对象

面向对象 01 何为面向对象

  1. 面向过程 & 面向对象

    • 面向过程思想

      • 步骤清晰简单,第一步、第二步……

      • 适合处理较为简单的问题

      • 面向对象思想

        • 分类的思维模式,先思考解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类的细节进行面向过程的思索。
        • 面向对象适合处理复杂的问题、需要多人协作的问题。
    • 描述复杂的事物,为从宏观上把握、从整体上分析,需要使用面向对象的思路。但具体到微观操作,仍需要面向过程的思路去处理。

  2. 何为面向对象

    • 面向对象编程(Object - Oriented Programming, OOP)

    • 面向对象编程的本质是:以类的方式组织代码,以对象的方式组织(封装)数据。

    • 抽象

      • 三大特性
        • 封装
        • 继承
        • 多态
    • 从认识论角度考虑,是先有对象后有类。对象是具体的事物;类是对对象的抽象。

    • 从代码运行角度考虑,是先有类后有对象。类是对象的模板。

面向对象 02 方法的定义

  • 修饰符
  • 返回类型
  • breakreturn的区别:break结束循环;return结束方法。
  • 方法名:驼峰原则、见名知意
  • 参数列表:(参数类型,参数名)…(可变参数)
  • 异常抛出

面向对象 03 方法的调用

  • 静态方法

    • package oop;
      public class Demo02 {
          public static void main(String[] args) {
              // 调用静态方法
              // 类名.方法名
              Student.say1();
          }
      }
      
  • 非静态方法

    • package oop;
      public class Demo02 {
          public static void main(String[] args) {
              // 调用非静态方法
              // 对象类型 对象名 = new 对象值
              Student student = new Student();
              student.say2();
          }
      }
      
  • 形参和实参

  • 值传递和引用传递

    • package oop;
      // 值传递
      public class Demo04 {
          public static void main(String[] args) {
              int a = 1;
              new Demo04().change(a);
              System.out.println(a); // 输出 a = 1,而非 a = 10
          }
          public void change(int a) {
              a = 10;
          }
      }
      
    • package oop;
      // 引用传递
      public class Demo05 {
          public static void main(String[] args) {
              Person aaa = new Person();
              System.out.println(aaa.name);// 输出 null
              Demo05.change(aaa);
              System.out.println(aaa.name);// 输出 狗蛋
          }
          public static void change(Person bbb) {
              bbb.name = "狗蛋";
          }
      }
      // 定义了一个 Person 类,有一个属性:name
      class Person {
          String name; // 默认初始值 null
      }
      
    • KuangStudy 狂神说 JavaSE 学习笔记_第2张图片

    • KuangStudy 狂神说 JavaSE 学习笔记_第3张图片

  • this关键字

    • KuangStudy 狂神说 JavaSE 学习笔记_第4张图片

    • KuangStudy 狂神说 JavaSE 学习笔记_第5张图片

面向对象 04 类 与 对象

  1. 类与对象的关系

    • 类是一种抽象的数据类型,它是对某一类事物的整体描述 / 定义,但是并不能代表某一个具体的事物。

      • 动物、植物、手机、电脑……
      • Person 类、Pet 类、Car 类等,这些类都是用来描述 / 定义某一类具体的事物应该具备的特征
    • 对象是抽象概念的具体实例

      • “张三”是“人”的具体实例
        • 能体现出特点、展现出功能,而非抽象概念
  2. 创建与初始化对象

    • 使用new关键字创建对象

    • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化,以及调用类中构造器。

    • 类中构造器也称为构造方法,是创建对象时必须要调用的。构造器有以下特点:

      1. 必须和类的名字相同
      2. 必须没有返回类型,也不能写void
    • package oop.demo;
      // 一个项目应该只存在一个 main 方法
      public class Application {
          public static void main(String[] args) {
              // 类:抽象的,实例化
              // 类实例化后会返回一个自己的对象
              // goudan 对象就是一个 Student 类的具体实例
              Student goudan = new Student();
              goudan.name = "狗蛋";
              goudan.age = 3;
              System.out.println(goudan.name);
              System.out.println(goudan.age);
              new Student().study();
          }
      }
      
    • package oop.demo;
      // 学生类
      public class Student {
          // 属性:字段
          String name;
          int age;
          // 方法
          public void study() {
              System.out.println(this.name + "在学习");
          }
      }
      

面向对象 05 构造器详解

  • 构造器:

    1. 和类名相同
    2. 没有返回值
  • 作用:

    1. new(本质在调用构造方法)
    2. 初始化对象的值
  • 注意点:

    • 定义有参构造后,若想使用无参构造,需显式地定义一个无参构造

    • 构造快捷键 alt + ins

    • this. = (this 代表本方法)

    • package oop.demo;
      // 一个项目应该只存在一个 main 方法
      public class Application {
          public static void main(String[] args) {
              // new 实例化了一个对象
              // 1.使用 new 关键字,本质是在调用构造器
              // 2.用来初始化值
              Person person = new Person("狗蛋");
              System.out.println(person.name);
          }
      }
      
    • package oop.demo;
      public class Person {
          // 一个类即使什么都不写,它也会存在一个方法
          String name;
          // 显式地定义无参构造器则如下
          public Person() {
      
          }
          // 有参构造:一旦定义了有参构造,无参就必须显式定义
          // 本质是 Person 方法的重载
          public Person(String name) {
              this.name = name;
          }
          // 快捷键 alt + insert 自动生成构造器
      }
      

面向对象 06 创建对象内存分析

  • KuangStudy 狂神说 JavaSE 学习笔记_第6张图片

面向对象 07 类与对象小结

  1. 类与对象
    • 类是一个抽象的模板;对象是一个具体的实例。
  2. 方法
    • 定义
    • 调用
  3. 对象的引用
    • 引用类型:8 大基本类型之外基本都是引用类型
    • 对象是通过引用来操作的:栈 —> 堆(地址)
  4. 属性:字段(Field)成员变量
    • 默认初始化
      • 数字:0 0.0
      • char:u0000
      • boolean:false
      • 引用:null
    • 修饰符 属性类型 属性名 = 属性值
  5. 对象的创建和使用
    • 必须使用new关键字创造对象(Person goudan = new Person();
    • 对象的属性goudan.name
    • 对象的方法goudan.sleep()
  6. 类:
    • 静态的属性 属性
    • 动态的行为 方法

面向对象 08 封装

  • 该露的露,该藏的藏

    • 程序设计追求**“高内聚,低耦合”**。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
  • 封装(数据的隐藏)

    • 通常,应禁止直接访问一个对象中数据的实际表示,应通过操作接口来访问。
  • 属性私有,get / set

    • package oop.demo;
      // 一个项目应该只存在一个 main 方法
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              s1.setName("狗蛋");
              System.out.println(s1.getName());
              s1.setAge(18);
              System.out.println(s1.getAge());
          }
      }
      
    • package oop.demo;
      // 学生类
      public class Student {
          private String name;
          private int age;
          // alt + insert 快捷键,设置 setter & getter
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
          public int getAge() {
              return age;
          }
          public void setAge(int age) {
              this.age = age;
          }
      }
      

面向对象 09 继承

  • 继承的本质是对某一批类的抽象,从而实现更好地建模

  • extends(扩展),子类是父类的扩展

  • Java 中只有单继承,没有多继承

    • 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等

    • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。

    • 子类和父类之间,从意义上讲应该具有 “is a” 的关系

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              //Student 类已继承 Person 类的全部方法
              s1.say();
          }
      }
      
    • public class Person {
          // public
          // protected
          // default
          // private 
          private int money = 10_0000_0000;
          // Person 类的 say() 方法
          public void say() {
              System.out.println("说了一句话");
          }
          public void setMoney(int money) {
              this.money = money;
          }
          public int getMoney() {
              return money;
          }
      }
      
    • // extends 关键字,继承 Person 类的 say() 方法
      public class Student extends Person {
          
      }
      

面向对象 10 super详解

  1. super调用父类的属性

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              s1.test1("狗蛋"); // print "狗蛋" \n "学生" \n "人类"
          }
      }
      
    • package oop.demo;
      public class Person {
          protected String name = "人类";
      }
      
    • package oop.demo;
      // Student 类继承 Person 类
      public class Student extends Person {
          private String name = "学生";
          public void test1(String name) {
              System.out.println(name); // 局部变量 形式参数name("狗蛋")
              System.out.println(this.name); // 实例变量 "学生"
              System.out.println(super.name); // 实例变量 "人类"
          }
      }
      
  2. super调用父类的方法

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              s1.test2(); // print "学生" \n "学生" \n "人类"
          }
      }
      
    • package oop.demo;
      public class Person {
          public void print() {
              System.out.println("人类");
          }
      }
      
    • package oop.demo;
      public class Student extends Person {
          public void print() {
              System.out.println("学生");
          }
          public void test2() {
              print(); // "学生"
              this.print(); // "学生"
              super.print(); // "人类"
          }
      }
      
  3. super调用构造器(无参)

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              // 虽然只调用了 Student 类,但因为 Student 类 extends 了 Person 类,故会先生成父类 Person 的构造器,再生成子类 Student 的构造器
              Student s1 = new Student(); // print "人类 无参" \n "学生 无参"
          }
      }
      
    • package oop.demo;
      public class Person {
          // 显式无参构造
          public Person() {
              System.out.println("人类 无参");
          }
      }
      
    • package oop.demo;
      public class Student extends Person {
          // 显式无参构造
          public Student() {
              // 隐藏代码 super(); ,调用了父类 Person 的无参构造。调用父类的构造器,必须要在子类构造器的第一行
              System.out.println("学生 无参");
          }
      }
      
  4. super调用构造器(有参)

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              // 调用子类 有参构造器
              Student s1 = new Student("狗蛋"); // print "人类" \n "狗蛋"
          }
      }
      
    • package oop.demo;
      public class Person {
          // 父类 有参构造器
          public Person(String name) {
              System.out.println(name);
          }
      }
      
    • package oop.demo;
      public class Student extends Person {
          // 子类 显式无参构造器
          public Student() {
              super("人类");
              System.out.println("学生 无参");
          }
          // 子类 有参构造器
          // 因父类构造器有参,必须用 super("String") 调用,否则报错
          public Student(String name) {
              super("人类");
              System.out.println(name);
          }
      }
      
  5. 小结

    • super注意点:
      • super调用父类的构造方法,必须在构造方法的第一个
      • super只能出现在子类的方法或构造方法中
      • superthis不能同时调用构造方法
    • VSthis
      • 代表的对象不同
        • this:本身调用者这个对象
        • super:代表父类对象的引用
      • 前提
        • this:没有继承也可以使用
        • super:只能在继承条件下使用
      • 构造方法
        • this():本类的构造
        • super():父类的构造

面向对象 11 方法重写

  1. 静态方法调用

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              // 静态方法的调用只和左边定义的数据类型(B)有关
              B b = new B();
              b.test(); // print "B --> test()"
      
              // 父类的引用指向了子类
              A a = new B();
              a.test(); // print "A --> test()"
          }
      }
      
    • package oop.demo;
      public class A {
          // static 静态方法
          public static void test() {
              System.out.println("A --> test()");
          }
      }
      
    • package oop.demo;
      public class B extends A {
          // static 静态方法
          public static void test() {
              System.out.println("B --> test()");
          }
      }
      
  2. 非静态方法重写

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              B b = new B();
              b.test(); // print "B --> test()"
      
              // 父类的引用指向了子类
              A a = new B(); // 子类重写了父类的方法
              a.test(); // print "B --> test()"
          }
      }
      
    • package oop.demo;
      public class A {
          // 非静态方法
          public void test() {
              System.out.println("A --> test()");
          }
      }
      
    • package oop.demo;
      public class B extends A {
          @Override // 注解:有功能的注释
          // 非静态方法
          public void test() {
              System.out.println("B --> test()");
          }
      }
      
  3. 小结

    • 重写:需要有继承关系,子类重写父类的方法
      • 方法名必须相同
      • 参数列表必须相同
      • 修饰符:范围可以扩大 private < default < protected < public
      • 抛出的异常:范围可以被缩小,但不能扩大
    • 为何需要重写:
      • 父类的功能,子类不一定需要,或者不一定满足
    • 快捷键 alt + insert:override

面向对象 12 多态

  • 即同一方法可以根据发送对象的不同而采用多种不同的行为方式

  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多

  • 多态是方法的多态,属性没有多态性

  • instanceof类型转换(引用类型)

  • 重写前的多态

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              Person s2 = new Student();
              Object s3 = new Student();
              // 子类型 Student 的对象 s1 可以直接调用父类型 Person 的方法
              s1.run(); // print "人类 跑"
              s2.run(); // print "人类 跑"
          }
      }
      
    • package oop.demo;
      public class Person {
          public void run() {
              System.out.println("人类 跑");
          }
      }
      
    • package oop.demo;
      public class Student extends Person {
      
      }
      
  • 重写后的多态,拓展

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              Student s1 = new Student();
              Person s2 = new Student(); // 父类的引用指向子类
              Object s3 = new Student();
              // run() 已被重写
              s2.run(); // print "重写为:飞"
              // s2.eat();报错:父类型 Person 的对象 s2 不能直接调用子类型 Student 的方法,需强制转换类型(Person 转 Student)
              ((Student) s2).eat(); // print "吃"
          }
      
    • package oop.demo;
      public class Person {
          public void run() {
              System.out.println("跑");
          }
      }
      
    • package oop.demo;
      public class Student extends Person {
          // 重写 run()
          @Override
          public void run() {
              System.out.println("重写为:飞");
          }
          public void eat() {
              System.out.println("吃");
          }
      }
      

面向对象 13 instanceof 和类型转换

  • instanceof是 Java 的保留关键字,其作用是:测试左边的对象是否是右边的类的实例,返回boolean的数据类型

    • 比如 Person X = new Student(),我们知道左边 Person 是引用类型,右边 Student 是实际类型

    • X instanceof Y,若 X 的引用类型(Person)和 Y 有没有继承关系,则编译报错

    • 若 X 的实际类型(Student)是 Y 本身,或 Y 的子类,则返回 true,否则返回 false

    • 若 X 的实际类型是 Y 的父类,也返回 false

    • package oop.demo;
      public class Application {
          public static void main(String[] args) {
              // Object > String
              // Object > Person > Teacher
              // Object > Person > Student
              Object o = new Student();
              System.out.println(o instanceof Student); // true
              System.out.println(o instanceof Person); // true
              System.out.println(o instanceof Object); // true
              System.out.println(o instanceof Teacher); // false
              System.out.println(o instanceof String); // false
      
              Person p = new Student();
              System.out.println(p instanceof Student); // true
              System.out.println(p instanceof Person); // true
              System.out.println(p instanceof Object); // true
              System.out.println(p instanceof Teacher); // 引用类型 Person 和 Teacher 无继承关系,编译报错
              System.out.println(p instanceof String); // 引用类型 Person 和 String 无继承关系,编译报错
      
              Student s = new Student();
              System.out.println(s instanceof Student); // true
              System.out.println(s instanceof Person); // true
              System.out.println(s instanceof Object); // true
              System.out.println(s instanceof Teacher); // 引用类型 Student 和 Teacher 无继承关系,编译报错
              System.out.println(s instanceof String); // 引用类型 Student 和 String 无继承关系,编译报错
          }
      }
      
    • package oop.demo;
      public class Person {
      }
      
    • package oop.demo;
      public class Student extends Person {
      }
      
    • package oop.demo;
      public class Teacher {
      }
      
  • 基本类型不能用instanceof判断

面向对象 14 static关键字详解

  • 静态属性

    • package oop.demo;
      public class Student {
          private static int age; // 静态变量
          private double score; // 非静态变量
          public static void main(String[] args) {
              Student s1 = new Student();
              System.out.println(Student.age);
              System.out.println(Student.score); // 报错
              System.out.println(s1.age);
              System.out.println(s1.score);
          }
      }
      
  • 静态方法

    • package oop.demo;
      public class Student {
          // 静态方法
          public static void run() {
          } 
          // 非静态方法
          public void go() {
          } 
          public static void main(String[] args) {
              run();
              go(); // 报错
          }
      }
      
  • 静态代码块

    • package oop.demo;
      public class Student {
          // 执行顺序第 2 ,可用来赋初始值
          {
              System.out.println("匿名代码块"); // 代码块(匿名代码块)
          }
          // 执行顺序第 1 ,只执行 1 次
          static {
              System.out.println("静态代码块"); // 静态代码块
          }
          // 执行顺序第 3
          public Student() {
              System.out.println("构造方法"); // 构造器
          }
      
          public static void main(String[] args) {
              Student s1 = new Student(); // print "静态代码块" \n "匿名代码块" \n "构造方法" \n
              Student s2 = new Student(); // print "匿名代码块" \n "构造方法" \n
          }
      }
      
  • 静态导入包

    • package oop.demo;
      import static java.lang.Math.random; // 静态导入 Math.random() 方法
      import static java.lang.Math.PI; // 静态导入 Math.PI 常量
      public class Student {
          public static void main(String[] args) {
              System.out.println(random());
              System.out.println(PI);
          }
      }
      

面向对象 15 抽象类

  • abstract修饰符可以用来修饰方法(抽象方法),也可以用来修饰类(抽象类)。

  • 抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类。

  • 抽象类不能使用new关键字来创建对象,它是用来让子类继承的。

  • 抽象方法只有声明,没有实现,它是用来让子类实现的。

  • 子类继承抽象类,则必须要实现抽象类未实现的抽象方法,否则该子类也要声明为抽象类

    • package oop.demo;
      public abstract class Person {
          public abstract void doSomething(); // 抽象方法只有声明,没有方法体
      }
      
    • package oop.demo;
      public abstract class Student extends Person {
          // 子类继承抽象类,重写实现抽象类未实现的抽象方法
          @Override
          public void doSomething() {
              System.out.println("抽象类");
          }
      }
      

面向对象 16 接口

  • 普通类:只有具体实现

  • 抽象类:具体实现和规范(抽象方法)都有

  • 接口:只有规范

  • 接口的本质是契约

  • 声明类的关键字是class,声明接口的关键字是interface

    • package oop.demo;
      public interface UserService {
          // 接口中所有定义的属性其实都是静态常量 public static final
          int AGE = 99;
          // 接口中所有定义的方法其实都是抽象的 public abstract
          void add(String name);
          void delete(String name);
          void update(String name);
          void query(String name);
      }
      
    • package oop.demo;
      public interface TimeService {
          void timer();
      }
      
    • package oop.demo;
      public class UserServiceImpl implements UserService, TimeService {
          // 实现了接口的类,就需要重写接口中的方法
          @Override
          public void add(String name) {
          }
          @Override
          public void delete(String name) {
          }
          @Override
          public void update(String name) {
          }
          @Override
          public void query(String name) {
          }
          @Override
          public void timer() {
          }
      }
      
  • 接口的作用:

    • 约束
    • 定义一些方法,让不同的人实现
    • 属性默认都是public static final常量
    • 方法默认都是public abstract抽象
    • 接口中没有构造方法,不能被实例化
    • implements可以实现多个接口
    • 必须要重写接口中的方法

面向对象 17 内部类

  • 内部类就是在一个类的内部再定义一个类。 A 类中定义一个 B 类,则 B 是 A 的内部类,A 是 B 的外部类。

  • 种类

    • 成员内部类

    • 创建成员内部类对象,必须先创建外部类对象,再通过外部类对象.new 构造内部类对象

    • 成员内部类中不能有静态成员

    • 成员内部类可以访问外部类的私有属性

      • package oop.demo;
        public class Application {
            public static void main(String[] args) {
                Outer outer = new Outer();
                outer.out(); // print "这是外部类的方法"
                // 语法 outer.new Inner();
                Outer.Inner inner = outer.new Inner();
                inner.in(); // print "这是内部类的方法"
                inner.getID(); // print 10
                inner.getOut(); // print "这是外部类的方法"
            }
        }
        
      • package oop.demo;
        // 外部类
        public class Outer {
            private int id = 10;
            public void out() {
                System.out.println("这是外部类的方法");
            }
            // 内部类
            public class Inner {
                public void in() {
                    System.out.println("这是内部类的方法");
                }
                // 可获得外部类的私有属性
                public void getID() {
                    System.out.println(id); // print 10
                }
                // 可直接调用外部类的方法
                public void getOut() {
                    out(); // print "这是外部类的方法"
                }
            }
        }
        
    • 静态内部类

    • 静态内部类中只能访问外部类的静态成员

      • package oop.demo;
        public class Application {
            public static void main(String[] args) {
                Outer outer = new Outer();
                outer.out(); // print "这是外部类的方法"
                // 语法 new Outer.Inner();
                Outer.Inner inner = new Outer.Inner();
                inner.in(); // print "这是内部类的方法"
            }
        }
        
      • package oop.demo
        // 外部类
        public class Outer {
            public void out() {
                System.out.println("这是外部类的方法");
            }
            // 静态内部类
            public static class Inner {
                public void in() {
                    System.out.println("这是内部类的方法");
                }
            }
        }
        
    • 局部内部类

    • 局部内部类创建对象时,只能在定义它的方法内部进行

    • 局部内部类可以去访问外部类的私有属性

    • 局部内部类也可以去访问它所在方法的局部变量,但是要求局部变量必须被final修饰

    • jdk 8.0开始,默认为局部变量添加final

      • package oop.demo;
        public class Application {
            public static void main(String[] args) {
                Outer outer = new Outer();
                outer.out(); // print "这是外部类的方法" \n "这是外部类的方法"
            }
        }
        
      • package oop.demo;
        public class Outer {
            public void out() {
                System.out.println("这是外部类的方法");
                class Inner {
                    public void in() {
                        System.out.println("这是内部类的方法");
                    }
                }
                // 局部内部类创建对象,只能在 out() 方法内进行
                Inner inner = new Inner();
                inner.in();
            }
        }
        
    • 匿名内部类

异常

异常 01 ErrorException

  • Java 把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类
  • 检查性异常:最具代表的是用户错误或问题引起的异常,这是程序员无法预见的。例如打开一个不存在的文件时,一个异常就发生了。
  • 运行时异常:是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
  • 在Java API 中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
    • Throwable
      • Error
        • VirtualMachineError虚拟机运行错误
          • StacOverFlowError栈溢出错误
          • OutOfMemoryError内存不足错误
        • AWTError
      • Exception
        • IOExcption
          • EOFException
          • FileNotFoundException找不到文件异常
        • RuntimeException运行时异常
          • ArrithmeticException算术异常
          • MissingResourceException丢失资源
          • ClassNotFoundException找不到类
          • NullPointerException空指针异常
          • IllegalArgumentException不合法参数
          • ArrayIndexOutOfBoundsException数组下标越界
          • UnknownTypeException不明类型异常
  • 错误Error:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,编译中也检查不到。
    • Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关
    • 当 JVM 不再有继续执行操作所需的内存资源时,将出现 Java 虚拟机运行错误VirtualMachineError
    • 发生在虚拟机试图执行应用时,如类定义错误NoClassDefFoundError,链接错误LinkageError,这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
  • ErrorException的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,JVM 一般会选择终止线程。Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能地去处理这些异常。

异常 02 捕获和抛出异常

  • 快捷键 Ctrl + Alt + T

  • 抛出异常

  • 捕获异常

  • 异常处理 5 个关键字

    • trycatchfinallythrowthrows

    • package exception;
      public class Demo01 {
          public static void main(String[] args) {
              int a = 1;
              int b = 0;
              // 假设要捕获多个异常:按照范围从小到大的顺序
              try { // try 监控区域
                  System.out.println(a / b);
              } catch (ArithmeticException e) { // catch 捕获异常
                  System.out.println("运行异常!除数不能为0!");
                  e.printStackTrace(); // 打印错误的栈信息
              } catch (Error e) {
                  System.out.println("Error");
              } catch (Exception e) {
                  System.out.println("Exception");
              } catch (Throwable e) {
                  System.out.println("Throwable");
              } finally { // 处理善后工作
                  System.out.println("finally");
              }
          }
      }
      
    • package exception;
      public class Demo02 {
          public static void main(String[] args) {
              Demo02 test = new Demo02();
              test.test(1,0);
          }
          // 假设该方法中处理不了这个异常
          // public void test(int a, int b) throws ArithmeticException() {
          public void test(int a, int b) {
              if (b == 0) {
                  // 主动抛出异常,一般在方法中使用
                  throw new ArithmeticException();
              }
          }
      }
      

异常 03 自定义异常及经验小结

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

    • 创建自定义异常类

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

    • 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理(程序继续正常运行);否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常(程序中断不继续运行)

    • 在出现异常方法的调用者中捕获并处理异常

    • package exception;
      // 自定义的异常类
      public class MyException extends Exception{
          // 传递数字 >10
          private int detail;
      
          public MyException(int a) {
              this.detail = a;
          }
          // toString:异常的打印信息
          @Override
          public String toString() {
              return "MyException{" + "detail=" + detail + '}';
          }
      }
      
    • package exception;
      public class Test {
          // 可能会存在异常的方法,`throws`关键字抛出
          static void test(int a) throws MyException {
              System.out.println("传递的参数为:" + a);
              if (a > 10) {
                  throw new MyException(a);
              }
              System.out.println("OK");
          }
          public static void main(String[] args) {
              // `try`-`catch`关键字捕获
              try {
                  test(11);
              } catch (MyException e) {
                  System.out.println("自定义异常:" + e);
                  // print 传递的参数为:11 \n 自定义异常:MyException{detail=11}
              }
          }
      }
      
  • 实际应用中的经验总结

    • 处理运行时异常时,采用逻辑去合理规避,同时辅助try-catch处理
    • 在多重catch块后面,可以加一个尽可能大的catch (Exception e)来处理可能会被遗漏的异常
    • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
    • 尽量去处理异常,切忌简单地调用printStackTrace()去打印输出
    • 具体如何处理异常,要根据不同地业务需求和异常类型去决定
    • 尽量添加finally语句块去释放占用的资源

你可能感兴趣的:(学习,java,开发语言)