Java语言基础编程

yzdev
2023-05-09 / 0 评论 / 82 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年05月09日,已超过507天没有更新,若内容或图片失效,请留言反馈。
本文共 14202 个字数,平均阅读时长 ≈ 36分钟

Java语言基础编程

一. Java语言概述

01. Java的相关概念以及特点

  • 相关概念:

    • 95年sun公司推出JAVA
    • 09年oracle收购sun公司
    • JAVA之父: 詹姆斯.高斯林
  • 技术方向:

    • JAVASE

      • 桌面应用程序
    • JAVAEE

      • 企业级的应用程序
    • JAVAME

      • 手机上的应用程序
  • java开发步骤:

    • 编写源文件后缀名.java
    • 编译后缀名.class
    • 运行
  • 特点:

    • 面向对象
    • 简单
    • 安全
    • 多线程
    • 跨平台
    • 分布式
    • 自动垃圾回收

02. JAVA的搭建环境

  • 几个环境方面的概念:

    • JDK

      • java开发工具包
      • 包含:JRE+工具包
    • JRE

      • JAVA运行环境
      • 包含:JVM + 类库
    • JVM

      • java 虚拟机
      • 执行class文件时,根据不同操作系统,解释成不同平台下的可执行文件,再去执行,所以说jvm真正让java实现跨平台性
  • 步骤:

    • 1、下载并安装JDK

    • 注意:安装目录下不要有中文或空格

      • jdk下的目录结构

      • bin工具包

        • src源码包
        • jre
        • lib类库
  • 2、配置环境变量:

    • JAVA_HOME
    • JAVA_HOME
    • 指jdk安装目录
    • 值:如c:\jdk
    • path
    • 指编译、运行命令的目录
    • 值:如 c:\jdk\bin

03. 开发第一个JAVA程序

  • java代码框架

    class 类名{
        public static void main(String[] args){
        }
    }
    
    • class 类名{主方法}

    • 主方法是程序的入口

      • 主方法的签名:public static void main(String[] args){ }
      • public:公共的,在任意位置可见
      • static:静态的,不需要实例
      • void:方法执行完成不需要返回值
      • main:方法名
      • String[]:表示可以给main方法传递参数,而且可以传一个字符串数组的参数
      • args:形参名,在main()中如果要使用外界传的字符串数组的参数值,可以通过args取出
  • Java程序开发的步骤:

    • 1、编辑:编写Java代码

      • 保存成:.java源文件
    • 2、编译:转成字节码文件

      • 得到的结果是:一个或多个的.class字节码文件
      • javac 源文件名.java
    • 3、运行:运行字节码文件

    • java 类名

  • java代码注意事项:

    • 1、严格区分大小写
    • 2、所有符号都是英文状态
    • 3、括号都是成对出现的
    • 4、每一条命令结尾 是分号
    • 5、类名和文件名要一致
    • 6、类名 要遵循标识符的命名规则和命名规范
    • 7、一个良好的java代码,要有注释、要注意缩进

04. JAVA的注释和API文档

  • Java中的注释:

    • 1、单行注释

      • //
    • 2、多行注释

      • /* */

        • 避免嵌套
    • 3、文档注释

      • /** */

        • 结合注解
        • 使用javadoc.exe
  • API文档

    • 应用程序编程接口文档,其实就是帮助文档,方便开发时查阅

05. 常用DOS命令

  • javac:编译
  • java:运行
  • cd:切换目录
  • cd..:退回上一级
  • cd\:退回根目录
  • md:新建目录
  • rd:删除目录
  • echo:新建或编辑文件
  • exit:退出

二. 基本语法

标识符

  • 标识符的命名规则

    • 1、可以有大小写的英文字母,0-9的阿拉伯数字,下划线_,美元符号$
    • 2、数字不能开头
    • 3、不能使用Java的50个关键字和保留字,3个特殊值
    • 4、Java严格区分大小写
    • 5、不能使用空格
  • 标识符的命名规范

    • 包:所有字母都小写

      • 形式:xxx.yyy.zzz
      • 例如:com.atguigu.bean
    • 类名/接口名:所有单词的首字母大写

      • 形式:XxxYyyZzz
      • 例如:HelloWorld
    • 变量名/方法名:第一个单词的首字母小写,其余单词的首字母大写

      • 形式:xxxYyyZzz
      • 例如:age, studentId, sName, getArea
    • 常量名:所有字母都大写,每个单词使用_连接

      • 形式:XXX_YYY_ZZZ
      • 例如:MAX_VALUE

变量

  • 概念

    • 代码一块内存空间,使用变量名来访问和使用这块内存空间
  • 变量的特点和作用

    • 概念:一块内存中的数据存储空间的表示

    • 特点:

      • 里面的内容可以更改

      • 一个变量只能存一个值

      • 变量的三要素

        • 数据类型
        • 变量名
    • 作用

      • 保存数据
  • 如何声明和使用

    • 声明的格式:

      • 数据类型 变量名;
    • 变量的赋值

      • 变量名 = 值;

        • 其中的值是(常量值,变量值,表达式)
    • 使用变量直接通过变量名就可以使用

  • 该注意什么

    • 1、先声明后使用
    • 2、变量要有初始值
    • 3、变量有作用域

数据类型

  • 基本数据类型

    • 整型

      • byte

        • 1个字节
        • 数据的范围:-128~127
      • short

        • 2个字节
        • 数据的范围:-32768~32767
      • int

        • 4个字节
        • 数据的范围:
      • long

        • 8个字节
        • 注意:在数字后面加L
    • 浮点型

      • float

        • 4个字节
      • 小数的十进制科学计数法,大概小数点后6~7位

    • double

      • 8个字节
      • 小数的十进制科学计数法,大概小数点后15~16位
    • 字符型

      • char

        • 2个字节

        • 因为是2个字节,所以一个中文汉字,也可以表示一个字符 char c = '中';

          • Java中使用Unicode值来表示每一个字符

          • 至于这个Unicode值在底层怎么存,这个要和与具体的编码方式相结合

            • 编码方式有:GBK,UTF-8,ISO8859-1等
      • 注意:

        • (1)字符常量需要使用单引号' ',单引号中有且只能有一个字符

          • 如果有多个字符,就需要使用" ",变成字符串类型,就是String类型

            • 字符型 char c = 'a';
            • char c ='';(错误)
          • 字符串类型 String str = "hello";

            • 双引号中,需要0~n个字符

              • String str = "";
              • 空字符串
        • (2)字符还可以转义字符

          • 无法用一个字符表示的符号

            • 回车

              • \r
            • 换行

              • \n
            • 退格

              • \b
            • 制表位

              • \t
          • 在Java中有特殊意义的标点符号

            • 单引号

              • '
            • 双引号

              • "
            • 斜杠本身

              • \
        • (3)字符的表示可以使用Unicode值

          • \u000a
    • 布尔型

      • boolean

        • 只有两个值:true和false
  • 引用数据类型

      • String,System
    • 接口

    • 数组

  • 数据类型的转换

    • 基本数据类型的转换

      • 自动类型转换

        • 存储容量小的值或变量赋值给存储容量大的,或者存储容量小的与存储容量大的进行混合运算,出现存储容量小的会自动转成存储容量大的类型

        • byte,short,char --> int --> long -->float -->double

          • 因为 float 和 double 类型的存储方式不一样,因此8个字节的long可以转成float而不会溢出
          • float 和 double 存储是分为:符号位,指数部分,尾数部分
      • 强制类型转换

        • 把存储容量大的常量值/变量/表达式的值赋值给存储容量小的变量时
        • 需要使用强制类型转换符()
        • 强制类型转换有风险:溢出,损失精度

