1. 背景【Java帝国】


  • 1972年C诞生
    • 贴近硬件,运行极快,效率极高(有汇编语言的优点)
    • 操作系统,编辑器,数据库,网络系统等
    • 指针和内存管理
  • 1982年C++诞生
    • 面向对象
    • 兼容C
    • 圆形领域、游戏等
  • 【反抗】我们要建立一个新的语言:
    • 语法有点像C
    • 没有指针
    • 没有内存管理
    • 真正的可移植性,编写一次,到处运行(JVM--Java虚拟机)
    • 面向对象
    • 类型安全
    • 高质量的类库
  • Java初生
    • 1995年的网页简单而粗糙,缺乏互动性
    • 图形界面的程序(Applet)
    • BillDates说:这是迄今为止设计的最好的语言!
    • Java 2 标准版(J2SE):占领桌面
    • Java 2 移动版(J2ME):占领手机
    • Java 2 企业版(J2EE):占领服务器
    • 大量的巨头加入基于Java开发了巨多的平台、系统、工具
      • 构建工具:Ant,Maven,Jekins
      • 应用服务器:Tomcat,Jetty,Jboss,Websphere,weblogic
      • Web开发:Struts,Spring,Hibernate,myBatis
      • 开发工具:Eclipse,Netbean,intellij idea,Jbuider
  • 2006:Hadoop(大数据领域)
  • 2008:Android(手机端)

三高问题:高可用,高性能,高并发

2. Java特性和优势


  • 简单性(易学)
  • 面向对象(万物皆对象)
  • 可移植性(跨平台 window linux Mac)【Write Once;Run Anywhere】
  • 高性能
  • 分布式
  • 动态性【反射】
  • 多线程(变看视频边听音乐)
  • 安全性【捕获异常】
  • 健壮性

3. Java三大版本


  • Write Once;Run Anywhere 【JVM,Java虚拟机可以跑在所有的平台上】

  • JavaSE:标准版(桌面程序,Dos控制台开发……)

  • JavaME:嵌入式开发(手机、小家电……)

  • JavaEE:E企业开发(web端,服务器开发…)

4. JDK、JRE、JVM


  • JDK:Java Development Kit 【Java开发者工具,包含了JRE和JVM】
  • JRE:Java Runtime Environment【Java运行环境,包含了JVM】
  • JVM:Java Virtual Machine【Java虚拟机】
JDK、JRE、JVM

5. Java开发环境搭建

  • JDK下载安装

  • 配置环境变量

  • JDK目录介绍

  • Hello World 及 简单语法规则

  • Notepad++ 安装和使用


5.1 JDK下载安装

5.1.1 卸载JDK

  1. 在环境变量【JAVA_HOME】中找到路径,删除Java的安装目录
  2. 删除环境变量的配置:
    • 删除JAVA_HOME
    • 删除path下关于Java的目录
  3. Dos命令【java -version】验证JDK是否已卸载

5.1.2 下载并安装JDK

  1. 下载JDK

  2. 安装JDK

    • 双击安装
    • 更改目录
    • 记住安装路径,配置环境变量

创建JAVA_HOME

编辑Path
  1. 验证JDK是否安装成功

    • 【Dos命令】java
    • javac
    • java -version

5.2 下载notepad++

5.3 第一个Java程序 【Hello World】

  1. 新建一个文件夹,存放代码

  2. 新建一个Java文件,编写代码

    • 文件后缀名为.java

    • Hello.java

      public class Hello {
          public static void main(String[] args) {
              System.out.println("Hello World!");
          }
      }
      
  3. 编译并运行代码

    打开文件夹,地址前 + cmd 打开命令窗口

    > javac Hello.java    #Java文件 编译成 字节码文件
    > java Hello		 #运行 字节码文件
    
    编译并运行Hello

    可能遇到的情况

  4. 每个单词的大小写不能出现问题,Java是大小写敏感的

  5. 尽量使用英文,避免乱码

  6. 文件名类名必须保持一致,并且首字母大写、

6. Java程序运行机制


  • 编译型(一本书直接翻译出来)【操作系统】

  • 解释型(一本书看一句,翻译一句)【网页,服务器脚本】

  • 程序运行机制

程序运行机制

7. IDEA安装和介绍


7.1 什么是IDE

集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。如微软的Visual Studio系列,Borland的C++ Builder、Delphi系列等。该程序可以独立运行,也可以和其它程序并用。IDE多被用于开发HTML应用软件。例如,许多人在设计网站时使用IDE(如HomeSite、DreamWeaver等),因为很多项任务会自动生成。

用于简化代码开发的工具

7.2 IDEA介绍

7.3 IDEA下载及安装

8. Java基础语法

  • jdk在线帮助文档:

https://docs.oracle.com/javase/8/docs/api/?xd_co_f=47c934d9-e663-4eba-819c-b726fc2d0847

  • 注释、标识符、关键字
  • 数据类型
  • 类型转换
  • 变量、常量
  • 运算符
  • 包机制、Java Doc

8.1 注释、标识符、关键字

8.1.1 Java中的注释有三种:书写注释是一个非常好的习惯

  • 单行注释
  • 多行注释
  • Java Doc 文档注释
    • Java Doc 命令是用来生成自己的API文档的
    • 参数信息
      • @author 作者名
      • @version 版本号
      • @since 指明需要最早使用的 jdk 版本
      • @param 参数名
      • @return 返回值情况
      • @throws 异常抛出情况
//【单行注释】输出一个Hello World!

/*
    【多行注释】
 */

/**
 * 【JavaDoc文档注释】
 */

生成 Java Doc 文档:

  • 通过命令行生成 Java Doc 文档

    //1.命令行
    javadoc -encoding UTF-8 -charset UTF-8 _01_Hello.java
    

    cmd生成doc文档

  • 通过idea生成 Java Doc 文档

    用idea生成 Java Doc 文档

8.1.2 标识符 【Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符

  • 所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始
  • 首字符之后可以是字母(A-Z或者a-z),美元符($),下划线(_)或数字的任何字符组合
  • 不能使用关键字作为变量名或方法名
  • 标识符是大小写敏感
  • 可以是中文名,但是一般不建议这样使用,也不建议使用拼音,很low

8.1.3 关键字

标识符-关键字

8.2 数据类型

  • 强类型语言(Java、C++)【安全性高,速度会慢】

    • 要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用
  • 弱类型语言(JavaScript)

  • Java的数据类型分类两大类(基本类型 和 引用类型)

    • 基本类型

      • 整数类型:byte(占1个字节)、short(占2个字节)、int(占4个字节)、long(占8个字节);

      • 浮点类型:float(占4个字节)、double(占8个字节);

      • 字符串类型:char(占2个字节);

      • boolean类型:true 和 false(占1个位)

        //八大基本数据类型
        
        //整数
        byte num1 = 1;
        int num2 = 1;
        short num3 = 1;
        long num4 = 1L;  //Long类型要在数字后面加个L
        
        //浮点数(小数)
        float num5 = 1.0f;      //Long类型要在数字后面加个f
        double num6 = 1.0;
        
        //字符
        char name = 'a';
        //字符串【String不是关键词,是类】
        String namea = "小李";
        
        //布尔值【是/非】
        boolean flag = true;
        
        System.out.println("------------------整数拓展--------------------");
        //整数拓展:【进制】二进制0b  十进制  八进制0  十六进制0x
        int i1 = 10;
        int i2 = 010;//八进制
        int i3 = 0x11;//十六进制【0-9 A-F】
        System.out.println(i1);
        System.out.println(i2);
        System.out.println(i3);
        
        System.out.println("------------------浮点数拓展--------------------");
        //浮点数拓展:【银行业务怎么表示?钱】
        //BigDecimal    数据工具类
        //float     有限、离散、舍入误差【大约数,接近但不等于】
        //double
        //最好完全避免使用浮点数进行比较
        //最好完全避免使用浮点数进行比较
        //最好完全避免使用浮点数进行比较
        float f = 0.1F;//0.1
        double d = 1.0/10;//0.1
        System.out.println(f == d);//false
        
        float f1 = 123454321F;
        float f2 = f1+1;
        System.out.println(f1 == f2);//true
        
        System.out.println("------------------字符拓展--------------------");
        //字符串拓展
        //所有的字符本质还是数字
        //编码  Unicode  2个字节  0-65536   U0000-UFFFF
        char c1 = 'a';
        char c2 = '中';
        System.out.println(c1);
        System.out.println((int)c1);//强制转换
        System.out.println(c2);
        System.out.println((int)c2);//强制转换
        
        char c3 = '\u0061';
        System.out.println(c3);
        
        System.out.println("------------------转义字符--------------------");
        //转义字符
        // \t换行
        // \n回车
        // ......
        System.out.println("Hello\tWorld\n!");
        
        System.out.println("------------------字符串拓展--------------------");
        //字符串拓展
        //【面向对象】从内存分析
        String sa = new String("Hello World!");
        String sb = new String("Hello World!");
        System.out.println(sa == sb);//false
        
        String sc = "Hello World!";
        String sd = "Hello World!";
        System.out.println(sc == sd);//true
        
        System.out.println("------------------布尔值拓展--------------------");
        //布尔值拓展
        //Less is More!  代码要简单易读
        boolean flag = true;
        if (flag) {
            System.out.println("真");
        }
        
    • 引用类型(类、接口、数组)

科普:什么是字节?

  • 位(bit):是计算机 内部数据存储 的最小单位 (如:11001100是一个八的二进制数)
  • 字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写 B 来表示
  • 1B(byte,字节) = 8 bit(位)
  • 字符:是指计算机中使用的字母、数字、字和符号
字节和位的换算关系

8.3 类型转换

  • 由于Java是强类型语言,所以要进行有些运算的时候需要用到类型转换

    从低到高转换(小数优先级高于整数)
  • 运算中,不同类型的数据先转化为同一类型,然后进行运算

  • 强制类型转换

  • 自动类型转换

//数据类型转换
public void DateTypeChange(){
    int i = 128;
    byte b = (byte)i;//(int)强制转换【高到低】    128溢出了,Byte最大127
    double d = i;//自动转换【低到高】
    System.out.println(b);
    System.out.println(d);

    /*
    注意点:
        1.不能对布尔值进行转换
        2.不能把对象类型转换为不相干的类型
        3.在把高容量转换到低容量的时候,强制转换!
        4.转换的时候可能存在内存溢出,或者精度问题!
     */
    System.out.println("=================精度问题测试====================");
    System.out.println((int)23.7);
    System.out.println((int)-45.89);
    System.out.println("=================精度问题测试====================");
    char c = 'a';
    int c1 = c + 1;
    System.out.println(c1);
    System.out.println((char)c1);//强转

    /*
        操作比较大的数的时候,注意溢出问题
        JDK7新特性:数字之间可以用下划线分割
     */
    int money = 10_0000_0000;
    System.out.println(money);
    int yeas = 20;
    int total = yeas * money;//计算的时候溢出了
    long total1 = yeas * money;//默认是int,计算之后再转换为long,依旧溢出
    long total2 = (long) yeas * money;//先把一个数据转换为long
    System.out.println(total);//计算的时候溢出了
    System.out.println(total1);//计算的时候溢出了
    System.out.println(total2);
}

8.4 变量、常量

8.4.1 变量

  • 变量是什么:就是可以直接变化的量!

  • Java是一种强类型语言,每个变量都必须声明其类型

  • Java变量是程序中最基本的存储单元,其要素包括变量名变量类型作用域

    //数据类型 变量名 = 值;		可以使用逗号隔开来声明多个同类型变量
    
    • 变量作用域
      • 类变量
      • 实例变量
      • 局部变量
    /**
     * 变量和常量
     */
    public class _02_VariableAndConstant {
        //属性:变量
    
        //类变量 static
        static double salary = 2500;
    
        /*
        实例变量:从属于对象【_02_VariableAndConstant】
          如果不自行初始化,会输出这个类型的默认值
              整数:0
              小数:0.0
              字符:0000
              布尔类型:false
              除了基本数据类型,其他的默认值都是:null
         */
        private String name;    //null
        private int age;    //0
    
        //代码块
        static {
            System.out.println("_02_VariableAndConstant");
        }
    
        //main方法
        public static void main(String[] args) {
    
            //局部变量:必须声明 并 初始化值
            int i = 10;
            System.out.println(i);
    
            //输出实例变量
            //变量类型 变量名字 = new 变量类型()
            _02_VariableAndConstant var = new _02_VariableAndConstant();
            System.out.println(var.name);
            System.out.println(var.age);
    
            //输出类变量(可直接输出)
            System.out.println(salary);
        }
    
        //其他方法
        public void variable(){
            //程序的可读性
            String name = "xiaoLi";
            char x = 'X';
            double pi = 3.14;
        }
    }
    
  • 注意事项:

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

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

8.4.2 常量(final)

  • 常量(Constant):初始化(initalize)后不能再改变值!不会变动的值
  • 所谓常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变
  • 常量名一般使用大写字符
//常量(修饰符,不存在先后顺序)
static final double PI = 3.14;

8.5 运算符

Java语言支持如下运算符:【优先级 ()最高

  • 算术运算符:+ - * / % ++ --

  • 赋值运算符:=

  • 关系运算符:> < >= <= == != instanceof(面向对象)

  • 逻辑运算符:&& || !

  • 位运算符:& | ^ ~ >> << >>>(了解)

  • 条件运算符:? :

  • 扩展赋值运算符:+= -= *= /=

8.6 包机制(本质:文件夹)

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

  • 包语句 的语法格式为:

    //package pkg1.pkg2.pkg3...
    package com.xin;
    
  • 一般利用公司域名倒置作为包名 【com.xin】

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

    //import package1.package2.classname|*
    import java.text.SimpleDateFormat;
    import java.util.Date;
    

9. Java 流程控制

  • 用户交互 Scanner

  • 顺序结构

  • 选择结构

  • 循环结构

  • break & continue

  • 练习


9.1 Scanner 对象

  • 之前我们学的基本语法中并没有实现程序和人的交互,但是Java给我们提供了这样的工具类,我们可以获取用户的输入。java.util.Scanner 是 Java5 的新特性,我们可以通过 Scanner 类来获取用户的输入

  • 基本语法:

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

    • next():
      1. 一定要读取到有效字符后才可以结束输入;
      2. 对输入有效字符之前遇到的空白,next()方法会自动将其去掉;
      3. 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符;
      4. next() 不能得到带有空格的字符串
    • nextLine():
      1. 以 Enter 为结束符,也就是说 nextLine() 方法返回的是输入回车之前的所有字符;
      2. 可以获得空白。
package com.xin._20_process_control;

import java.util.Scanner;

/**
 * Scanner 扫描器【实现程序和人的交互】
 *      1.截止空格(以 空格 为结束符)【scannerNext(scanner)】
 *      2.截止回车(以 Enter 为结束符)【scannerNextLine(scanner)】
 *      3.Scanner 进阶【输入整数和浮点数】【scannerAdvanced(scanner)】
 *      4.【练习】我们可以输入多个数字,并求其总和与平均数,没输入一个数字用回车确定,通过输入非数字来结束输入并输出执行结果
 */
public class _01_Scanner {
    public static void main(String[] args) {
        //【创建】一个扫描器对象,用于接收键盘数据
        Scanner scanner = new java.util.Scanner(System.in);//.useDelimiter("\n");

        //截止空格(以 空格 为结束符)
        scannerNext(scanner);
        //截止回车(以 Enter 为结束符)
        scannerNextLine(scanner);
        //Scanner 进阶【输入整数和浮点数】
        scannerAdvanced(scanner);

        //练习
        scannerPractice(scanner);

        //凡是属于IO流的类,如果不关闭会一直占用资源,要养成好习惯用完就【关掉】
        scanner.close();
    }

    /**
     * next():
     * 1. 一定要读取到有效字符后才可以结束输入;
     * 2. 对输入有效字符之前遇到的空白,next()方法会自动将其去掉;
     * 3. 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符;
     * 4. <span style="color:#FF0000;">next() 不能得到带有空格的字符串</span>。
     * @param scanner
     */
    private static void scannerNext(Scanner scanner) {
        System.out.println("使用next()方式获取输入的字符串:");

        //判断是否还有输入
        if(scanner.hasNext()){
            //使用next()方式获取输入的字符串:
            String str = scanner.next();
            System.out.println("输入的内容为:" + str);//截止空格
        }
    }

    /**
     * nextLine()
     * 1. 以 Enter为结束符,也就是说 nextLine() 方法返回的是输入回车之前的所有字符;
     * 2. 可以获得空白。
     * @param scanner
     */
    private static void scannerNextLine(Scanner scanner) {
        System.out.println("使用next()方式获取输入的字符串:");

        //判断是否还有输入
        if(scanner.hasNextLine()){
            //使用nextLine()方式获取输入的字符串:
            String str = scanner.nextLine();//截止回车
            System.out.println("输入的内容为:" + str);
        }
    }

    /**
     * Scanner 进阶
     * 输入整数、浮点数
     * @param scanner
     */
    private static void scannerAdvanced(Scanner scanner) {
        int i= 0;
        float f = 0.0f;

        System.out.print("请输入整数:");
        if(scanner.hasNextInt()){
            i = scanner.nextInt();
            System.out.println("输入的内容为:" + i);
        }else if(scanner.hasNextFloat()){
            System.out.println("输入的不是整数数据!");
            f = scanner.nextFloat();
            System.out.println("输入的内容为浮点数:" + f);
        }
    }

    /**
     * 【练习】
     * 我们可以输入多个数字,并求其总和与平均数,没输入一个数字用回车确定,通过输入非数字来结束输入并输出执行结果
     * @param scanner
     */
    private static void scannerPractice(Scanner scanner) {
        //计数
        int i = 0;
        //和
        double num = 0;

        System.out.println("请输入参数:");
        //通过循环判断是否还有输入,并在里面对每一次进行求和和统计
        while(scanner.hasNextLine()){
            if(scanner.hasNextDouble()){
                System.out.println("请继续输入:");
                i++;
                num += scanner.nextDouble();
            }else{
                System.out.println("参数获取成功,计算中请等待。。。");
                break;
            }
        }
        System.out.println("总和:" + num);
        System.out.println("平均数:" + (num/i));
    }
}

9.2 顺序结构

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

  • 顺序结构是最简单的算法结构

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