运算符

  • 算术运算符

    • 正号

      • +
    • int a = 10;

  • 负号

    • -

      • int b = -a;
  • 加法

    • +

      • int c = a + b;
  • 减法

    • -

      • int c = a - b;
  • 乘法

    • *
  • 除法

    • /

      • 特殊:

        • 两个整数相除,只保留整数部分

          • 两个整数相除时,除数不能为0,否则报Exception in thread "main" java.lang.ArithmeticException: / by zero
        • 两个浮点数相除时,除数为0的话,结果是Infinity

  • 取模

    • %

      • 求余数

        • 特殊:

          • 模数的负号会被忽略
  • 自增

    • ++

      • 运算的规则:

        • 1、对于自增变量本身来说,无论++在前还是在后,自增变量本身都会自增1

        • 2、但是对于自增表达式来说,++在前还是在后的结果是不同 - a++

          • 这个表达式的值与a自增之前的值相同
          • ++a
          • 这个表达式的值与a自增之后的值相同
    • 自减 - 类似于自增

    •  

  • 逻辑运算符

    • 逻辑与

      • &

        • true & true才为true,其他的都为false
    • 逻辑或

      • |
    • 左右两边有一个为true,结果就为true

  • 逻辑非

    • !

      • !true为false , !false为true
  • 逻辑异或

    • ^

      • 左右两边必须是一个为true,一个为false,结果采为true,否则为false
  • 短路与

    • &&

      • 如果&&的左边的值为false,右边的就不看了,结果直接为false
  • 短路或

    • ||

      • 如果||的左边的值为true,右边的就不看了,结果直接为true
    • 注意:

      • 逻辑运算符的操作数,必须是布尔值
  • 赋值运算符

    • 基本赋值运算符

      • =

        • 表示把=右边的值赋值给左边的变量
    • 扩展赋值运算符

      • +=

        • 把左边的变量的值,与右边整个表达式的值相加,最后赋值给左边的变量
        • 如果左右两边的类型不一致,会隐含有类型转换
      • -=

      • *=

      • /=

      • %=

  • 比较运算符

    • 大于

      •  

    • 小于

      • <
    • 大于等于

      • =

    • 小于等于

      • <=
    • 等于

      • ==
      • 注意和赋值运算符区分
    • 不等于

      • !=
    • 注意:比较表达式的结果一定布尔值,要么为true,要么为false

    • 如果要表示 a变量在[0,100]

      • 0<=a<=100

        • 因为0<=a结果为true或false
        • false/true <=100
      • 0<=a && a<=100

  • 条件运算符

    • 三元运算符: 条件表达式 ? 结果表达式1 : 结果表达式2

    • 运算规则:

      • 如果条件表达式的结果为true,那么整个表达式的结果就取 “结果表达式1”的值,否则就取“结果表达式2”的值
  • 位运算符

    • 左移

      • <<

        • 右边补0
    • 右移

      •  

        • 左边补什么,看最高位,最高位是0,补0,最高位是1,补1
    • 无符号右移

      • 左边直接补0
    • 按位与

      • 1 & 1结果才为1,其他都是0
    • 按位或

      • 0 | 0结果采为0,否则都是1
    • 按位异或

      • 1 ^ 0 或 0 ^ 1是1,其他都是0
    • 位取反

      • ~1就是0,~0就是1
    • 注意:二进制的补码运算

  • 注意:以上所有运算符都是针对基本数据类型

    • 特殊的

      • +

        • 可以用于字符串的拼接
      • ==

        • 可以用于引用数据类型的“地址”的比较
      • =

        • 可以用于引用数据类型变量的赋值
  • 三元运算符

    • 语法:条件?表达式1 : 表达式2
    • 执行特点:先判断条件,如果成立,结果为表达式1,否则 为表达式2
  • 运算符的优先级

    • ( ) >算术运算符 >关系运算符>逻辑运算符>三元运算符>赋值运算符

流程控制结构

  • 顺序

    • 特点:程序从上往下执行
  • 分支

    • if

      • 单分支

        • 语法:

          if(条件){语句}
          
        • 执行顺序:判断条件是否成立,如果成立执行后面的语句

        • 条件一般是: boolean的变量、关系表达式、逻辑表达式 , 最终结果肯定是 true | false

      • 双分支

        • 语法:

          if(条件){语句1}else{语句2}
          
        • 执行顺序: 判断条件是否成立,如果成立执行语句1,否则 执行语句2

        • 条件一般是: boolean的变量、关系表达式、逻辑表达式 , 最终结果肯定是 true|false

      • 多重分支

        • 语法:

           if(条件1 ){语句1} else if(条件2){语句2} ...... else{语句n}
          
        • 执行顺序:先判断条件1,如果成立执行语句1,否则判断条件2 ,如果成立执行语句2,以此类推,如果都不成立执行 语句 n

        • 条件一般是: boolean的变量、关系表达式、逻辑表达式 , 最终结果肯定是 true|false

        • 注意事项: else 可以省略,但不能提到前面

      • 嵌套if

        • 语法:

          if(条件){
              if(条件){}
          }else{
              if(){}
          }
          
        • 执行顺序:先判断外层if的条件,如果成立,才去判断内部if的条件

      • 注意:

        • if、else if、else后面的大括号都可以省略,默认执行第一句,只是不建议省略,可读性差
    • switch

      • 语法:

        switch(变量或表达式){
        case 常量值1: 语句1;break;
        case 常量值2: 语句2;break;
        ......
        default: 语句n;break;
        }
        
      • 执行顺序:

        先去判断switch变量或表达式的值,依次从上往下和各个case去匹配,如果有匹配的,执行对应的后面的语句,直到遇见break为止。如果没有匹配的,则执行default后面 的语句,直到遇见break为止。

      • 注意:

        • 1.常量值不能重复
        • 2.switch判断 的变量类型 只能是 int或char、short、byte、枚举、String(jdk1.7以后)
        • 3、default可以省略,也可以提到前面
        • 4、break可以省略
    • if和switch的对比

      • 能用switch的,肯定能用if,反之,能用if的,不一定能用switch。条件: 等值判断、类型必须满足switch所需类型、多重分支
  • 循环

    • 好处:

      • 1.代码简洁
      • 2.易修改
    • 四要素

      • 循环变量初始化
      • 循环条件
      • 循环操作(循环体)
      • 循环变量更新
    • 分类

      • while

        • 语法:

          循环变量初始化 
          while(循环条件){
          循环操作
          循环变量更新
          }
          
        • 执行顺序:

          循环变量初始化     ①
          while(循环条件){   ②  ⑤
          循环操作           ③
          循环变量更新       ④
          }
          

           

      • do while

        • 语法

          循环变量初始化
          do{
          循环操作
          循环变量更新
          }while(循环条件);
          
        • 执行顺序

          循环变量初始化  
          do{
          循环操作         
          循环变量更新       
          }while(循环条件);  
          
      • for

        • 语法

          for(循环变量初始化; 循环条件; 循环变量更新){
                  循环操作
          }
          
        • 执行顺序

          for(循环变量初始化; 循环条件; 循环变量更新){
              循环操作  
          }
          
        • 注意事项

          1、两个分号必不可少

          2.三个表达式 可以省略,但有可能造成死循环

          3.表达式1可以有多个变量声明,但必须是同一类型,但用逗号隔开

          表达式3可以有多个变量更新,用逗号隔开
          for(int i=0,double j=6;;){错误的!!!!}
          
      • 增强for

        增强for循环(也称for each循环)是迭代器遍历方法的一个"简化版",是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合.
          其内部原理是一个Iteration迭代器,在遍历数组/集合的过程中,不能对集合中的元素进行增删操作.
          
        增强for循环的使用:
          1.使用范围: 用来遍历集合和数组(必须有遍历目标.目标只能是集合或者数组).所有单列表集合都可以使用增强for循环
          2.格式如下:
            for(ElementType element: arrayName) { //集合或数组的数据类型 变量名: 集合名/数组名
            System.out.println(变量名);
        };
        
        上述for循环可被读为:for each element in arrayName do {…}
        
        例如:
        ①使用普通for循环
            int[] num = {1,2,3,4,5,6};
            for(int i =  0 ; i<num.length ; i++){ 
                System.out.println("元素:"+ num[i]); 
            } 
        ②使用增强for循环
             int[] num = {1,2,3,4,5,6};
             for(int i :num){    //集合或数组a: 数组名称num
                 System.out.println("元素:"+ i); 
             } 
        
        使用时要注意的事项:
        1.增强for循环底层也是使用了迭代器获取的,在使用增强for循环遍历元素的过程中不准使用集合对象对集合的元素个数进行修改.
        
        2.迭代器与增强for循环遍历元素的区别: 使用增强for循环遍历集合的元素时,不能调用迭代器的remove方法删除元素,而使用迭代器遍历集合的元素时可以删除集合的元素.
        
        3.增强for循环和普通for循环的区别: 普通for循环可以没有遍历的目标,而增强for循环一定要有遍历的目标.
        
        总结: 相对于普通for循环(;;)而言增强for循环有两个好处:
                                        1.写起来简单容易
                                        2.遍历集合、数组比较简单
        
        
      • 三种常见循环的对比

        • 相同点

          • 都有循环四要素
          • 都能实现任意形式的循环结构,包括题型一和题型二
        • 不同点

          • 执行顺序不同,for、while先判断后执行。do while先执行后判断
          • 执行效率不同, do while效率最高
          • 执行效果不同, do while 无条件执行一次,而while和for 如果第一次条件不成立,执行0次。
          • 语法不同
      • 嵌套循环

        • 特点

          • 一个循环中又嵌套了另一个完整的循环结构
        • 语法

          • 三种循环中,都可以嵌套另外一种任意的循环结构
        • 执行特点

          • 先执行外层循环,然后执行内层循环
        • 案例

          • 九九乘法表
          • 二维图形题,如 等腰三角形
    • 案例

      • 1.1-100求和

        public class MyException {
            public static void main(String[] args) {
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                System.out.println(sum);
            }
        }
        

         

      • 2.统计顾客年龄

      • 3.水仙花数

      • 4.不断输入姓名,直到输入q停止

      • 5.1-100的偶数打印

  • 跳转

    • break

      • 只能 用在switch或循环中,用于跳出所在的最近的循环或switch
    • continue

      • 只能用在循环中,结束本次循环,继续下一次循环
    • return

      • 用于跳出所在的方法

         

三. 数组

数组的好处

  • 1、同时存储多个数,代码简洁,而且提高了效率
  • 2、分类存储,方便查找