9.3 选择结构【重点】

  • if 单选择结构
  • if 双选择结构
  • if 多选择结构
  • 嵌套的 if 结构
  • switch 多选择结构

  1. if 单选择结构

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

    • 语法:

      if(布尔表达式){
      	//如果布尔表达式为true将执行的语句
      }
      
      if 单选择结构
  2. if 双选择结构

    • 现在有个需求:公司要收购一个软件,成功了,给人支付100万元;失败了自己找人开发。

      这样的需求用一个 if 就搞不定了,我们需要有两个判断,需要一个双选择结构,所以有了 if-else 结构。

    • 语法:

      if(布尔表达式){
          //如果布尔表达式的结果为 true
      }else {
          //如果布尔表达式的结果为 false
      }
      
      if 双选择结构
  3. if 多选择结构

    • 我们发现刚才的代码不符合实际情况,真实的情况还可能存在ABCD,存在区间多级判断。

      比如 90-100 就是 A,80-90 就是B..等等,在生活中我们很多时候的选择也不仅仅只有两个,所以我们需要一个多选结构来处理这类问题!

    • 语法:

      if(布尔表达式 1){
          //如果 布尔表达式 1 的值为 true 执行代码
      }else if(布尔表达式 2){
          //如果 布尔表达式 2 的值为 true 执行代码
      }else if(布尔表达式 3){
          //如果 布尔表达式 3 的值为 true 执行代码
      }else {
          //如果以上布尔表达式都不为 true 执行代码
      }
      
      if 多选择结构
  4. 嵌套的 if 结构

    • 使用嵌套的 if…else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。你可以像 if 语句一样嵌套 else if …else.

    • 语法:

      if(布尔表达式 1){
          //如果布尔表达式 1的值为true执行代码
          if(布尔表达式 2){
              //如果布尔表达式 2的值为true执行代码
          }
      }
      
    • 思考?我们需要寻找一个数,在1-100之间

  5. switch 多选择结构【匹配一个具体的值】

    • 多选择结构还有一个实现方式就是 switch case 语句
    • switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支
    • switch 语句中的变量类型可以是:
      • byte、short、int 或者 char
      • 从 Java SE 7开始,switch 支出字符串String 类型了,同时 case 标签必须为字符串常量或字面量
    switch(expression){
        case value:
            //语句
            break;//可选
        case value:
            //语句
            break;//可选
        default://可选
            //语句
    }
    
package com.xin._20_process_control;

import java.util.Scanner;

/**
 * 选择结构
 *      1. if 单选择结构
 *      2. if 双选择结构
 *      3. if 多选择结构
 *      4.嵌套的 if 结构
 *      5. switch 多选择结构
 */
public class _03_SelectStruct {

    public static void main(String[] args) {
        //打开
        Scanner sc = new Scanner(System.in);

//        //1. if 单选择结构
//        selectStruct_IfSingle(sc);
//        //2. if 双选择结构
//        selectStruct_IfDouble(sc);
//        //3. if 多选择结构
//        selectStruct_IfMultiple(sc);
//        //4.嵌套的 if 结构
//        selectStruct_IfNested(sc);
        //5. switch 多选择结构
        selectStruct_SwitchMultiple(sc);

        //关闭
        sc.close();
    }

    /**
     * 1. if 单选择结构
     * @param sc
     */
    private static void selectStruct_IfSingle(Scanner sc) {
        //接收用户的输入
        scSystemIn(sc);
        if(sc.hasNextLine()){
            String line = sc.nextLine();

            if("hello".equals(line)){
                //如果输入的内容为 hello 就输出
                System.out.println("已输入:"+line);
            }
        }
        System.out.println("END");
    }

    /**
     * 2. if 双选择结构
     * @param sc
     */
    private static void selectStruct_IfDouble(Scanner sc){
        //考试分数大于60就是及格,小于60就是不及格
        scSystemIn(sc);
        if(sc.hasNextLine()){
            if(sc.hasNextDouble()){
                double score = sc.nextDouble();
                if(score > 60){
                    System.out.println("及格");
                }else {
                    System.out.println("不及格");
                }
            }else{
                System.out.println("输入内容不合法,请重新输入!");
            }
        }
    }

    /**
     * 嵌套的 if 结构
     * 【0-100 猜数游戏】
     * @param sc
     */
    private static void selectStruct_IfNested(Scanner sc){
        double randomNum = Math.random();
        //将随机数转换为整数
        int randomInt = (int)(randomNum * 100)+1; //生成0到100之间的随机整数

        System.out.println("游戏开始啦,请猜一个0-100之间的数字:");
        while(sc.hasNextInt()){
            int i = sc.nextInt();
            if(randomInt == i){
                System.out.println("恭喜!输入正确,本次游戏的数字为:" + randomInt);
                return;
            }else if(i < randomInt){
                System.out.println("输入的数字太小啦,请重新输入:");
            }else {
                System.out.println("输入的数字太大啦,请重新输入:");
            }
        }
    }

    /**
     * 3. if 多选择结构
     * @param sc
     */
    public static void selectStruct_IfMultiple(Scanner sc){
        scSystemIn(sc);
        if(sc.hasNextLine()){
            if(sc.hasNextDouble()){
                double score = sc.nextDouble();
                if(score == 100){
                    System.out.println("A级");
                }else if(score < 100 && score >= 90){
                    System.out.println("B级");
                }else if(score < 90 && score >= 80){
                    System.out.println("C级");
                }else if(score < 80 && score >= 70){
                    System.out.println("D级");
                }else if(score < 70 && score >= 60){
                    System.out.println("E级");
                }else if(score < 60 && score >= 0){
                    System.out.println("不及格");
                }else{
                    System.out.println("成绩不合法");
                }
            }else{
                System.out.println("输入内容不合法,请重新输入!");
            }
        }
    }

    /**
     * 5. switch 多选择结构
     * @param sc
     */
    private static void selectStruct_SwitchMultiple(Scanner sc) {
        //请输入
        scSystemIn(sc);
        String grade = sc.nextLine();
        //case 穿透【break 判断是否继续输出】
        switch (grade){
            case "A":
                System.out.println("优秀!");
                break;
            case "B":
                System.out.println("良好!");
                break;
            case "C":
                System.out.println("加油!");
                break;
            default:
                System.out.println("输入不合法,请检查!");
        }

        //从 Java SE 7开始,switch 支出字符串String 类型了,同时 case 标签必须为字符串常量或字面量
        //字符的本质还是数字
        //反编译   java --- class(字节码文件) --- 反编译(idea)
        String name = sc.nextLine();
        switch (name){
            case "小李":
                System.out.println("输出结果为:小李!");
                break;
            case "小王":
                System.out.println("输出结果为:小王!");
                break;
            case "小张":
                System.out.println("输出结果为:小张!");
                break;
            default:
                System.out.println("End");
        }
    }

    private static void scSystemIn(Scanner sc) {
        System.out.println("请输入:");

    }
}

9.4 循环结构

  • while 循环

  • do ... while 循环

  • for 循环

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


  1. while 循环

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

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

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

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

    • 循环条件一直为 true 就会造成无限循环【死循环】,我们正常的业务编程中应该尽量避免死循环,会影响程序性能或者造成程序卡死崩溃!

    • 思考:计算1+2+3+……+100=?

  2. do ... while 循环

    • 对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。

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

      do{
          //代码语句
      }while(布尔表达式);
      
    • while 和 do ... while 的区别:

      • while 先判断后执行,do ... while 是先执行后判断;
      • do ... while 总是保证循环体会被至少执行一次!这是他们的主要差别。
  3. for 循环

    • 虽然所有循环结构都可以用 while 或者 do…while 表示,但Java提供了另一种语句 -- for 循环,使一些循环结构变得更加简单

    • for 循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构

    • for 循环执行的次数是在执行前就确定的

    • 语法:

      for(初始化;布尔表达式;更新){
          //代码语句
      }
      
    • 练习1:计算0-100之间的奇数和偶数的和

    • 练习2:用 while 或 for 循环输出 1-1000之间能被5整除的数,并每行输出3个

    • 练习3:打印九九乘法表

  4. 增强 for 循环

    • 这里我们先只是见一面,做个了解,之后数组我们重点使用

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

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

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

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

package com.xin._20_process_control;

/**
 * 循环结构
 *      1. while 循环            【loopStruct_While】
 *      2. do ... while 循环     【loopStruct_DoWhile】 【loopStruct_While_DoWhile】
 *      3. for 循环
 *      4.增强 for 循环
 */
public class _04_LoopStruct {
    public static void main(String[] args) {
        //1. while 循环
//        loopStruct_While();
//
//        //2. do ... while 循环
//        loopStruct_DoWhile();
//
//        //while 和 do ... while 的区别
//        loopStruct_While_DoWhile();

        //3. for 循环
        loopStruct_For();
        //4.增强 for 循环
        loopStruct_ForEnhance();
    }

    /**
     * 1. while 循环
     * 思考:计算1+2+3+……+100=?
     */
    private static void loopStruct_While() {
        int i = 0;
        int num = 0;
        while (i <= 100) {
            num += i;
            i++;
        }
        System.out.println(num);
    }

    /**
     * 2. do ... while 循环
     * 思考:计算1+2+3+……+100=?
     */
    private static void loopStruct_DoWhile() {
        int i = 0;
        int num = 0;
        do {
            num += i;
            i++;
        }while (i <= 100);
        System.out.println(num);
    }

    /**
     * while 和 do ... while 的区别
     *  1. while 先判断后执行,do ... while 是先执行后判断;
     *  2. do ... while 总是保证循环体会被至少执行一次!这是他们的主要差别。
     */
    private static void loopStruct_While_DoWhile() {
        int i = 0;
        while (i < 0 ){
            i++;
            System.out.println("while :" + i);
        }

        do{
            i++;
            System.out.println("do ... while :" + i);
        }while(i<0);
    }

    /**
     * 3. for 循环
     *   练习 1:计算0-100之间的奇数和偶数的和
     *   练习 2:用 while 或 for 循环输出 1-1000之间能被5整除的数,并每行输出3个
     *   练习 3:打印九九乘法表
     */
    private static void loopStruct_For() {
        //练习 1:计算0-100之间的奇数和偶数的和
        System.out.println("练习 1:计算0-100之间的奇数和偶数的和");
        int oddNumber = 0;//奇数
        int evenNumber = 0;//偶数
        for(int i = 0; i <= 100; i++){
            if(i%2==0){
                evenNumber += i;
            }else{
                oddNumber += i;
            }
        }
        System.out.println("奇数和:" + oddNumber);
        System.out.println("偶数和为:" + evenNumber);

        //练习 2:用 while 或 for 循环输出 1-1000之间能被5整除的数,并每行输出3个
        System.out.println("练习 2:用 while 或 for 循环输出 1-1000之间能被5整除的数,并每行输出3个");
        //for 循环
        for (int i = 1; i < 1000; i++) {
            if(i%(5*3)==0){
                System.out.println(setThreeDigitNumber(i, 3));
            }else if(i%5==0){
                System.out.print(setThreeDigitNumber(i, 3) + "\t\t");
            }
        }
        System.out.println();

        //while 循环
//        int z=1;
//        while(z<=1000){
//            if(z%(5*3)==0){
//                System.out.println(setThreeDigitNumber(z, 3));
//            }else if(z%5==0){
//                System.out.print(setThreeDigitNumber(z, 3) + "\t\t");
//            }
//            z++;
//        }

        //练习 3:打印九九乘法表
        System.out.println("练习 3:打印九九乘法表");
        for(int i = 1;i<=9;i++){
            for(int j = 1;j<=i;j++){
                System.out.print(j +"*" + i + "=" + setThreeDigitNumber(j*i,2) + "\t");
            }
            System.out.println();
        }
    }

    /**
     * 数字转化为 n位
     * @param i
     * @param digit
     */
    private static String setThreeDigitNumber(int i, int digit) {
        StringBuilder result = new StringBuilder(i + "");
        while(result.length()<digit){
            result.insert(0, " ");
        }
        return result.toString();
    }

    /**
     * 4.增强 for 循环
     */
    private static void loopStruct_ForEnhance() {
        //定义了一个数组
        int[] arr = {10,20,30,40,50};
        //遍历数组的元素
        for (int i : arr) {
            System.out.println(i);
        }
    }
}

9.5 break & continue

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

  • continue 语句用在循环体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。

    package com.xin._20_process_control;
    
    /**
     * break & continue
     */
    public class _05_BreakAndContinue {
        public static void main(String[] args) {
            //break
            breakPractice();
    
            //continue
            continuePractice();
        }
    
        /**
         * break
         * break 在任何循环语句的主体部分,均可用 break 控制循环的流程。break 用于强行退出循环,不执行循环中剩余的语句。
         */
        private static void breakPractice(){
            int i=0;
            while (i<100){
                i++;
                System.out.println(i);
                if(i==30){
                    break;
                }
            }
            System.out.println("END");
        }
    
        /**
         * continue
         * continue 语句用在循环体中,用于终止**某次**循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
         */
        private static void continuePractice(){
            int i=0;
            while (i<100){
                i++;
                if(i%10==0){
                    System.out.println();
                    continue;
                }
                System.out.print(i);
            }
            System.out.println("END");
        }
    }
    
  • 关于 goto 关键字

    • goto 关键字很早就在程序设计语言中出现。尽管 goto 仍是 Java 的一个保留字,但并未在语言中得到正式使用;Java没有 goto 。然而,在 break 和 continue 这两个关键字的身上,我们仍然能看出一些 goto 的影子——带标签的 break 和 continue 。
    • “标签”是指后面跟一个冒号的标识符,例如:label:
    • 对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于 break 和 continue 关键字通常只中断当前循环,但若随同标签使用,他们就会中断到存在标签的地方。
    package com.xin._20_process_control;
    
    /**
     * 不用掌握,长见识
     */
    public class _06_LabelGoto {
        public static void main(String[] args) {
            //打印101-150之间的所有质数
            //质数是指在大于1的自然数中,除了1和它本身之外不再有其他因数的自然数
    //        findPrimeNumbers(101, 150);
    
            findPrimeNumbers_Goto(101, 150);
        }
    
        private static void findPrimeNumbers(int start, int end) {
            for (int i = start; i <= end; i++) {
                boolean flag = true;
                for (int j = 2; j < i/2; j++) {
                    if(i%j==0){
                        flag = false;
                        break;
                    }
                }
                if(flag){
                    System.out.println(i);
                }
            }
        }
    
        /**
         * 带标签的 break 和 continue
         * 不建议使用,了解
         * @param start
         * @param end
         */
        private static void findPrimeNumbers_Goto(int start, int end) {
            outer:for (int i = start; i <= end; i++) {
                for (int j = 2; j < i/2; j++) {
                    if(i%j==0){
                        continue outer;
                    }
                }
                System.out.println(i);
            }
        }
    }
    

9.6 练习

【打印三角形】

package com.xin._20_process_control;

/**
 * 练习:打印三角形
 */
public class _07_Practice {
    public static void main(String[] args) {
        printTriangle(10);
    }

    private static void printTriangle(int length) {
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < (length-i); j++) {
                System.out.print("  ");
            }
            for (int j = 0; j < (2*i+1); j++) {
                System.out.print("* ");
            }
            System.out.println();
        }
    }
}

10. Java 方法详解

  • 何为方法
  • 方法的定义及调用
  • 方法重载
  • 命令行传参
  • 可变参数
  • 递归

10.1 何为方法?

  • System.out.println(),那么它是什么呢?【类.对象.方法】

  • Java 方法的语句的集合,它们在一起执行一个功能

    • 方法是解决一类问题的步骤的有序组合
    • 方法包含于类或对象中
    • 方法在程序中被创建,在其他地方被引用
  • 设计方法的原则:方法的本意的功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性就是一个方法只完成一个功能,这样利于我们后期的扩展

  • 回顾:方法的命名规则?

10.2 方法的定义及调用

10.2.1 方法的定义

​ Java 的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:

  • 方法包含一个方法头和一个方法体。下面的一个方法的所有部分:
    • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
    • 返回值类型:方法可能会返回值。returnValueType 是方法返回值的数据类型。有些地方执行所需的操作,但没有返回值,在这种情况下,returnValueType 是关键字 void。
    • 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
    • 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数
      • 形式参数:在方法被调用时,用于接收外界输入的数据。
      • 实参:调用方法时,实际传给方法的数据。
    • 方法体:方法体包含具体的语句,定义该方法的功能
修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}

10.2.2 方法的调用

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

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

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

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

      System.out.println("Hello,World");
      
  • 课后拓展了解:值传递(Java) 和 引用传递

10.3 方法的重载【重要】

  • 重载就是在一个类中,有相同的函数名称,但形参不同的函数。

  • 方法的重载的规则:

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

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

10.4 命令行传参

  • 有时候你希望运行一个程序的时候再传递给它消息。这要靠传递命令行参数给 main() 函数实现。
/**
 * 命令行传参
 */
public class method_02 {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "]:" + args[i]);
        }
    }
}

命令行传参

10.5 可变参数

  • JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
  • 在方法声明中,在指定参数类型后加一个省略号(…)。
  • 一个方法中只能指定一个可变参数,它必须是方法是最后一个参数。任何扑通的参数必须在它之前声明。
public class method_03_Change {
    public static void main(String[] args) {
        int add = add(1, 2, 3, 4, 5);
        System.out.println(add);
    }

    //可变参数
    public static int add(int ... a) {
        int sum = 0;
        for (int i: a){
            sum += i;
        }
        return sum;
    }
}

10.6 递归【重点 / 难点】

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

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

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

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

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

思想的学习,尽量避免使用

作业:写一个计算器,要求实现加减乘除功能,并且能够循环接收新的数据,通过用户交互实现。

  • 思路推荐:
    • 写四个方法:加减乘除
    • 利用循环+switch进行用户交互
    • 传递需要操作的两个数
    • 输出结果
package com.xin._03_method;

import java.util.Scanner;

/**
 * 作业:写一个计算器,要求实现加减乘除功能,并且能够循环接收新的数据,通过用户交互实现。
 *
 * 思路推荐:
 *   写四个方法:加减乘除
 *   利用循环+switch进行用户交互
 *   传递需要操作的两个数
 *   输出结果
 */
public class method_04_RecursionPractice {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        calc(sc);