数组的概念

  • 对一组具有相同数据类型的变量,使用一个名字来进行管理,这个统一的名字,称为数组名
  • 这一组具有相同数据类型的变量,在堆内存中开辟连续的空间
  • 数组中的每一个数据,称为元素
  • 数组的每一个元素,依靠下标,索引,编号进行区分
  • 数组的元素的总个数,称为数组的长度

一维数组

  • 数组的声明

    • 元素的数据类型[] 数组名;

      • 例如:int[] arr;

        • 元素是int

          • 每一个元素存的是一个整数值
      • 例如:Student[] arr;

        • 元素是Student

          • 每一个元素存的是Student对象的地址
    • 数组的数据类型 数组名;

      • 上面的两个数组,int[]就是数组的类型,元素的类型是int
  • 数组的初始化

    • 动态初始化

      • 数组名 = new 元素的数据类型[数组的长度];
    • 静态初始化

      • 元素的数据类型[] 数组名 = new 元素的数据类型[]{元素1的值,元素2的值......}; 元素的数据类型[] 数组名; 数组名 = new 元素的数据类型[]{元素1的值,元素2的值......};
      • 元素的数据类型[] 数组名 ={元素1的值,元素2的值......};//简写的只能是声明与初始化在一行
  • 数组的长度

    • 数组名.length
  • 数组的元素的表示

    • 数组名[下标]

    • 下标的范围

      • [0,数组的长度-1] [0,数组的长度)
    • 如果超过下标的范围,会报"数组下标越界ArrayIndexOutOfBoundsException"

  • 数组如何遍历

    • (1)for循环遍历

      • for(int i=0; i<数组名.length; i++){ 数组名[i]表示一个元素 }
    • (2)foreach循环遍历

      • for(元素的数据类型 临时变量 : 数组名){ 每一次循环临时变量代表一个元素 }
      • 每一次循环把元素的值copy给临时变量
  • for循环与foreach循环的区别

二维数组

  • 数组的声明

    • 元素的数据类型[][] 二维数组名;
  • 初始化

    • 动态初始化

      • 规则

        • 二维数组名 = new 元素的数类型[行数][列数];

          • 总元素的个数是行数*列数
          • 每一行的列数相同的
      • 不规则

        • 二维数组名 = new 元素的数据类型[行数][];

        • 二维数数组名[行标] = new 元素的数据类型[该行的列数];

          • 二维数数组名[行标] 又称为行对象
        • 二维数数组名[行标] = new 元素的数据类型[]{元素1,元素2.。。。};

    • 静态初始化

      • 二维数组名 = new 元素的数据类型[][] {
            {第一行的各个元素},
            {第二行的各个元素},
            {第三行的各个元素},
            {......}
        };
        

        长度

    • 行数

      • 二维数组名.length
    • 每一行的列数

      • 行对象.length
      • 二维数组名[行标] .length
  • 元素的表示

    • 行对象

      • 二维数组名[行标]

        • 把二维数组如果看成一个一维数组,那么它的每一个元素就是一个行对象
    • 具体的元素

      • 二维数组名[行标][列标]

        • 行标的范围

          • [0,二维数组名.length)
        • 列标的范围

          • [0, 二维数组名[i].length)
  • 如何遍历

    • 普通for

      • for (int i = 0; i < array.length; i++) {
                    for (int j = 0; j < array[i].length; j++) {
                        array[i][j] //代表一个元素
                    }
                }
        

         

    • 增强for

      • for (行对象的类型 行对象名 : 二维数组名) {
                    for (元素的类型 临时变量 :行对象名){
                        临时变量代表每一行中的某个具体元素
                    }
                }
        

         

数组的特点和注意事项

  • 1、数组的类型可以为任意类型

  • 2、数组访问里面的元素 ,通过 数组名[下标], 下标:0-名.length-1

  • 3、数组的长度一旦指定不能修改

  • 数组的多种写法

    • 一维数组

      • 元素的数据类型[] 一维数组名

        • 例如:int[] arr
        • 例如:Student[] arr
      • 元素的数据类型 一维数组名[]

        • 例如:int arr[]
        • 例如:Student arr[]
    • 二维数组

      • 元素的数据类型[][] 二维数组名

        • 例如:int[][] arr
        • 例如:Student[][] arr
      • 元素的数据类型 二维数组名[][]

        • 例如:int arr[][]
        • 例如:Student arr[][]
      • 元素的数据类型[] 二维数组名[]

        • 例如:int[] arr[]
        • 例如:Student[] arr[]

常见异常

  • ArrayIndexOutofBoundsException: 数组下标越界

    • NullPointerException: 空指针异常当一个引用(引用类型的变量),没有具体指向的对象,此时java中用null来表示它,我们如果想使用此变量,则会报空指针异常

对象数组

  • 1.声明

    类型[] 名;

  • 2.开辟空间(指定长度)

    名=new 类型[长度];

  • 3.赋值

    ① 先创建对象(也就是房间中打算放的内容)

    Student s=new Student();
    s.setName();
    s.setAge();
    或
    Student s=new Student(name,age);
    

    ②将对象放到指定的数组某个空间中

    名[下标]=对象;

  • 4.使用

    使用一:循环打印

    for(int i=0;i<数组名.length;i++){
    数组名[i] .方法()/属性;
    }
    

    使用二:循环判断

    for (int i = 0; i < 数组名.length; i++) {
                if (数组名[i].属性 < max) {
                }
            }
    

数组的操作工具类

  • java.util.Arrays

  • 1、查找

    • public static int binarySearch(int[] a,int key)

      • a数组必须是排好序的
  • 2、填充

    • public static void fill(int[] a,int val)

      • 给a数组的每一个元素填充为val
    • public static void fill(int[] a, int fromIndex, int toIndex, int val)

      • 给数组a中fromIndex到toIndex之间的元素填充为val
  • 3、复制

    • public static int[] copyOf(int[] original, int newLength)

      • 复制original数组,得到一个新的数组,新的数组的长度是newLength
      • 如果newLength<original数组的长度,相当于 复制了 original数组的[0,newLength-1]
      • 如果newLength>original数组的长度,相当于 新的数组的前面的元素都是和original数组一样的,剩下的元素都是默认值
    • public static int[] copyOfRange(int[] original, int from, int to)

      • 从original数组的from开始复制到to这个部分的元素,产生一个新的数组
      • from必须在original数组的[0, original数组长度]范围之内
      • to可以在original数组下标范围内,也可以超过,如果超过,超过的部分使用默认值
  • System类中也声明了一个数组复制的方法
  • System.arraycopy(src, srcPos, dest, destPos, length);
  • 第一个参数:源数组 (被复制的数组)
  • 第二个参数:从源数组的srcPos这个下标开始复制
  • 第三个参数:目标数组
  • 第四个参数:从目标数组的destPos位置开始放
  • 第五个参数:一共复制几个
  • 4、排序

    • public static void sort(int[] a)
    • 关于引用数据类型的数组排序,需要java.lang.Comparable或java.util.Comparator接口的支持
  • 5、toString

    • 字符串表示形式由数组的元素列表组成,括在方括号("[]")中。相邻元素用字符 ", "(逗号加空格)分隔。
    • public static String toString(int[] a)

四. 面向对象编程

类与对象

  • 面向对象的理解

    • 面向对象和面向过程的区别

      • 都是编程思想

      • 面向过程强调的是"怎么做"

        • 面向过程的程序,是以函数为基本单位
      • 面向对象强调的是"谁来做"

        • 面向对象是关注的是对象的个体,以类为基本单位,重点在于类的设计,某个行为,数据是在哪个类中描述更合适
    • 面向对象的好处

      • 解决中大型的问题,使用面向对象代码简化了
      • 以人类的思维方式来思考问题,比较好理解
    • 面向对象的思考步骤

      • 1、先分析里面涉及到几个类,分别是什么
      • 2、抽取每个类中的属性和方法,再加以定义
      • 3、面向过程正常调用和执行
  • 类与对象

    • 什么是类?

      • 对现实世界中,具有共同特性的某一类事物的抽象的描述,在Java中使用一个类来描述一类事物
    • 什么是对象?

      • 对象是类的一个具体的个体,是类的一个实例(instance)
    • 类是对象的设计模板,对象是类的实例

    • 二者的区别

      • 类是抽象的
      • 对象是具体的,实际存在的,也称为 实体或实例
    • 二者的关系

      • 类是由对象总结或抽取出来的一个概念。 是对象的所属类型
      • 对象是通过类创建出来的具体的实体
  • 如何定义一个类?

    • 语法格式

      • 【修饰符】 class 类名{ 属性列表 构造器列表 方法列表 }
  • 对象的内存结构图

      • 存放方法中的基本类型变量
      • 存放方法中的引用类型 变量的引用(地址号)
      • 存放对象本身
    • 方法区

      • 存放类的信息:类名、包名、属性的描述信息、方法的描述信息