        sc.close();

    }

    private static void calc(Scanner sc){
        System.out.println("请输入:");
        String s = sc.nextLine();
        if("q".equals(s)){
            System.out.println("程序已退出");
            return;
        }
        String type = obtainType(s);
        //转移字符
        String[] split = s.split("\\" + type);

        switch (type){
            case "+":
                double d = add(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
                System.out.println("结果为:" + d);
                break;
            case "-":
                d = subtract(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
                System.out.println("结果为:" + d);
                break;
            case "*":
                d = multiple(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
                System.out.println("结果为:" + d);
                break;
            case "/":
                d = divide(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
                System.out.println("结果为:" + d);
                break;
            default:
                System.out.println("输入数据不合法,请重新输入!");
        }
        calc(sc);
    }

    private static String obtainType(String s) {
        //1.字符串拆分成字符数组  遍历
        for(char c : s.toCharArray()){
            if(!(c>='0' && c<='9')){
                return c+"";
            }
        }
        return "error";
    }

    private static double add(double a, double b) {
        return a+b;
    }

    private static double subtract(double a, double b) {
        return a-b;
    }

    private static double multiple(double a, double b) {
        return a*b;
    }

    private static double divide(double a, double b) {
        return a/b;
    }
}

11. 数组

  • 数组概述
  • 数组声明创建
  • 数组使用
  • 多维数组
  • Arrays 类
  • 稀疏数组

11.1 数组的定义

  • 数组是相同类型数据的有序集合。
  • 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
  • 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。

11.2 数组的声明创建

11.2.1 数组的声明创建

  • 首先必须声明数组变量,才能在程序中使用数组,下面是声明数组变量的语法:

    dataType[] arrayRefVar;	//首选的方法
    dataType arrayRefVar[];	//效果相同,但不是首选方法
    
  • Java 语言使用 new 操作符来创建数组,语法如下:

    dataType[] arrayRefVar = new dataType[arraySize];
    
  • 数组的元素是通过索引访问的,数组索引从 0 开始。

  • 获取数组长度:

    arrays.length
    

11.2.2 内存分析

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

Java 内存分析

  • 写代码画图分析内存!

    /**
     * 数组声明创建
     */
    public class Array_01 {
        //变量的类型     变量的名字 = 变量的值
        public static void main(String[] args) {
            //1.声明一个数组
            int[] nums;
            //2.创建一个数组
            nums = new int[10];//这里面可以存放 10 个 int 类型的数字
            //输出长度
            System.out.println(nums.length);
    
            //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;
            System.out.println(nums[0]);//不赋值,默认值为 0
            System.out.println(nums[9]);
            System.out.println(nums[10]);//报错【数组下标越界】 java.lang.ArrayIndexOutOfBoundsException
    
            //4.变量数组
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
            System.out.println("总和为:" + sum);
        }
    }
    

画图分析内存

11.2.3 三种初始化

  • 静态初始化

    int[] a = {1,2,3};
    Man[] mans = {new Man(1,1), new Man(2,2)};
    
  • 动态初始化

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

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

11.2.4 数组的四个基本特点

  • 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。

  • 其元素必须是相同类型,不允许出现混合类型。

  • 数组中元素可以是任何数据类型,包括基本类型和引用类型。

  • 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。

    数组本身就是对象,Java 中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。

11.2.5 数组边界

  • 下标的合法区间:[0, length-1],如果越界就会报错;

    public static void main(String[] args) {
        int[] nums = new int[2];
        System.out.println(nums[2]);//越界报错
    }
    
  • java.lang.ArrayIndexOutOfBoundsException:数组下标越界异常!

  • 小结:

    • 数组是相同数据类型(数据类型:任意类型)的有序集合。
    • 数组也是对象。数组元素相当于对象的成员变量。
    • 数组长度是确定的,不可改变的。如果越界,直接报错:ArrayIndexOutOfBoundsException

11.3 数组使用

  • For-Each 循环
  • 数组 作 方法入参
  • 数组 作 返回值
package com.xin.array;

/**
 * 数组的使用
 */
public class Array_03 {
    public static void main(String[] args) {
        int[] arrays = {1,2,16,4,5};

        int sum = 0;
        for (int array : arrays) {
            //打印全部的数组元素
            System.out.println(array);
            //计算所有元素的和
            sum += array;
        }
        System.out.println("数组和:" + sum);

        //获取数组中最大的元素
        System.out.println("数组中最大元素:" + maxData(arrays));

        //反转数组
        for (int i : reverseArray(arrays)) {
            System.out.println(i);
        }
    }


    /**
     * 获取数组中最大的元素
     * @param arrays
     * @return
     */
    private static int maxData(int[] arrays){
        int max = arrays[0];
        for (int array : arrays) {
            if (array > max) {
                max = array;
            }
        }
        return max;
    }

    /**
     * 反转数组
     * @param arrays
     * @return
     */
    private static int[] reverseArray(int[] arrays){
        int[] reversedArray = new int[arrays.length];
        for (int i = 0; i < arrays.length; i++) {
            reversedArray[i] = arrays[arrays.length - 1 - i];
        }
        return reversedArray;
    }
}

11.4 多维数组

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

  • 二维数组:

    int a[][] = new int[2][5];
    
  • 解析:以上二维数组 a 可以看成一个两行五列的数组。

  • 思考:多维数组的使用?

/**
 * 多维数组
 */
public class Array_04_Multiply {
    public static void main(String[] args) {

        //[4][2]    之后会学面向对象,了解
        /*
        *   1,2     arrays[0]
        *   2,3     arrays[1]
        *   3,4     arrays[2]
        *   4,5     arrays[3]
        * */
        int[][] arrays = {{1,2},{2,3},{3,4},{4,5}};

        System.out.println(arrays[0]);//一个对象

        //打印二维数组
        for (int[] array : arrays) {
            for (int i : array) {
                System.out.print(i + " ");
            }
            System.out.println();
        }
    }
}

11.5 Arrays 类

  • 数组的工具类 java.util.Arrays

  • 由于数组对象本身并没有什么方法可以供我们调用,但 API 中提供了一个工具类 Arrays 供我们使用,从而可以对数据对象进行一些基本的操作。

  • 查看 JDK 帮助文档

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

  • 具有以下常用功能:

    • 给数组赋值:通过 fill 方法。
    • 对数组排序:通过 sort 方法,按升序。
    • 比较数组:通过 equals 方法比较数组中元素值是否相等。
    • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
public class Array_05_Arrays {
    public static void main(String[] args) {
        int[] a = {2,5,67,9886,2345,7860,37456,2,56,21};
        System.out.println(a);//[I@1eb44e46 -- 会输出Hashcode,学完【反射】会看懂这个

        //打印数组元素
        System.out.println(Arrays.toString(a));//有方法不要自己写,不要重复造轮子

        //排序数组元素
        Arrays.sort(a);
        System.out.println(Arrays.toString(a));

        //给数组赋值
        Arrays.fill(a, 7, 8, 5);
        System.out.println(Arrays.toString(a));

        //对排序好的数组进行二分查找法操作(没看懂,有兴趣可以再研究一下)
        System.out.println(Arrays.binarySearch(a,9886));
    }
}

11.6 冒泡排序

  • 冒泡排序无疑是最为出名的排序算法之一,总共有八大排序

    冒泡排序
  • 冒泡排序的代码还是相当简单的,两层循环,外层冒泡轮数,里层一次比较,江湖中人尽皆知。

  • 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2)

  • 思考:如何优化?

public class Array_05_Sort {
    public static void main(String[] args) {
        int[] a = {2,5,67,9886,2345,7860,37456,2,56,21};
        //冒泡排序
        System.out.println(Arrays.toString(sort(a)));

        //Arrays工具类排序
        Arrays.sort(a);
        System.out.println(Arrays.toString(a));
    }

    /**
     * 冒泡排序
     * 1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,就交换他们的位置
     * 2.每一次比较,都会产生一个最大或者最小的数字
     * 3.下一轮,可以少一次排序
     * 4.依次循环直到结束
     * @param array
     * @return int[]
     */
    private static int[] sort(int[] array) {
        //临时变量
        int trans;

        //外层循环,判断我们这个要走多少次
        for (int i = 0; i < array.length-1; i++) {
            //内层循环,比较两个数,如果第一个数比第二个数大,则交换位置
            boolean flag = true;
            for(int j = 0; j < array.length-1-i; j++){
                if(array[j] > array[j+1]) {
                    trans = array[j];
                    array[j] = array[j+1];
                    array[j+1] = trans;
                    flag = false;
                }
            }
            //如果不产生交互,说明已经排序成功,提前结束循环,优化程序质量
            if(flag) {
                break;
            }
        }

        return array;
    }
}

11.7 稀疏数组【二维数组】

  • 需求:编写五子棋游戏中,有存盘退出和续上盘的功能。

    五子棋游戏
  • 分析解决:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据。

  • 解决:稀疏数组(压缩算法)


稀疏数组介绍:

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

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

    • 记录数组一共有几行几列,有多少个不同值【6行 7列 8个值】
    • 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
  • 如下图:左边是原始数组,右边是稀疏数组
    稀疏数组 举例

    /**
     * 稀疏数组
     *  相互转化:
     *      稀疏数组 --> 棋盘【printBoard】
     *      棋盘 --> 稀疏数组【printSparseArray】
     */
    public class Arrays_07_Sparse {
        public static void main(String[] args) {
            //1.创建一个二维数组 11*11
            // 0:没有棋子
            // 1:黑棋
            // 2:白棋
    
            //输入稀疏数组
            int[][] array = new int[][] {{11,11,2},{1,2,1},{2,3,2}};
            //获取棋盘
            String[][] board = printBoard(array);
            //打印棋盘
            for (String[] ints : board) System.out.println(Arrays.toString(ints));
            //转换成稀疏数组
            int[][] sparseArray = printSparseArray(board);
            //打印稀疏数组
            for (int[] ints : sparseArray) System.out.println(Arrays.toString(ints));
    
            System.out.println("================== 分界线 ===================");
            //输入稀疏数组
            array = new int[][] {{6,7,8},{0,3,22},{0,6,15},{1,1,11},{1,5,17},{2,3,-6},{3,5,39},{4,0,91},{5,2,28}};
            //获取棋盘
            board = printBoard(array);
            //打印棋盘
            for (String[] ints : board) System.out.println(Arrays.toString(ints));
            //转换成稀疏数组
            sparseArray = printSparseArray(board);
            //打印稀疏数组
            for (int[] ints : sparseArray) System.out.println(Arrays.toString(ints));
        }
    
        /**
         * 稀疏数组 练习
         * 稀疏数组 --> 棋盘
         * @param array
         * @return String[][]
         */
        public static String[][] printBoard(int[][] array){
            //创建棋盘
            String[][] board = new String[array[0][0]][array[0][1]];
    
            //赋值
            for (String[] line : board) {
                Arrays.fill(line, " 0");
            }
            for(int i = 1; i < array.length; i++){
                Arrays.fill(board[array[i][0]],array[i][1],array[i][1]+1,_04_LoopStruct.setThreeDigitNumber(array[i][2],2));
            }
            return board;
        }
    
        /**
         * 稀疏数组 练习
         * 棋盘 --> 稀疏数组
         * @param boards
         * @return int[][]
         */
        public static int[][] printSparseArray(String[][] boards){
            //获取棋盘的 行
            int line = boards.length;
            //获取棋盘的 列
            int arrange = boards[0].length;
    
            //获取棋盘的 数据总数
            int sum = 0;
            for (String[] board : boards) {
                for (String chess : board) {
                    if(!"0".equals(chess.trim())){
                        sum++;
                    }
                }
            }
    
            //创建稀疏数组
            int[][] array = new int[sum+1][3];
            //赋值
            array[0]= new int[]{line, arrange, sum};
            sum = 0;//初始化
            //遍历二维数组,将非零的值,存放在稀疏数组中
            for (int i=0;i<boards.length;i++){
                for (int j=0;j<boards[i].length;j++){
                    if(!"0".equals(boards[i][j].trim())){
                        sum++;
                        array[sum][0]=i;
                        array[sum][1]=j;
                        array[sum][2]=Integer.parseInt(boards[i][j].trim());
                    }
                }
            }
            return array;
        }
    }
    

12. 面向对象编程【OOP】

  • 初识面向对象
  • 方法的回顾及加深
  • 对象的创建分析
  • 面向对象三大特征【重点】【封装、继承、多态】
  • 抽象类和接口【企业级】【抽象是一种思维】
  • 内部类及OOP实战

12.1 初识面向对象

12.1.1 面向过程 & 面向对象

  • 面向过程思想

    • 步骤清晰简单,第一步做什么,第二步做什么……
    • 面对过程适合处理一些较为简单的问题
  • 面向对象思想

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

12.1.2 什么是面向对象

  • 面向对象编程(OOP)

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

  • 抽象【编程思想!持续的学习,茅塞顿开!多实践、多测试大脑中的想法!实践出真知~】

  • 三大特性:

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

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

12.2 方法的回顾及加深

  • 方法的定义

    • 修饰符
    • 返回类型
    • break(跳出switch,结束循环) 和 return(结束方法) 的区别
    • 方法名:注意规范、见名知意
    • 参数列表:(参数类型 参数名)…可变参数
    • 异常抛出:疑问,后面讲解
  • 方法的调用(之前:递归)

    • 静态方法【static】
    • 非静态方法【没有static】
    • 形参和实参
    • 值传递和引用传递
    • this 关键字
/**
 * 方法的调用(之前:递归)
 *      1.静态方法【static】
 *      2.非静态方法【没有static】
 *      3.形参和实参
 *      4.值传递 和 引用传递(传递对象,本质:值传递)       加深理解:对象 和 内存 !
 *      5. this 关键字(集成和多态的时候讲)
 */
public class Oop_01 {
    public static void main(String[] args) {
        //1.静态方法
        System.out.println("--------------- 静态方法 ----------------");
        Student.say();

        //2.非静态方法
        //实例化这个类 new
        //对象类型  对象名 = 对象值
        System.out.println("--------------- 非静态方法 ----------------");
        Student student = new Student();
        student.eat();

        //3.实参(实际参数和形式参数的类型要一一对应)
        System.out.println("--------------- 形参和实参 ----------------");
        System.out.println(add(1,2));

        //4.值传递和引用传递
        System.out.println("--------------- 值传递和引用传递 ----------------");
        //值传递
        int a = 1;
        System.out.println(a);//1
        change(a);
        System.out.println(a);//1
        //引用传递:传递对象     本质:值传递
        Student student1 = new Student();
        System.out.println(student1.name);//null
        change(student1);
        System.out.println(student1.name);//小李

    }

    /**
     * 加法运算     3.实参
     * @param a
     * @param b
     * @return
     */
    public static int add(int a, int b) {
        return a + b;
    }

    /**
     * 返回值为空,
     * 不影响实参,赋值只是赋给了形参的 a
     * @param a
     */
    public static void change(int a) {
        a= 10;
    }
    public static void change(Student student) {
        //Student 是一个对象
        //Student student1 = new Student();
        //student1 是一个具体的学生,所以可以改变属性
        student.name= "小李";
    }
}

12.3 对象的创建分析

12.3.1 类与对象的关系

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

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

    • 张三 就是 人 的一个具体实例;张三家的旺财 就是 狗 的一个具体实例。
    • 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。

12.3.2 创建与初始化对象

  • 使用 new 关键字创建对象

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

  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:

    1. 必须和类的名字相同
    2. 必须没有返回类型,也不能写 void
  • 构造器必须掌握

//1.使用 new 关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象 进行默认的初始化
public static void main(String[] args) {
    //类 是 抽象的,需要实例化
    Student student = new Student();
    student.sleep();//String 默认初始化为 null
    student.name = "小李";
    student.sleep();

}
//学程序好?对世界进行更好的建模!

//System.out.println("=====================分界线====================");

//2.对类中构造器的调用
public static void main(String[] args) {
    //使用构造器 创建对象
    Student student = new Student("小新新");
    student.sleep();

    student.name = "小李";
    student.sleep();
}
/**
 * 学生类
 * 类:属性 + 方法
 */
public class Student {
    /**
     * 属性:字段
     */
    public String name;
    public int age;

    /**
     * alt + insert
     * 构造器
     * 一个类:即使什么都不写,也会存在一个无参构造器
     *
     * 1.使用 new 关键字,本质是在调用构造器
     * 2.用来初始化值
     */
    public Student(){
        //无参
    }

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

    public Student(String name, int age){
        //有参【重载】
        //一旦定义了有参构造,无参构造就必须显式定义
        this.name = name;
        this.age = age;
    }

    /**
     * 方法
     */
    //和类一起加载
    public static void say(){
        System.out.println("学生说话了。。。。。。");

//        eat();//【报错】无法从 static 上下文引用非 static 方法 'eat()'
    }

    //类实例化之后才存在
    public void eat(){
        System.out.println("学生吃饭了。。。。。。");
    }

    public void sleep(){
        System.out.println(this.name + "在睡觉");
    }

}

12.3.3 内存分析

Pet 内存分析

//Pet 类
public class Pet {
    public String name;
    public int age;

    //无参构造

    public void shut(){
        System.out.println("叫了一声");
    }
}

//启动类
public class Application {
    public static void main(String[] args) {
        Pet dog = new Pet();
        dog.name = "旺财";
        dog.age = 3;
        dog.shut();

        System.out.println(dog.name);
        System.out.println(dog.age);

        Pet cat = new Pet();
    }
}

12.3.4 总结

/**
     * 总结:
     *  1.类与对象
     *      类   是一个模板,是抽象的
     *      对象 是一个具体的实例
     *  2.方法
     *      定义、调用!
     *
     *  3.对象的引用
     *      引用类型:类 接口 数组
     *      对象是通过引用来操作的:栈 ---> 堆
     *
     *  4.属性:字段 Field 成员变量
     *      默认初始化:
     *          数字:     0    0.0
     *          char:     u0000
     *          boolean:  flase
     *          引用:     null
     *      格式:
     *          修饰符  属性类型  属性名 = 属性值
     *  5.方法:注意命名规范,不要死循环
     *
     *  6.对象的创建和使用
     *      必须使用 new 关键字创建对象【需要构造器】     Pet dog = new Pet();
     *      对象的属性   dog.name
     *      对象的方法   dog.shut()
     *
     *  7.类:
     *      静态的属性   属性
     *      动态的行为   方法
     */

12.4 面向对象三大特征【重点】【封装、继承、多态】

12.4.1 封装

  • 该露的露,该藏的藏
    • 我们程序设计要追求 “高内聚,低耦合”
      • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
      • 低耦合:仅暴露少量的方法给外部使用。
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
  • 记住这句话就够了:属性私有,get / set
/**
 * 1.提高程序的安全性,保护数据
 * 2.隐藏代码的实现细节
 * 3.统一接口 get/set
 * 4.增加了系统的可维护性
 * @param args
 */
public static void main(String[] args) {
    Student_03 student = new Student_03();
    student.setName("小李");
    System.out.println(student.getName());
    student.setAge(-1);//封装了初始化判断
    System.out.println(student.getAge());
}
/**
 * 类
 * private 私有
 */
public class Student_03 {

    //属性私有
    private String name;//名字
    private int id;//学号
    private char sex;//性别
    private int age;

    //提供一些可以操作这个属性的方法
    //提供一些 public 的get、set方法
    //快捷键: alt + insert

    //get 获得这个数据
    public String getName() {
        return name;
    }

    //set 给这个数据设置值
    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;
    }

    public void setAge(int age) {
        this.age = age;
        if(age<0 || age>120){
            this.age = 0;
            System.out.println("年龄输入不合法!");
        }
    }
}

12.4.2 继承

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

  • extends 的意思是 “扩展”。子类是父类的扩展。

  • Java 中类只有单继承,没有多继承!(一个儿子只能有一个爸爸,一个爸爸可以有多个儿子)

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

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

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

  • object 类

  • super - this

  • 方法重写【重点】---> 多态

/**
 * super 注意点:
 *      1.super 调用父类的构造器方法,必须在子类构造方法的第一行
 *      2.super 必须只能出现在子类的方法或者构造方法中!
 *      3.super 和 this 不能同时调用构造方法!
 *
 * this 和 super 的区别:
 *      1.代表的对象不同
 *          this:本身调用者的这个对象
 *          super:代表父类对象的引用
 *      2.前提
 *          this:没有继承也可以使用
 *          super:只有在继承条件下才能使用
 *      3.调用构造方法
 *          this:调用本类的构造
 *          super:调用父类的构造
 *
 *  重写:需要有继承关系,子类重写父类的方法!
 *      1.方法名必须相同
 *      2.参数列表必须相同
 *      3.修饰符:范围可以扩大,但不能缩小  public > protected > default > private
 *      4.抛出异常:范围可以被缩小,但不能扩大  ClassNotFoundException < Exception
 *  总结:
 *      重写:子类的方法和父类必须一致,但方法体不同!
 *      为什么需要重写?
 *          父类的功能,子类不一定 需要/满足
 *          alt + insert:override
 */
public static void main(String[] args) {
    //1.Object 类
    System.out.println("========= Object 类 =========");
    Person person = new Person();
    //所有类都继承 Object 类
    // getClass() 是 Object 类的方法
    System.out.println(person.getClass());

    //2.super
    System.out.println("========= 继承 =========");
    Student_04 student_04 = new Student_04();
    student_04.say();//公有继承
    //System.out.println(student_04.money);//私有不能继承
    System.out.println(student_04.getMoney());//通过开放的公共接口来继承

    System.out.println("========= super =========");
    student_04.test("小李");
    student_04.test1();

    //3.重写
    System.out.println("========= 重写 =========");
    /**
     * 静态和非静态的方法区别很大!
     *      静态方法:方法的调用 只和左边定义的数据类型有关
     *      非静态方法:方法的重写
     */
    student_04.run();
    //父类的引用指向了子类
    Person p = new Student_04();//子类重写了父类的方法
    p.run();
}
//在 Java 中,所有的类,都默认直接或者间接继承 Object 类
//Person  人:父类
//查看继承关系树:ctrl + h
//private 私有的 属性/方法 无法被继承
//重写都是方法的重写,和属性无关
public class Person /*extends Object*/{
    //public
    //protected
    //default
    //private   私有的 子类无法继承
    private int money = 10_0000_0000;
    protected String name = "Person";
    Person(){
        System.out.println("Person 的无参构造 执行了");
    }

    Person(String name){
        System.out.println("Person 的有参构造 执行了");
    }

    public void run(){
        System.out.println("Person => run()");
    }

    public void say(){
        System.out.println("说了一句话");
    }

    public void print(){
        System.out.println("输出 Person");
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}
//Teacher is 人:派生类,子类
public class Teacher extends Person {
}
//Student_04 is 人:派生类,子类
//子类继承父类,就会拥有父类的全部方法!
public class Student_04 extends Person {
    private String name = "Student";

    Student_04(){
        //调用父类的构造器,必须要在子类构造器的第一行
        super();//隐藏代码:调用了父类的无参构造器
        System.out.println("Student_04 的无参构造 执行了");
    }

    @Override
    public void run() {
        System.out.println("Student_04 => run()");
    }

    public void test(String name){
        System.out.println(name);
        System.out.println(this.name);
        System.out.println(super.name);//子类调用父类属性
    }

    public void test1(){
        print();
        this.print();
        super.print();//子类调用父类方法
    }

    public void print(){
        System.out.println("输出 Student_04");
    }
}

12.4.3 多态

  • 动态编译:让类型的可扩展性变得更强

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

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

  • 多态存在的条件

    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象
  • 注意:多态的方法的多态,属性没有多态性

  • instanceof 类型转换 -- 引用类型直接的转换【判断一个对象是什么类型】

/**
 * 多态注意事项:
 *      1.多态是方法的多态,属性没有多态
 *      2.有父子关系才能转换,不能随意转换【类型转换异常 - ClassCastException】
 *      3.存在条件:
 *          (1)继承关系
 *          (2)方法需要重写
 *          (3)父类引用指向子类对象 Father f = new Son();
 *
 *  不能被重写:
 *      1.static 修饰的方法,属于类,不属于实例
 *      2.final 修饰的类
 *      3.private 修饰的方法
 *  多态:
 *      1.父类引用指向子类的对象
 *      2.把子类转换为父类,向上转型
 *      3.把父类转换为子类,向下转型【强制转换】
 *      4。方便方法的调用,减少重复的代码!简洁
 *
 *   抽象     【面向对象】封装、继承、多态
 *            抽象类、接口
 */
public static void main(String[] args) {
    //一个对象的实际类型是确定的
    //new Person();
    //new Student_05();

    //可以指向的引用类型就不确定了:父类的引用指向子类
    Student_05 ss1 = new Student_05();//子类指向的引用
    Person_05 ps2 = new Student_05();//父类指向的引用
    Object os3 = new Student_05();

    //子类能调用的方法:都是自己的或者继承父类的!
    //父类型虽然可以引用子类,但是不能执行子类独有的方法
    ps2.jump();//【父类引用 调用父类方法】
    ps2.run();//【父类引用 调用父类方法,被子类重写】子类重写父类的方法,按照子类的执行
    ss1.run();//【子类调用子类方法】

    //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
    //高到低,强转
    ((Student_05) ps2).eat();//【父类引用 调用子类方法】需要强转为子类再调用方法
}
System.out.println("======================= 3.多态 instanceof =======================");
public static void main(String[] args) {

    /**
     * System.out.println(x instanceof y);能不能编译通过【x y是否存在父子关系】  接口
     */

    //Object > String
    //Object > Person_05 > Teacher_05
    //Object > Person_05 > Student_05
    System.out.println("============= Object 引用 Student_05 ==============");
    Object object = new Student_05();
    System.out.println(object instanceof Student_05);//true
    System.out.println(object instanceof Person_05);//true
    System.out.println(object instanceof Object);//true
    System.out.println(object instanceof String);//false
    System.out.println(object instanceof Teacher_05);//false

    System.out.println("============= Person_05 引用 Student_05 ==============");
    Person_05 person = new Student_05();
    System.out.println(person instanceof Student_05);//true
    System.out.println(person instanceof Person_05);//true
    System.out.println(person instanceof Object);//true
    //System.out.println(person instanceof String);//编译报错【二者没有关系】
    System.out.println(person instanceof Teacher_05);//false

    System.out.println("============= Student_05 ==============");
    Student_05 student = new Student_05();
    System.out.println(student instanceof Student_05);//true
    System.out.println(student instanceof Person_05);//true
    System.out.println(student instanceof Object);//true
    //System.out.println(student instanceof String);//编译报错【二者没有关系】
    //System.out.println(student instanceof Teacher_05);//编译报错【二者没有关系】

    System.out.println("============= 类型转化 ==============");
    //类型直接的转化:基本数据类型    高 低   【高转低需要强转,低转高则不需要】
    //父【高】   子【低】
    //子类转换为父类,可能丢失自己本来的一些方法!
    Person_05 person1 = new Student_05();//高转低,直接转化
    //person1 将这个对象转化为 Student_05 类型,就可以使用 Student_05 类型的方法了
    ((Student_05) person1).eat();//低转高,强转
}
public class Student_05 extends Person_05{
    @Override
    public void run() {
        System.out.println("son --> run!");
    }

    public void eat() {
        System.out.println("son --> eat!");
    }
}
public class Person_05 {
    public void jump(){
        System.out.println("father --> jump!");
    }

    public void run(){
        System.out.println("father --> run!");
    }
}

12.4.4 static 关键词

/**
 * static
 *      1.静态代码块
 *      2.匿名代码块
 */
public class Student_06 {

    private static int age;//静态的变量  多线程!
    private int score;//非静态的变量

    //2【赋初始值】
    {
        //代码块(匿名代码块)
        //创建对象的时候调用,在构造器之前
        //不建议使用
        System.out.println("匿名代码块");
    }

    //1【只执行一次】
    static{
        //静态代码块
        //类加载的时候就执行了,永久执行一次
        //初始化时使用
        System.out.println("静态代码块");
    }

    //3
    Student_06(){
        System.out.println("无参构造器");
    }

    Student_06(int age, int score){
        this.age = age;
        this.score = score;
        System.out.println("有参构造器");
    }

    public void run(){
        //普通方法
    }

    public static void go(){
        //静态方法
    }

    public static void main(String[] args) {
        Student_06 st = new Student_06();
        System.out.println(st.age);
        System.out.println(st.score);
        System.out.println(Student_06.age);//直接调用

        go();//直接调用
        Student_06.go();
        st.run();

        System.out.println("======== 第二次实例化对象 静态代码块不执行 ============");
        Student_06 st2 = new Student_06();
    }
}
package com._02_oop;

import static java.lang.Math.PI;
import static java.lang.Math.random;

/**
 * 静态导入包
 * 虽然不常用,但是常看开发文档,一切皆有可能!
 * import static
 */
public class Oop_04 {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);

        System.out.println(random());
        System.out.println(PI);
    }
}