类的内部成员

  • 属性

    • 又称为成员变量

      • 属性的声明是在类中,方法外
    • 如何声明

      • 声明格式

        • [修饰符] 数据类型 属性名;

        • 如何在声明类时没有显式(手动)给属性赋初始值,那么在创建对象后,这个属性有默认的初始值

          • byte,short,int,long 初始值为: 0

            • float,double 初始值为: 0.0
            • boolean 初始值为: false
            • char 初始值为: \u0000
            • 引用数据类型(类、接口、数组) 初始值为: null
      • [修饰符] 数据类型 属性名 = 初始值; //显式初始化

    • 封装

      • 在属性的修饰符位置,使用private修饰

        • 只能在本类中直接使用,在其他类中不可见
      • 提供公共的get/se

        public class Student {
            private String name;
            private int age;
        
            public void setName(String name) {
                this.name = name;
            }
        
            public String getName() {
                return name;
            }
        
            public void setAge(int age) {
                this.age = age;
            }
        
            public int getAge() {
                return age;
            }
        }
        
        • set方法

          • 用于为属性赋值,修改属性值
        • get方法

          • 用于获取,访问某个属性的值
  • 方法

    • 基本概念

      • 方法:代表一个功能的定义,具有可重用性,就可以把这样的一段代码,抽象成一个方法

      • 一个方法完整的定义:

        • 方法的签名

          • 修饰符 返回值类型 方法名(形参列表)抛出的异常列表
        • 方法的实现

          • { 方法的实现 }
      • 结论:

        • 1、只要是无返回值的方法的调用,只能是单独一个语句

        • 2、在其他类中

          • 如果被调用的方法是static修饰

            • 被调用的方法是无参无返回值的

              • 类名.方法名();
            • 被调用的方法是有参无返回值的

              • 类名.方法名(实参列表);
            • 被调用的方法是无参有返回值

              • 变量名 = 类名.方法名();
            • 被调用的方法是有参有返回值

              • 变量名 = 类名.方法名(实参列表);
          • 如果被调用的方法是非static修饰的

            • 同上,就是"类名."改成"对象名."
        • 3、要不要传实参,看被调用的方法是否声明了形参

        • 4、要不要接收返回值,看被调用的方法返回值类型是不是void,如果不是void,就可以接收;

          如果是void,就不能接收
          
    • 方法的分类

      • 四种形式

        • 无参无返回值

          • 声明格式

            • 修饰符 void 方法名(){ 方法体 }
          • 调用格式

            • (1)在本类中

              • 方法名();

                • 单独一个语句
            • (2)在其他类中

              • 如果被调用的方法是static修饰

                • 类名.方法名();
              • 如果被调用的方法是非static修饰的

                • 对象名.方法名();

                  • Scanner input = new Scanner(System.in); //声明了Scanner对象
                  • input.nextInt();
        • 有参无返回值

          • 声明格式

            • 修饰符 void 方法名(形参列表){ 方法体 }
            • 其中形参列表
            • 形参的数据类型 形参名1, 形参的数据类型 形参名2
            • 每一个形参的声明之间使用,分割
          • 调用格式

            • (1)在本类中

              • 方法名(实参列表);

                • 单独一个语句

                • 实参列表

                  • 实参直接是“常量值”,“变量名”,“表达式”
                  • 实参的个数、顺序、数据类型必须与被调用的方法的形参的个数、顺序、数据类型一一对应
            • (2)在其中类中,加上类名/对象名.即可

        • 无参有返回值

          • 声明格式

            • 修饰符 返回值类型 方法名(){ 方法体 }
          • 调用格式

            • (1)在本类中

              • (A)经典形式

                • 变量名 = 方法名();

                  • 注意:这个变量的数据类型必须与被调用方法的返回值类型一致(或兼容)
              • (B)作为另一个方法调用的实参

                class TestReview {
                
                    public static int getVolume(int area, int height) {
                        return area * height;
                    }
                
                  public static double getArea(double lenght, double width) {
                        return lenght * width;
                  }
                
                    public static void main(String[] args) {
                        int a = 2;//长
                        int b = 3;//宽
                        int h = 4;//高
                      double area = getArea(a, b);//把方法的返回值赋值给一个变量
                        //变量area的类型要与 方法getArea()的返回值类型 一致
                      //int v = getVolume(getArea(a,b) ,h);//错误
                        //getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
                        double v = getArea(a, b) * h; //把方法的返回值 作为表达式的一部分
                    }
                }
                
              • System.out.println(方法名());

                • 被调用的方法的返回值类型与另一个方法的形参的类型必须一致或者兼容
                • getVolume( getArea() , height);
                • getArea()的返回值类型与getVolume()的第一个形参的类型要求一致
            • (C )作为表达式的一部分

              class TestReview {
               public static int getVolume(int area, int height) {
                          return area * height;
               }
                      
               public static double getArea(double lenght, double width) {
                        return lenght * width;
               }
                    
               public static void main(String[] args) {
                          int a = 2;//长
                        int b = 3;//宽
                      int h = 4;//高
                        double area = getArea(a, b);//把方法的返回值赋值给一个变量
                 
                        //变量area的类型要与 方法getArea()的返回值类型 一致
                   
                        //int v = getVolume(getArea(a,b) ,h);//错误 
              
                        //getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
              
                        double v = getArea(a, b) * h; //把方法的返回值 作为表达式的一部分
              
                    }
              }
              
      • 有参有返回值

        • 声明格式

          • 修饰符 返回值类型 方法名(形参列表){ 方法体}
        • 调用格式

          • (1)在本类中

            • (A)经典形式

              • 变量名 = 方法名(实参列表);

                • 注意:这个变量的数据类型必须与被调用方法的返回值类型一致(或兼容)
            • (B)作为另一个方法调用的实参

              class TestReview {
                    public static int getVolume(int area, int height) {
                        return area * height;
                  }
                
                  public static double getArea(double lenght, double width) {
                  return lenght * width;
                    }
              
                  public static void main(String[] args) {
                        int a = 2;//长
                      int b = 3;//宽
                        int h = 4;//高
                      double area = getArea(a, b);//把方法的返回值赋值给一个变量
                  //变量area的类型要与 方法getArea()的返回值类型 一致
                      
                  //int v = getVolume(getArea(a,b) ,h);//错误 
              
                        //getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
                        
                      double v = getArea(a, b) * h; //把方法的返回值 作为表达式的一部分
                    }
              }
              
        • System.out.println(方法名(实参列表));

          • 被调用的方法的返回值类型与另一个方法的形参的类型必须一致或者兼容
        • getVolume( getArea(实参列表) , height);

          • getArea()的返回值类型与getVolume()的第一个形参的类型要求一致

            • (C )作为表达式的一部分
                        class TestReview {
        
                            public static int getVolume(int area, int height) {
                return area * height;
                            }
          
                        
              public static double getArea(double lenght, double width) {
                                return lenght * width;
                      }
                
                      public static void main(String[] args) {
                    int a = 2;//长
                              int b = 3;//宽
                    int h = 4;//高
                              double area = getArea(a, b);//把方法的返回值赋值给一个变量
            
                          //变量area的类型要与 方法getArea()的返回值类型 一致
                    
                          //int v = getVolume(getArea(a,b) ,h);//错误 
            
                              //getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
                      
                              double v = getArea(a, b) * h; //把方法的返回值 作为表达式的一部分
                          }
                      }
        
  • 命令行参数(了解)

    • 给main方法传的参数

    • public static void main(String[] args)

      • 通过命令行传的参数,在args数组中
    • 如果要使用命令行的参数 args[0] ......

    • 怎么传

    • java 包.类 参数1 参数2 参数3 ......

      • 参数之间使用空格
    • eclipse

      • run菜单 --> run configration --> 选择运行哪个main方法 --> program argument

        • 各个参数空格分隔
    • 可变参数

      • 可变参数的声明

        • 数据类型 ...... 形参名

          • 声明必须在方法的形参列表的最后一个
        • 一个方法只能有一个可变参数

          • 可变参数的数据类型 可以 是任意类型
      • 可变参数的使用

        • 在声明它的方法中,可变参数当做数组使用

          • 形参名就是数组名
      • 可变参数的方法调用时

        • 1、可变参数的对应的位置,可以传0~n个实参
      • 2、可变参数的对应的位置,可以传1个数组对象,当然这个数组对象的元素数据类型必须与可变参数的数据类型是一致

      • 示例

        找多个整数中的最大值,至少一个

          int getMax ( int a, int...b){
                  int max = a;
                      for (int num : b) {
                        if (max < num) {
                              max = num;
                        }
                      }
                    return max;
                  }
      

       

    • 方法重载

    • 特点:

      • 同一个类中,方法名相同,参数列表不同(参数个数或参数类型或参数顺序)不同,返回类型不要求
      • 作用:允许功能相似的方法重名,使方法定义和调用都简化了
    • 调用:

      • 正常方法调用即可,传进对应的参数,编译器自动选择 你指定类型参数的方法执行
  • 方法参数传递

    • java中是按值传递的

      • 基本类型做参数

        • 值代表的是数值内容,传参相当于拷贝了一个备份到形参.形参的改变不影响实参
      • 引用类型做参数

        • 值代表的是地址号,传参相当于传递了一个地址号(引用)到形参.形参的改变影响实参
      • 实参将值传给形参的过程,称为参数传递,实际上就是一个赋值的过程

  • 构造器

    • 又称为构造方法

    • 如何声明

      • (1)无参构造

        • 【修饰符】 构造器方法名(){ 方法体 }
      • (2)有参构造

        • 【修饰符】 构造器方法名(形参列表){ 方法体 }
    • 特点

      • A:构造器方法名必须与类名相同 B:构造器没有返回值类型 C:构造器可以重载 D:任何类中都有构造器,如果没有显式/手动声明构造器,编译器将自动添加一个默认的无参构造器 E:如果显式/手动声明了任何一个构造器,编译器将不再自动添加默认的无参构造
    • 作用

      • (1)和new一起使用,创建对象

        • new 构造方法()

          • 调用无参构造

            • Random rand = new Random();
        • new 构造方法(实参列表)

          • 调用有参构造

            • Scanner input = new Scanner(System.in);
      • (2)为属性赋值

  • 代码块

    • 静态代码块

      • 声明格式

        • static{ }
      • 作用

        • 为静态属性赋值
    • 非静态代码块

      • 声明格式

        • { }

          • 在类中,称为构造块
          • 在方法中,称为普通代码块,这里不做说明
      • 作用

        • 为非静态属性赋值
        • 当多个构造器中重复的代码,可以提取到构造块中
    • 执行的特点

      • 静态代码块(只执行一次)-->构造块(每次创建对象) -->构造器(每次创建对象)
    • 示例

       public class Son extends Father {
                  public Son() {
                      System.out.println("4444444444");
                  }
           
                  {
                      System.out.println("555555555555");
                  }
      
                  static {
                      System.out.println("66666666666");
                  }
      
                  public static void main(String[] args) {
                      System.out.println("77777777777777");
                      new Son();//3 6 7 2  1  5  4
                      System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                      new Son();//2  1  5  4
                      System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                      new Father();//2 1
                  }
              }
              class Father {
                  {
                      System.out.println("22222222222");
                  }
      
                  public Father() {
                      System.out.println("11111111111");
                  }
      
                  static {
                      System.out.println("3333333333");
                  }
              }
      

       

       

  • 内部类

    • 根据声明的位置不同

      • 成员内部类

        • 根据是否有static修饰

          • 静态成员内部类,简称静态内部类
          • 非静态成员内部类,简称成员内部类
      • 局部内部类

        • 根据是否有名字

          • 局部内部类
          • 匿名局部内部类,简称匿名内部类
    • 各种内部类的特点

      • 成员内部类

        • 如何声明

          • 【修饰符】 class 外部类{ 【修饰符】 class 内部类{ } }
        • 如何使用

          • (1)在外部类中使用内部类

            • 除了在外部类的静态成员中,不能直接使用内部类
            • 在外部类的非静态成员部分,把内部类当做普通类使用
          • (2)在外部类的外面使用内部类

            • 它需要外部类的对象,才能获得内部类的对象等

              • 示例一

                class  Outer{
                    class Inner{
                    }
                }
                //使用
                Outer.Inner  inner = new Outer().new Inner();
                Outer out = new Outer();
                Outer.Inner  inner = out.new Inner();
                
                
                
              • 示例二

                class Outer{
                          class Inner{
                          }
                          public Inner getInner(){
                              return new Inner();
                          }
                  }
                  
                  //使用 
                  Outer out = new Outer();
                  Outer.Inner in = out.getInner();
                

                 

              • 示例三

                class Outer{
                    class Inner{
                    }
                }
                //继承 Inner
                

                 

                MyClass需要调用Inner的构造器而调用Inner的构造器,需要外部类Outer的对象

                class MyClass extends  Outer.Inner{
                        public MyClass(Outer out){
                            out.super();
                        }
                }
                
          • (3)在内部类中使用外部类的成员

            • 没有限制,都能用,包括静态的,包括私有的
        • 其他特点

          • (1)外部类的权限修饰符只能是public 和缺省的,而成员内部类的权限修饰符可以4种
          • (2)非静态的成员内部类中是不能包含静态成员的(静态的属性,静态方法,静态的代码块)
          • (3)成员内部类有自己独立的字节码文件: 外部类名$内部类名.class;在类型描述上使用:外部类名.内部类名
      • 静态内部类

        • 如何声明

          • 【修饰符】 class 外部类{ 【修饰符】static class 内部类{ } }

            • (1)需要在内部类中声明静态成员,那么这个内部类必须是static
            • (2)需要在外部类的静态成员中使用这个内部类,那么这个内部类也必须是static
        • 如何使用

          • (1)在外部类中使用: 没有限制

          • (2)在外部类的外面

            • 示例一

              class Outer {
                  static class Inner {
                      public void test() {
                      }
                  }
              }
              

              //如何调用test()

              (1)从test()看,它不是静态的,因此需要Inner的对象

              (2)Inner在Outer里面,而Inner又是static修饰

              Outer.Inner in = new Outer.Inner();

              in.test();

               

            • 示例二:

            class Outer {
                static class Inner {
                    public static void test() {
                    }
                }
            }
            

             

            //使用test()

            (1)看test(),是static修饰符,说明不需要Inner对象

            (2)看Inner是static修饰,也不需要外部类的对象

            Outer.Inner.test();

          • (3)在内部类中使用外部类的成员

            • 在静态内部类中,不能使用外部类的非静态成员
            • 其他的成员可以直接使用,包括私有的
        • 其他的特点

          • (1)静态的内部类,可以使用权限修饰符4种
          • (2)静态的内部类可以包含静态的成员
          • (3)静态的内部类也有自己的独立的字节码文件:外部类$内部类.class
      • 局部内部类

      • 如何声明

        • 【修饰符】 class 外部类{ 【修饰符】 返回值类型 方法名(形参列表){ class 内部类名{ } } }
      • 如何使用

        • (1)在外部类中

          • 只能在声明它的方法中,而且作用域只在声明处开始,到它所属的}结束
        • (2)在外部类的外面

          • 不能直接使用局部内部类的类型

            • 但是,在外部类的外面,是否可以得到局部内部类的对象
              public class TestLocal {
                  public static void main(String[] args) {
                    MyInter m = new Out().test1();
                      Object obj = new Out().test2();
                }
              }
              
              interface MyInter {
                }
              
                class Out {
                  //使用父接口类型返回
                    public MyInter test1() {
                      class Inner implements MyInter {
              
                      }
                    return new Inner();
                  }
              
                  //使用父类类型返回
                public Object test2() {
                      class Inner implements MyInter {
                    }
                      return new Inner();
                }
              }
            
            • 可以
          • (3)在内部类中使用外部类的成员(成员变量,成员方法)

          • 它是否可以使用某个外部类的成员,受所在方法的影响(static)

          • (4)在内部类中使用外部类的局部变量

            • 只能使用声明该内部类方法中,并在内部类之前声明的局部变量
            • 而且这个局部变量要加final修饰
        • 其他的特点

          • (1)不能使用public,protected,private,static这些成员修饰
          • (2)局部内部类中,也不能包含静态成员的
          • (3)局部内部类也有自己的字节码文件:外部类$编号内部类.class
    • 匿名内部类

      • 如何声明

        • 【修饰符】 class 外部类{ 【修饰符】 返回值类型 方法名(形参列表){ new 父类/父接口(){ //内部类的成员 } } }
        • 此处,父类要注意是否有无参构造
      • 如何使用

        • (1)使用父类的多态引用使用它对象

          • 只能调用父类中声明过的方法

              //在main中,写一个Father的匿名内部类的对象
            public class TestNiMing {
                  public static void main(String[] args) {
                  Father f = new Father("尚硅谷") {
              
                          /*@Override
                          public String getInfo() {
                          return "内部类的示例:" + getInfo();//java.lang.StackOverflowError
                          }*/
                      @Override
                          public String getInfo() {
                              return "内部类的示例:" + super.getInfo();
                          }
              
                          public void method() {
                              System.out.println("匿名内部类自己的方法");
                          }
                  };
                      System.out.println(f.getInfo());
                      //f.method();//无法访问
                  }
              
              }
              
              class Father {
                  private String info;
              
                  public Father(String info) {
                      super();
                  this.info = info;
                  }
            
              
                  public String getInfo() {
                      return info;
                  }
              
            
                  public void setInfo(String info) {
                      this.info = info;
                  }
              }
            

             

        • (2)本态引用调用方法

          • 可以调用父类的方法,也可以调用子类自己的方法

              public static void main(String[] args) {
                  //父类是Object,使用匿名内部类,声明一个方法void fun(),并调用
                new Object(){
                      public void fun(){
                        System.out.println("匿名内部类的方法");
                      }
                }.fun();
              }
            
          • (3)作为方法的实参

            import java.util.Arrays;
            import java.util.Comparator;
            
            public class TestLocal3 {
                  public static void main(String[] args) {
                      Dog[] dogs = new Dog[3];
                      dogs[0] = new Dog(7.8);
                      dogs[1] = new Dog(5.8);
                      dogs[2] = new Dog(9.8);
                    Arrays.sort(dogs, new Comparator() {
                          @Override
                        public int compare(Object o1, Object o2) {
                              Dog d1 = (Dog) o1;
                              Dog d2 = (Dog) o2;
                              if (d1.getWeight() > d2.getWeight()) {
                                  return 1;
                              } else if (d1.getWeight() < d2.getWeight()) {
                                  return -1;
                              }
                              return 0;
                          }
                      });
                  }
              }
            
              class Dog {
                private double weight;
            
                  public Dog(double weight) {
                      super();
                      this.weight = weight;
                  }
                  
                  public double getWeight() {
                      return weight;
                  }
                 
                  public void setWeight(double weight) {
                      this.weight = weight;
                  }
              }
            

             

        • 其他特点

          • (1)没有修饰符
          • (2)没有构造器
          • (3)没有类名
          • (4)只有一个对象,声明匿名内部类与创建对象,同时进行
          • (5)也有自己独立的字节码文件:外部类名$编号.class
    • 为什么使用内部类

      • 1、在一个类还有一个完整的结构(有属性,方法等),需要使用内部类,而且这个内部类只为外部类服务
    • 2、内部类可以使用外部类的成员,包括私有的

设计模式

  • 单例设计模式

    • 特征

      • 某个类型在整个系统中,只有一个唯一的对象
    • 形式

      • 1、懒汉式

        • class Single {
              private static Single instance;
          
              private Single() {
              }
          
              public static Single getInstance() {
                  if (instance == null) {
                      instance = new Single();
                  }
                  return instance;
              }
          }
          

           

      • 2、饿汉式

        class Single {
            public static final Single INSTANCE = new Single();
        
            private Single() {
            }
        }
        
        class Single {
            private static Single instance = new Single();
        
            private Single() {
            }
        
            public static Single getInstance() {
                return instance;
            }
        }
        

         

      • 3、枚举式

    • 要点

      • (1)构造器私有化
      • (2)在单例类中创建这个唯一的对象
  • 模板设计模式

    • 特征

      • 在父类中能确定某个功能的整体的算法结构,但是对于其中的某个步骤,在父类中无法确定,要延迟到子类中去实现,这个时候就可以通过模板设计模式
    • 属于抽象类的应用

    • 示例

      计算任意一段代码或功能的运行时间

      //父类:确定算法结构
      abstract class SuperClass {
          public final long getTime() {
              //1、获取开始时间
              long start = System.currentTimeMillis();
              //2、运行要计算时间的代码
              run();
              //3、获取结束时间
              long end = System.currentTimeMillis();
              //4、计算
              return end - start;
          }
      
          protected abstract void run();
      }
      
      class SubClass extends SuperClass {
          protected void run() {
              //要测试时间的代码
          }
      }
      

       

  • 工厂设计模式

    • 目的:使得使用者与创建者分离

      • 把创建某些接口的实现类对象交给工厂来做
    • 简单工厂模式

      • 示例

        //1、所有的要创建的对象,都是符合某个标准的

        interface Car {
            void run();
        }
        
        class BMW implements Car {
            public void run() {
                ......
            }
        }
        
        class Benz implements Car {
            public void run() {
                ......
            }
        }
        

         

        //2、设计一个工厂类,用来创建各种各样的Car的实现类的对象

        class CarFactory {
        
            public static Car getCar(String name) {
                if ("宝马".equals(name)) {
                    return new BMW();
                } else if ("奔驰".equals(name)) {
                    return new Benz();
                }
                return null;
            }
        }
        

         

      • 优点:代码简洁

      • 缺点:当增加新产品时,需要修改工厂方法,违反了“对修改关闭,对扩展开放”的原则

    • 工厂方法模式

      • 示例

        //1、所有的要创建的对象,都是符合某个标准的

        interface Car {
            void run();
        }
        
        class BMW implements Car {
            public void run() {
                ......
            }
        }
        
        
        class Benz implements Car {
            public void run() {
                ......
            }
        }
        

         

        //2、每一种产品都自己的工厂生产

        //所有的工厂就有共同的特征
        interface Factory {
            Car getCar();
        }
        
        class BMWFactory implements Factory {
            public BMW getCar() {
                return new BMW();
            }
        }
        
        class BenzFactory implements Factory {
            public Benz getCar() {
                return new Benz();
            }
        }
        

        优点:如果增加新产品,只需要增加对应的工厂即可,不需要修改原来的代码

      • 缺点:类太多,代码复杂

    • 抽象工厂模式

  • 代理模式

    • 代理类来控制被代理类的方法的访问

    • 实现AOP的基础

    • 静态代理

      • 三个角色

        • 主题(接口)

          • 代理类与被代理类必须都实现主题接口
        • 代理类

          • 必须持有被代理类对象的引用
        • 被代理类

      • 示例代码

        //主题

        interface   BuyHouse{
            void trade();
        }
        

         

        //被代理者

        class Real implements BuyHouse{
            public void trade(){
                .....    
          }
        }
        

         

        //代理类

        class Agent implements BuyHouse {
            private BuyHouse target;
        
            public Agent(BuyHouse target) {
            this.target = target;
            }
        
            public void trade() {
                ....前置工作
                target.trade();
                ...后置工作
            }
        }
        
            BuyHouse a = new Agent(new Real());
            a.trade();
        
        
        
    • 动态代理

      • 三个角色

        • 主题(接口)

          • 代理类与被代理类必须都实现主题接口
        • 代理类

          • 代理类的创建方式变了,由类加载器对象,在运行时产生
        • 被代理类

      • 要点

        • 1、主题

          • 和静态代理模式一样,就是一个接口
        • 2、被代理类

          • 和静态代理模式一样,实现了主题的一个实现类
          • 负责核心业务逻辑代码
        • 3、处理器

          • 用来描述,代理类要做的工作
          • 必须实现一个InvocationHandler接口
          • 重写一个public Object invoke(Object proxy,Method method,Object[] args)
          • 还要持有被代理类的对象的引用
        • 4、动态的创建代理类或代理类的对象

          • Proxy.newProxyInstance(被代理类的类加载器,被代理类实现的所有接口,处理器对象);
          • 被代理类的类加载器,用来动态的在内存中生成代理类的Class对象用,即动态生成代理类,
          • 生成代理类时,要和被代理类实现同样的接口
      • 示例代码

关键字的使用

  • this

    • 表示当前对象

    • 用法

      • this.属性

        • 当成员变量与局部变量重名时
      • this.方法

      • this()或this(实参列表)

        • 表示调用本类的其他构造器
        • 必须在首行
        • 避免循环调用
  • super

    • 表示父类的引用

    • 用法

      • super.属性

        • 当子类的属性与父类的属性重名时
      • super.方法

        • 调用父类的方法实现
      • super()或super(实参列表)

        • 在继承时,调用父类的构造器
  • static

    • 表示静态的

    • 修饰

      • (1)属性

        • 该属性表示所有该类的对象,共享

          • 存储在“方法区”或“元空间”
          • 其中一个对象对它修改,其他的对象的该属性值也会被修改
          • 它的get/set也是static的
          • 可以使用“显式初始化”,还可以使用“静态代码块”进行初始化
      • (2)方法

        • 该方法是类方法

          • 可以通过“类名.”调用
      • (3)代码块

        • 静态代码块,在类加载并初始化时调用,并且只会执行一次
        • 一般用于静态属性的赋值
      • (4)内部类

  • final

    • 表示最终的

    • 修饰

      • (1)类

        • 该类不能被继承
      • (2)属性

        • 该属性值不能被修改,即是常量属性

        • 该属性必须手动初始化

          • (1)显式初始化
          • (2)构造块中初始化
          • (3)在每一个构造器中初始化
      • (3)方法

        • 该方法不能被重写
      • (4)局部变量

        • 该局部变量的值一旦被赋值,就不能被修改,即常量
  • native

    • 表示原生的

    • 表示非Java语言实现的

      • 但是使用者可以把它当做Java的方法或成员使用
  • new

    • 表示创建对象
    • 和构造器一起
  • instanceof

    • 表示某个对象是否是本类或本类的子类的对象

    • 例如:a instanceof Type

      • 返回true

        • a是Type的对象
        • a是Type的子类的对象
  • extends

    • 表示继承

       

  • 接口

    • 为什么要声明接口?

      • (1)解决Java的单继承

        例如:多功能的交通工具

        既具有飞机的飞行功能,

        又具有船的水上航行功能

        又具有车的路上行驶功能

         

      • (2)多个不相关的类,可能具有共同的行为特征,这样的行为特征,就可以提取成一个接口

        例如:

        飞机有飞的能力

        小鸟有飞的能力

        ......

        飞机与小鸟之间没有is-a的关系

        例如:

        学生对象可比较

        商品对象可比较

        ......

         

        学生与商品没有is-a的关系,可比较行为可以抽取成接口

        • 补充了类与类之间的关系,对象与对象之间的关系,has -a 的关系,like -a的关系
        • 因为继承只能表示is-a的关系
      如何声明接口?
          【修饰符】 interface  接口名
      
      如何实现接口?
      
          【修饰符】 class 实现类名  implements 接口名1,接口名2......
      如果继承一个类与,实现接口同时存在,那么要先继承后实现
          【修饰符】 class 子类  extends 父类  implements 接口名1,接口名2......
      
  • 接口的特点

    • (1)使用interface声明

    • (2)接口中只能有公共的抽象方法与全局静态常量,抽象方法的 public abstract可以省略,全局静态常量的 public static final可以省略

    • (3)接口不能实例化,不能直接创建对象

    • (4)接口用来被实现的,它的实现类如果不是抽象类,那么必须实现接口的所有的抽象方法,如果实现类是个抽象类,可以保留抽象方法

    • (5)一个类可以实现多个接口(解决Java单继承的问题)

    • (6)接口没有构造器

    • (7)接口可以继承接口,而且可以继承多个接口

    • (8)接口与实现类的对象之间是多态引用

  • 接口的应用

  • 抽象类

    • 为什么声明抽象类?

      • (1)在父类中明确子类应该包含某些方法,但是在父类中又不能给出具体的实现,父类中只能把这个方法声明为抽象方法,包含抽象方法的类必须是抽象类

        abstract class 几何图形类 {
            public abstract double getArea();
        }
        
        class 圆 extends 几何图形类 {
            private double 半径;
        
            public double getArea() {
                return Math.PI * 半径 * 半径;
            }
        }
        
        class 矩形 extends 几何图形类 {
            private double 长;
            private double 宽;
        
            public double getArea() {
                return 长 * 宽;
            }
        }
        

         

        原因:

        (1)从逻辑角度:几何图形类中,应该包含所有几何图形共同的特征那么所有几何图形,就应该包含"获取面积"的功能

        (2)语法角度:通过“几何图形”类对圆对象,矩形对象等的多态引用,应该是可以调用"获取面积"的方法,如果父类中没有声明该方法,就无法调用,无法实现多态引用如果父类是一个抽象的概念,虽然里面没有抽象方法,但是我不希望它被创建对象,那么可以把这个类声明为抽象类

    • 如何声明抽象类?

      • 格式

        • 【修饰符】 abstract class 抽象类的名称{ 类的成员列表 }
    • 抽象类的特点:

      • (1)使用abstract修饰 (2)不能实例化,不能直接创建对象 (3)抽象类可以包含抽象方法,也可以没有抽象方法 (4)抽象类可以包含其他的成员:属性(类变量,实例变量)、方法(类方法,实例方法)、构造器(无 参、有参,供子类调用)、代码块等 (5)抽象类用来被继承的,子类继承抽象类时,如果子类不是抽象类,那么必须重写/实现父类的所有抽象 方法,如果子类是抽象类,那么可以保留抽象方法 (6)有抽象方法的类必须是抽象类 (7)抽象类与子类的对象之间是多态引用
    • 如何声明抽象方法?

      • 【修饰符】 abstract 返回值类型 方法名(形参列表);
      • 没有方法体
    • 抽象类的应用

      • (1)模板设计模式
  • package

    • 包的作用

      • (1)避免类的重名 (2)限定访问权限,如果希望某个类或成员仅限于本包使用,那么该类或成员的权限修饰符是缺省的 (3)便于管理,把功能相近的类,放在一个包中,分类管理
    • 包的声明

      • package 包名;

      • 注意

        • (1)必须在源文件的第一行

        • (2)一个源文件只能有一句声明包的语句

        • (3)包名的命名习惯

          • 包名的命名规范: (1)域名倒置+分类的包名 (2)全部单词都小写,每个单词之间使用.
    • 包的导入

      • 标准格式

        • import 包.类名;
      • 简写格式:

        • import 包.*;

          • 此处的*只能代表类,不包括子包
      • 静态导入

        • import static 包.类.*;

          • 此处的*代表类中的静态成员(静态方法和静态的属性)
          • 意味着可以在本类中,直接使用被导入的静态成员,而不需要加“类名.”
    • 如果一个源文件声明了包

      • 编译的格式:

        • javac -d 字节码文件生成的存储目录 源文件名.java

        • 例如:javac -d ./ 源文件名.java

          • ./表示源文件的当前目录
      • 运行的格式

        • java 包.类名
    • JDK中常见的包

      1.java.lang ---- 包含一些Java语言的核心类,如String、Math、Integer、System和Thread,Object,提供常用 功能。

      1. java.net ---- 包含执行与网络相关的操作的类和接口。
      2. java.io ---- 包含能提供多种输入/输出功能的类。
      3. java.util ---- 包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
      4. java.text ---- 包含了一些java格式化相关的类
      5. java.sql ---- 包含了java进行JDBC数据库编程的相关类/接口
      6. java.aw t---- 包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的 图形用户界面(GUI)。
      7. java.applet----包含applet运行所需的一些类。

面向对象三大特征

  • 封装性

    • 广义的封装

      • 1、方法:把功能的实现,封装到方法中,对于使用这个方法者来说,隐藏方法的实现,暴露方法的签名,可以通过方法的签名调用即可
      • 2、把一个事物的数据描述,行为描述封装到一个类中
      • 3、包:如果某个类或成员的修饰符是“缺省”的,这个类或成员就仅限于本包使用
      • 4、组件的封装:例如:网银支付,支付宝等等,提供给使用者的是“接口”
      • 5、系统的封装:
    • 狭义的封装

      • 属性的封装

        • 如何实现

          • 1、在属性的前面加private修饰
          • 2、公共的get/set方法
    • 隐藏了实现细节,仅暴露使用的方式

    • 作用:

      • 安全,使用简便,隔离它的变化
    • 权限修饰符

      • private

        • 修饰

          • (1)属性
          • (2)方法
          • (3)构造器:构造器的修饰符只能是private,缺省,protected,public
          • (4)内部类
        • 权限范围

          • 仅限于本类中访问
      • 缺省

        • 修饰

          • (1)类:外部类,内部类
        • (2)属性

          • (3)方法
          • (4)构造器
        • 权限范围

          • 仅限于本包使用
      • protected

        • 修饰

          • (1)属性
        • (2)方法

          • (3)构造器
          • (4)内部类
        • 权限范围

          • 仅限于本包或其他包的子类
      • public

        • 修饰

          • (1)类:外部类,内部类
        • (2)属性

          • (3)方法
          • (4)构造器
        • 权限范围:

          • 任意位置

            • 只不过其他包,需要导包import
  • 继承

    • 为什么要继承

      • 1、当多个类之间具有共同的特征,那么可以把这个共同的部分抽取成一个父类,这些类就可以通过继承这个父类,而达到“代码复用”,这样就不用在多个类中重复声明相同的部分
      • 2、模拟,表现现实世界中一个“is-a”的关系
    • 如何实现继承?

      • extends

        • 扩展
      • 语法格式

        • 【修饰符】 class 子类名 extends 父类名{}
    • 继承的特点

      • 1、子类会继承父类的所有的属性和方法,但是不会继承父类的构造器

        • 子类也会继承父类的私有的属性与方法,但是在子类中不能直接使用父类的私有成员
      • 2、子类一定会调用父类的构造器

        • (1)如果子类构造器中,没有写super()或super(实参列表)语句,那么默认就是调用父类“无参构造”
        • (2)子类的构造器中,也可以手动调用父类的构造器,super()或super(实参列表)
        • (3)当父类没有无参构造时,子类的构造中必须手动调用父类的有参构造super(实参列表)
        • super()或super(实参列表)它必须在子类构造器的首行
      • 3、Java中只支持单继承,但是又支持多层继承

        • (1)Java类只有一个直接父类
        • (2)Java类又支持代代相传
        • (3)Java类的根父类是java.lang.Object
    • 作用:

      • (1)代码复用
      • (2)表示“is-a”的关系
  • 多态

    • 表现

      • (1)方法的多态性:方法的重写

      • (2)对象的多态性

        • 编译时类型与运行时类型不一致

          • 前提

            • (1)继承+重写
            • (2)多态引用:父类的变量指向子类的对象
          • 最终表现:编译时按照父类的类型进行编译检查,运行时,执行的是子类“重写”的代码

        • 是实现多态绑定技术的前提

    • 数据类型的转换

      • 向上转型

        • 多态引用

          • 父类的变量指向了子类的对象,编译时呈现出子类的对象向上转型成父类
        • 自动完成

      • 向下转型

        • 强制转换

          • 子类类型 变量 = (子类的类型)父类的变量;

            • 要保证转型成功

              • 这个父类的变量必须是指向该子类的对象

              • 在转换之前加判断:

                • if(变量 instanceof 子类的类型){ 才可以向下转型成这个子类 }
            • 其他的都失败

              • (1)父类的变量指向父类的对象
              • (2)父类的变量指向其他子类的对象
              • 失败报异常:java.lang.ClassCastException类型转换异常
    • 多态的作用:代码更灵活

    • 多态的应用

      • 1、多态参数
      • 2、多态数组
      • 3、多态属性
    • 面试题

      • 1、static的方法

        • static的方法不能被重写,因此也不符合多态的特征

        • 示例

          /*
           * static与多态static的方法,是不能被重写
           */
          public class TestStatic {
              public static void main(String[] args) {
                  Father.method();//执行的是父类的方法
                  Son.method();//子类的方法
                  System.out.println("------------------");
                  Father f = new Father();//本态引用
                  f.method();//可以这么调用    执行父类
                  System.out.println("------------------");
                  Son s = new Son();//本态引用
                  s.method();  //执行子类
                  System.out.println("------------------");
                  Father father = new Son();//多态引用
                  father.method();  //按照多态,应该执行的是子类的重写的方法   此处不符合多态的特点
              }
          }
          
          class Father {
              public static void method() {
                  System.out.println("父类的静态方法");
              }
          }
          
          class Son extends Father {
              //尝试重写父类的静态方法  ,但是其实是子类自己的方法,不是重写父类的方法
              //@Override  注解的作用,是按照重写的标准来检查该方法是否符合重写的要求
              public static void method() {
                  System.out.println("子类的静态方法");
              }
          }
          
      • 2、属性

        • 属性不能被覆盖,因此也不符合多态的特征

        • 示例

          /*
           * 属性没有重写一说,没有多态
           */
          public class TestField {
              public static void main(String[] args) {
                  Dad d = new Dad();
                  System.out.println(d.name);//Dad
                  Baby b = new Baby();
                  System.out.println(b.name);//baby
                  Dad dd = new Baby();
                  System.out.println(dd.name);//Dad
              }
          }
          
          class Dad {
              String name = "Dad";
          }
          
          class Baby extends Dad {
              String name = "baby";
          }
          
      • 3、可变参数

        • 示例

          public class TestSub {
              public static void main(String[] args) {
              Base base = new Sub();//多态引用
                  base.add(1, 2, 3);//2,3已经按照数组去编译
          }
          }
          
          class Base {
              public void add(int a, int... arr) {
                  System.out.println("base");
              }
          }
          
          class Sub extends Base {
          public void add(int a, int[] arr) {
                  System.out.println("sub_1");
          }
          
          public void add(int a, int b, int c) {
                  System.out.println("sub_2");
              }
          }
          

五. Java常用类

java.lang.Object

  • 它是所有类的根父类

    • 1、它当中声明的方法,在所有引用类型中都有

    • 2、所有类创建对象,都会调用它的无参构造

    • 3、它的变量可以指向任意类型的对象

      • 多态引用
  • 常见的方法

    • 1、getClass()

      • 返回某个对象的运行时类型
    • 2、equals(Object obj)

      • 默认,从Object继承的,表示比较两个对象地址,等同于==

      • 如果这个类的两个对象的比较有“特殊的逻辑”时,需要重写这个方法,可以比较两个属性的值

        • 例如:String,Date,File等
      • 一旦重写equals,也要重写hashcode方法

        • 要保证:equals返回true的两个对象,它们的hashcode值必须相等

          • y=f(x)

            • x1=x2,y1=y2
    • 3、hashcode()

      • 必须和equals一起重写,参与equals比较的属性,一定要参与hashcode值的计算

      • 31

      • 因为它是质数,素数,通过它计算的hashcode值可以最大程度的保证不相同

        • 如果两个对象的hashcode值不相等,这两个对象一定是不相等

          • y=f(x)

            • y1!=y2,得出x1!=x2
        • 如果两个对象的hashcode值相等,不能决定这两个对象不相等

          • y=f(x)

            • y1=y2,不能推出x1=x2
          • 所以,两个对象hashcode值相等时,还需要调用equals方法继续判断

    • 4、toString()

      • 默认,从Object继承的,返回 类名@hashcode的无符号十六进制形式
      • 如果需要,就重写

包装类

  • 为什么要使用包装类

    • 1、在程序中某些位置,例如(集合,泛型)等不能使用基本数据类型,因此需要把基本数据类型转换成对象类型
    • 2、但是基本数据类型,又节省空间又计算速度快,所以保留基本数据类型
  • 包装类

    • byte

      • Byte
    • short

      • Short
    • int

      • Integer
    • long

      • Long
    • float

      • Float
    • double

      • Double
    • char

      • Character
    • boolean

      • Boolean
  • 装箱

    • 手动

      • int a = 12; Integer i = new Integer(a);
    • 自动

      • Integer i = 12;
  • 拆箱

    • 手动

      • int a = new Integer(12).intValue();
    • 自动

      • int a = new Integer(12);
  • 包装类的其他的应用

    • 1、基本数据类型与字符串之间的转换

      • 基本数据类型-->String

        • +" "
      • String-->基本数据类型

        • 例如:int age = Integer.parseInt("23");
        • 例如:Integer age = Integer.valueOf("23");
        • 例如:Integer age = new Integer("23");
    • 2、获取数据范围

      • 例如:Integer.MAX_VALUE
    • 3、Character可以转某个字符的大小写

      • Character.toLowerCase(char)
      • Character.toUpcase(char)

Scanner类

  • 1、导包 import java.util.Scanner;

  • 2、创建Scanner类的变量 Scanner input = new Scanner(System.in);

  • 3、使用Scanner中的方法

    • 字符串 String name = input.next();
    • 整型 int age=input.nextInt();
    • 浮点型 double score = input.nextDouble();
    • 字符型 char sex = input.next().charAt(0);

六. 异常处理

什么是异常

  • 不是语法错误
  • 不是逻辑错误
  • 而是在程序运行中,一些不可控的因素,不期而至的各种状况,导致程序运行异常.
  • 例如:文件不存在,网络中断,非法输入等

异常的处理机制的原理,过程

  • Java程序在某句代码处发生了异常,JVM会new合适的异常对象,并把异常的信息封装到对象中,然后抛出,如果该代码没有环绕try...catch块,那么会先向上(调用者)抛出,如果上级也没有处理,那么一直往上抛,最终可能到达JVM,导致程序中断。如果其中有一个部分将这个异常对象“捕获”,程序继续进行,不会中断。

异常的体系结构

  • 1、java.lang.Throwable

    • Java 语言中所有错误或异常的超类。
    • (1)只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
    • (2)只有此类或其子类之一才可以是 catch 子句中的参数类型。
  • 2、java.lang.Error

    • 用于指示合理的应用程序不应该试图捕获的严重问题

      • 例如:VirtualMachineError虚拟机错误

        • StackOverflowError栈溢出
        • OutOfMemoryError内存溢出
  • 3、java.lang.Exception

    • 受检异常

      • 在编译阶段必须编写“异常处理”的代码,否则编译不通过

      • ParseException

      • IOException

        • FileNotFoundException
      • SQLException

    • 运行时异常

      • RuntimeException

        • 在编译阶段不要求强制编写“异常处理”的代码,但是希望程序员,尽量避免
        • ClassCastException
        • NullPointerException
        • ArrayIndexOutOfBoundsException
        • .......

异常的处理机制

  • 1、抛

    • (1)JVM自动抛

    • (2)throw异常对象;

      • (1)JDK中定义异常类型
      • (2)自定义异常类型
  • 2、抓

  • 体现为异常的处理方式

异常处理

  • 1、捕获处理

    • 语法

      try{
      可能发生异常的代码;
      }catch(异常的类型   对象名){
      异常的处理代码
      }....
      finally{
      无论是否发生异常都会运行的代码
      }
      
    • 说明

      • (1)try不可以单独存在,要么和catch,要么和finally一起使用

        • try{}catch(){}finally{}
        • try{}catch(){}
        • try{}finally{}
      • (2)catch可以多个,但是执行时,如果发生异常,只会进入其中一个

      • (3)多个catch()中的类型不能相同

        • 如果是"互斥"关系,某个异常不是继承关系

          • 顺序随意
        • 如果"包含"关系,异常之间有继承关系

          • 子上父下
      • (4)finally

        • 无论是否发生异常都会运行的代码

          • 一般编写释放资源的代码
        • 尽量避免在finally写return语句

  • 2、上报处理

    • throws

      • 语法格式

        • 【修饰符】 返回值类型 方法名(形参列表)throws 异常列表
      • 把异常抛给调用者处理

throw

  • 用于手动抛出异常对象
  • 可以抛出系统已定义异常,也可以抛出用户自定义异常
  • throw可以代替return语句结束方法,但是没有返回值

重写与异常列表

  • 重写方法的要求

    • 两同

      • 方法名与形参列表
    • 两小

      • 返回值类型

        • 基本数据类型与void:必须相同
        • 引用数据类型:子类的方法的返回值类型可以是父类被重写方法的返回值类型的子类
      • 异常类型

        • 子类抛出的异常类型 要么和父类被重写方法抛出的异常一样,要么是父类被重写方法抛出的异常的子类
    • 一大

      • 权限修饰符

        • 子类方法的权限修饰符 >= 父类被重写方法的权限修饰符
        • public > protected>缺省>private

finally与return都存在

  • 没有异常

    • try --> [返回值赋值] --> finally --> 结束方法
  • 有异常

    • try --> catch --> [返回值赋值] --> finally --> 结束方法

自定义异常

  • 是一个类,必须继承Throwable或它的子类

  • 必须通过throw才能抛出

  • 示例:

    public class MyException extends Exception {
        /**
       * 序列版本号唯一的标识当前类
         */
        private static final long serialVersionUID = 6650357798030558092L;
    
        public MyException() {
        }
        public MyException(String msg) {
            super(msg);
      }
    }
    
0

打赏

海报

正在生成.....

评论 (0)

语录
取消