12.5 抽象类和接口

12.5.1 抽象类

  • abstract 修饰符可以用来修饰方法也可以用来修饰类。如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类

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

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

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

  • 子类继承抽象类,那么就必须要实现抽象类没有实现的方法,否则该类也要声明为抽象类。

/**
 * 抽象类
 */
public class Oop_05 {
    public static void main(String[] args) {
        /**
         * abstract 抽象类【约束】
         * 类: 继承  extends  单继承
         * (接口可以多继承)
         *
         * 特点:【抽象的抽象 -- 约束】
         *      1.不能 new 抽象类,只能靠子类去实现它【只是一种约束】
         *      2.抽象类中可以写普通方法
         *      3.抽象方法必须在抽象类中
         *
         * 思考:
         *  1.抽象类既然不能 new 对象,那它存在构造器吗【存在,可以初始化】
         *  2.抽象类存在的意义【公有属性抽象出来,提高开发效率,代码的可扩展性很高】
         */
        new A();
    }
}
public abstract class Action {

    Action(){
        System.out.println("无参构造器");
    }

    //约束【有人帮我们实现】
    //抽象方法【只有方法的约束,没有具体的方法实现】
    public abstract void doSomething();

    public void run(){

    }
}
/**
 * 抽象类的子类
 *
 * 1.抽象类的所有方法,继承了它的子类都必须进行实现
 * 2.除非它的子类也是抽象类,可以由子子类来实现
 */
public class A extends Action{

    @Override
    public void doSomething() {

    }
}

12.5.2 接口

  • 普通类:只有具体实现

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

  • 接口:只有规范!【自己无法写方法】【专业级抽象(约束和实现分离),比抽象类还抽象】

  • 接口就是规范,定义的是一组规则,体现了现实世界中 “如果你是…则必须能…” 的思想。如果你是天使,则必须能飞;如果你是骑车,则必须能跑;如果你是好人,则必须干掉坏人。

  • 接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。

  • OO 的精髓,是对 对象 的抽象,最能体现这一点的就是接口。为什么我们讨论 设计模式 都只针对具备了抽象能力的语言(比如c++、Java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

public static void main(String[] args) {
    /**
     * 接口:
     *   1.约束
     *   2.定义一些方法,让不同的人实现
     *   3.接口中的所有定义的属性都是静态的常量   public static final
     *   4.接口中的所有定义的方法都是抽象的  public abstract
     *   5.接口不能被实例化,接口中没有构造方法
     *   6.implements 可以实现多个接口
     *   7.必须要重写接口里的方法
     */

    UserServiceImpl userService = new UserServiceImpl();
    userService.add("");
}
/**
 * 接口   interface   【接口都需要实现类】
 *
 * 锻炼抽象的思维 ~~ 架构师
 */
public interface UserService {
    //接口中的所有定义的属性都是静态的常量 public static final
    public static final int age = 0;

    //接口中的所有定义的方法都是抽象的 public abstract
    public abstract void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);

}

public interface TimeService {
    void timer();
}
/**
 * 类 可以实现接口   implements  多继承
 * 抽象类  extends 单继承
 *
 * Java 是单继承的,可以利用接口实现多继承
 */
public class UserServiceImpl implements UserService ,TimeService{

    @Override
    public void add(String name) {
        System.out.println("add");
    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}

12.6 内部类

  • 内部类就是在一个类的内部再定义一个类,比如,A类 中定义一个 B类,那么 B类 相对 A类 来说就称为内部类,而 A类 相对 B类 来说就是外部类了
  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类
  5. lambda表达式【进阶,之后学习】
public static void main(String[] args) {
    Outer outer = new Outer();
    outer.out();
    outer.new Inner().in();//通过这个外部类实例化内部类

    //匿名内部类【没有名字初始化类,不用将实例保存到变量中】
    new AA().eat();
    //匿名内部类,重写实现的方法
    new TimeService(){
        @Override
        public void timer() {

        }
    };
}
//外部类
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);
        }
    }

    //静态内部类
    public static class Inner2 {
        public void in() {
            System.out.println("这是 静态内部类 的方法");
        }

        //获得外部类的私有属性~
        public void getId() {
            //System.out.println(id);//不能获得非静态的属性
        }
    }

    //class 可以写在方法里
    //局部内部类
    public void method(){
        class Inner2{
            public void in() {
                System.out.println("这是 局部内部类 的方法");
            }
        }
    }
}

//一个Java类中只能有一个 public class,但是可以有很多 class
class AA {
    public void eat() {
        System.out.println("AA ---> eat()");
    }
}

13. 异常机制【Exception】

  • 什么是异常
  • 异常体系结构
  • Java 异常处理机制
  • 处理异常
  • 自定义异常
  • 总结

13.1 什么是异常

13.1.1 什么是异常?

  • 实际工作中,遇到的情况不可能的非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的等;我们的程序在跑着内存或硬盘满了…等等。

  • 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理,而不至于程序崩溃。

  • 异常指程序运行中出现的不期而至的各种情况,如:文件找不到、网络连接失败、非法参数等。

  • 异常发生在程序运行期间,它影响了正常的程序执行流程。

13.1.2 异常的简单分类:

  • 要理解 Java 异常处理是如何工作的,你需要掌握一下三种类型的异常:【异常处理框架】
    • 检查性异常:最具代表的检查性异常是用户输入错误或问题引起的异常,这是程序员无法预见的。例如:要打开一个不存在的文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略。【测试岗位】
    • 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
    • 错误(ERROR):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如:当栈溢出时,一个错误就发生了,他们在编译时也检查不到

13.2 异常体系结构

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

13.3 Java 异常处理机制

  • 抛出异常

  • 捕获异常

  • 异常处理的五个关键字

    • try、catch、finally、throw、throws
/**
 * Java 异常处理机制
 */
public class Exception_01 {

    public static void main(String[] args) throws Exception {
        /**
         * 假设要捕获多个异常:从小到大!
         * 快捷键:ctrl + alt + t
         */
        int a = 1;
        int b = 0;

        //主动抛出异常
        new Exception_01().divide(a, b);


        //异常抛出,正常向下执行
        try {
            //try 监控区间
            System.out.println(a/b);
            //new Exception_01().a();
        }catch (Exception e) {
            //catch 捕获异常
            e.printStackTrace();//打印错误的栈信息
            //System.exit(0);//手动结束程序
            System.out.println("异常抛出:Exception");
        }catch (Error e) {
            //catch 捕获异常
            System.out.println("异常抛出:Error");
        }catch (Throwable e){
            System.out.println("最高级异常抛出");
        }finally{
            //处理善后工作【可有可无】 如有:I/O流、Scanner 这种需要关闭资源的,在 finally 里关闭资源
            //无论是否捕捉到异常,finally 代码块一定会被执行
            System.out.println("finally");
        }

        System.out.println("继续执行余下代码!");

    }

    public void a(){
        b();
    }

    public void b(){
        a();
    }

    public void divide(int a,int b) throws Exception {
        //主动抛出异常
        if(b == 0){
            throw new Exception("算数异常,请检查!");
        }
        System.out.println(a/b);
    }
}

13.4 自定义异常

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

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

    1. 创建自定义异常类;
    2. 在方法中通过 throw 关键字抛出异常对象;
    3. 如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕捉并处理;否则在方法的声明处通过 throws 关键字指明要抛出给方法调用者的异常,继续进行下一步操作;
    4. 在出现异常方法的调用者中捕捉并处理异常
/**
 * 使用自定义异常类
 */
public class Exception_02 {

    public static void main(String[] args) {
        try {
            new Exception_02().judge(15);
        } catch (Exception e) {
            //增加一些处理异常的代码
            System.out.println("Exception_02 => " + e);
        }
    }

    /**
     * 传递数字,如果数字 > 10,就抛出自定义异常
     * @param i
     * @throws Exception
     */
    private void judge(int i) throws Exception {
        if(i>10){
            throw new MyException(i);//抛出
        }
        System.out.println("OK");
    }
}
/**
 * 自定义异常类
 */
public class MyException extends Exception {
    private final int detail;

    public MyException(int detail) {
        this.detail = detail;
    }

    @Override
    public String toString() {
        //异常的打印信息
        return "MyException {" + detail + "}";
    }
}

13.5 实际应用中的经验总结

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