Java语言基础

2023-10-29

文章目录

1. 开发环境

1.1. 认识开发环境JDK

  1. JDK-Java Development Kit (Java开发工具包),是开发Java程序使用的工具包,可以编译运行Java程序
  2. Java开发工具:用于编译运行Java程序工具命令
  3. JRE-Java Runtime Environment(Java运行环境):是Java程序运行的最小环境
  4. 系统API:Java运行时候用的基础核心组件
  5. JVM-Java Virtual Machines(Java虚拟机):用于执行Java字节码

1.运行Java程序需要使用JRE
2.开发Java程序需要使用JDK

1.2. JDK、JRE、JVM关系


1.3. JDK1.8本地安装目录

1.3.1. bin

该路径下存放了JDK的各种工具命令

  1. java.exe:运行工具
  2. javac.exe:编译工具

1.3.2. include

该路径下存放了一些平台特定的头文件

1.3.3. lib

该路径下存放了JDK工具的一些补充JAR包

1.3.4. conf

在1.8环境下没有,之前的版本有这个文件
该路径下存放了JDK的相关配置文件

1.3.5. src

source源码目录

1.4. JDK1.8环境配置

1.4.1. 配置JAVA_HOME

  1. 新建系统变量,变量名输入:JAVA_HOME
  2. 变量值输入JDK8的安装目录(根据自己jdk1.8所在的目录)

1.4.2. 配置Path

  1. 双击编辑系统变量中的Path

  1. 检查环境

快捷键’Ctrl+R’在运行窗口键入’cmd’在弹出来的命令提示符窗口中输入’java -version’回车后出现以下内容即配置成功

1.5. DOC命令

  1. dir:查看当前路径下的内容
  2. cd 目录:进入单级目录
  3. cd..:回退到上一级目录
  4. cd 目录1/目录2:进入多级目录
  5. cd /:回退到盘符目录
  6. cls:清屏
  7. exit:退出命令提示符窗口

2. IntelliJ IDEA

2.1. 安装

  1. ideaIU-2020.1.1.exe

  2. IDEA安装教程(多图预警)

  3. IntelliJ IDEA 下载安装配置教程(完整版)

2.2. 快捷键

  1. 十大Intellij IDEA快捷键

  2. Idea快捷键大全(Windows)

  3. 史上最全的IDEA快捷键总结

2.3. 插件

  1. 没用过这些 IDEA 插件?怪不得写代码头疼

  2. IntelliJ Idea 常用12款插件(提高开发效率),附优秀主题插件

  3. 值得推荐的Idea十几大优秀插件

  4. 晚上不用加班了,推荐十款精选IntelliJIdea插件,效率提升N倍

2.4. 主题&字体

  1. 设置 IntelliJ IDEA 主题和字体的方法

3. 开发Java程序

3.1. 开发三步骤

3.2. 命令行窗口运行HelloWorld

public class HelloWorld{
	public static void main(String[] args){
		System.out.println("Hello World!");
	}
}

在命令行窗口输入javac HelloWorld.java编译成HelloWorld.class字节码文件,然后再用java HelloWorld运行java字节码文件

3.2.1. 程序解析

第一行:public表示权限公共的意思,外部可以随意调用和访问,class表示类,HelloWorld就是类的名字,此行代码表示的含义:创建了一个名为HelloWorld的权限公开的Java类
第二行:main叫做方法名(或者叫做函数名),static表示
的,也就是只有一份,void表示无返回值,此行代码的含义:main方法是属于当前类HelloWorld的,只要这一份,且执行后没有值返回
第三行:输出语句,系统调用内部输出方法,打印出双引号中的内容

4. 注释

4.1. 单行注释

以双斜杠//标识,只能注释一行内容,用在注释信息内容少的地方

4.2. 多行注释

包含在/*和*/的之间,为了可读性比较好,一般首行和尾行不写注释信息(美观)

4.3. 文档注释

包含在/** 和 */之间,也能注释多行内容,一般用在类、方法和变量上面,用来描述其作用。注释后,鼠标放在类和变量上面会自动显示出来我们注释的内容

5. 关键字

所有关键字都是小写的

5.1. 数据类型

booleanintlongshortbytefloatdoublecharenumclassinterface

5.2. 流程控制

ifelsedowhileforswitchcasedefaultbreakcontinuereturntrycatchfinallyassert

5.3. 修饰符

publicprotectedprivatefinalvoidstatic、strict、abstracttransientsynchronizedvolatilenative

5.4. 动作

packageimportthrowthrowsextendsimplementsthis、supper、instanceofnew

5.5. 直接量

truefalsenull

5.6. 保留字

gotoconst

6. 标识符

6.1. 规则

  1. 标识符不能以数字作为首字母的开头
  2. 不能使用Java关键字或保留字
  3. Java严格区分大小写
  4. 首字母必须是字母、下划线_、美元符号$
  5. 标识符由数字、大写字母、小写字母、下划线、美元符号、人民币符号以及所有在十六进制0xc0前的ASCII码组成

6.2. 命名规范

  1. 标识符需要做到英文的“见名知意”
  2. 大驼峰命名法:首字母大写,后面组合的每个字母首字母大写–类名
  3. 小驼峰命名法:首字母小写,后面组合的每个字母首字母大写–方法名
  4. 变量命名规范:全部小写

7. 常量

常量是指在程序的整个运行过程中值保持不变的量

7.1. 定义常量

常量不同于常量值,它可以在程序中用符号来代替常量值使用,因此在使用前必须先定义。常量与变量类似也需要初始化,即在声明常量的同时要赋予一个初始值。常量一旦初始化就不可以被修改final int num = 10

7.1.1. 注意

  1. 在定义常量时就需要对该常量进行初始化
  2. final关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法
  3. 为了与变量区别,常量取名一般都用大写字符

7.2. 分类

类型 含义 数据举例
整数常量 所有的整数 0,1,567
小数常量 所有的小数 0.0,2.55
字符常量 单引号引起来,只能写一个字符,必须有内容 ‘a’,’ ',‘好’
字符串常量 双引号引起来,可以写多个字符,也可以不写 “A”,“Hello”
布尔常量 只有2个值 true,false
空常量 只有一个值 null

7.3. 常量值

常量值又称为字面常量,它是通过数据直接表示的,因此有很多种数据类型,像整型和字符串型等

7.4. 常量和常量值的区别

  1. 常量值是常量的具体和直观的表现形式
  2. 常量是形式化的表现
  3. 通常在程序中既可以直接使用常量值,也可以使用常量

8. 变量

变量是指在程序的整个运行过程中值可以发生改变的量

Java是强类型语言:

  1. 所有的变量必须先声明,后使用
  2. 指定类型的变量只能接受类型与之匹配的值

8.1. 变量作用域

变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。

8.1.1. 成员变量

Java的成员变量有两种,分别是全局变量和静态变量(类变量)。定义在方法体和语句块之外不属于任何一个方法,作用域就是整个类。
全局变量是有默认值的,而局部变量没有默认值,所以使用前一定要进行初始化

名称 修饰 访问 生命周期
全局变量(实例变量) 无static修饰 对象名.变量名 只要对象被当作引用,实例变量就将存在
静态变量(类变量) 用static修饰 变量名或类名.变量名或对象名.变量名 其生命周期取决于类的生命周期,类被垃圾回收机制彻底回收时才会被销毁

[admonition title=“注意” color=“red”]之间访问变量名需要在本类,最好的最规范的使用是类名.变量名[/admonition]

/*
 *全局变量(实例变量):
 * 作用范围:在类的内部,方法之外
 * static关键字
 *	修饰方法,则该方法是静态的,静态的就是表示只有一份,属于类,无论有多少个对象,都是对这一个进行修改
 *	修饰变量(属性),则该变量是静态的,表示只有一份属于类
 */
public class GlobalVariable {
//    创建了一个成员变量age【全局变量(实例变量)、静态变量】
    int age = 21; // 创建了一个全局变量
    static int age1 = 19;
    public static void main(String[] args){
//        使用全局变量age
//        访问方式:对象.全局变量
        GlobalVariable gv = new GlobalVariable();
        gv.age = 18;
        System.out.println(gv.age);
//        静态变量的访问
//        访问方式:类名.变量名或对象名.变量名
        GlobalVariable.age1 = 30;
        System.out.println((GlobalVariable.age1));
    }
}

8.1.2. 局部变量

局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块

/*
 * 局部变量
 * 变量的作用范围是部分,而非类的全部
 *
 */
public class LocalVariable {
//    main方法,有参数String数组,变量名是args,所有args的作用范围是整个main方法内容
    public static void main(String[] args){
        LocalVariable lv = new LocalVariable();
        sum1();
    }
//    sum方法的参数中,变量a和变量b的作用范围是这个方法内部
    public static void sum(int a, int b){
        a = 0;
        b = 1;
    }
//    sum方法的参数中,变量a和变量b的作用范围是这个方法内部
    public static void sum1(){
        int a = 1;
        int b = 0;
        System.out.println(a);
        System.out.println(b);
    }
//    面试题:代码块是属于对象还是属于类?
//    代码块是属于对象的,当创建对象,代码块开始运行
    {
        int a = 0;
        int b = 1;
        System.out.println(a);
        System.out.println(b);
    }

}

8.1.2.1. 方法参数变量(形参)

在整个方法内有效

8.1.2.2. 方法局部变量(方法内定义)

从定义这个变量开始到方法结束这一段时间内有效

8.1.2.3. 代码块局部变量(代码块内定义)

从定义这个变量开始到代码块结束这一段时间内有效

9. ASCII编码

百度百科

9.1. 位

计算机存储信息的最小单位,称之为位(bit),音译比特,二进制的一个"0"或一个"1"叫一位

9.2. 字节

字节(Byte)是一种计量单位,表示数据量多少,它是计算机信息技术用于计量存储容量的一种计量单位,8个二进制位组成1个字节。在ASCII码中,一个标准英文字母(不分大小写)占一个字节位置,一个标准汉字占2个字节位置。

9.3. 字符

字符是指计算机中使用的文字和符号

10. 小结

10.1. Java如何实现跨平台性

  1. Java语言具有很强语言规范,对于不同平台,数据类型的存储体现是一致的。C++/c中整数类型的长度是按照站位宽度来决定的,int整数类型:
    • 16位 2字节
    • 32位 4字节
    • 64位 8字节
      Java中,int整数类型就占4个字节
  2. 统一生成.Class文件,各种不同平台的虚拟机使用了统一的程序存储格式,就是指字节码(ByteCode),Java的虚拟机只与字节码的class文件进行交互,所以就出现了一次编译,到处使用的结果
  3. Java虚拟机的存在,可以将class文件转换为对应平台的二进制文件,Java平台的无关性是建立在Java虚拟机的平台有关性基础上的。Java虚拟机是屏蔽了底层操作系统和硬件之间的差异和关系。

10.2. JDK/JRE/JVM三者间的关系

JDK:Java development kit Java开发工具:开发Java应用程序的最小环境
JRE:Java Runtime Environment Java运行环境:运行Java应用程序的最小环境
JVM:Java Vritral Meschines Java虚拟机:解析和执行字节码文件的

大小关系:JDK>JRE>JVM

10.3. 电脑配置环境变量,2个必需的系统环境变量是什么?如何配置?

名:JAVA_HOME 值:jdk在系统盘中的存储路径
名:path 值:%JAVA_HOME%\bin

10.4. 变量和常量

10.4.1. 常量

在程序运行过程中不会发生改变的量

常量分类:

  1. 字符串常量:“123”、“a”、“中国”。双引号内可以没有东西
  2. 字符常量:‘a’、‘中’、’ '。单引号内必须有东西
  3. 整数型常量:没有小数点的数值 1、123
  4. 浮点型常量:含有小数点的数值 -0.2、3.14
  5. 布尔类型常量:只有两个值:true、false
  6. 空值类型常量:null 错误案例:Null、NULL

常量是由final所修饰的量
例如:final int A = 0;final static String NAME = "张三"

10.4.2. 变量

在程序运行的过程中可以发生改变的量

String info = "Hi,codewing23"; // 变量info声明并初始化
info = "你好,张三" // 对变量info进行修改
System.out.println(info);
10.4.2.1. 成员变量

定义在类里边,方法和代码块外部的变量

全局变量(实例变量)和静态变量(类变量:只有一份任何一个对象改变变量的值都会影响其他对象的访问)

10.4.2.2. 局部变量

定义在方法内部、定义在方法的参数中、定义在代码块中的变量

11. Java开发手册(嵩山版)

代码规范的必看手册

Java开发手册(嵩山版).pdf

12. Java数据类型

Java语言支持的数据类型分为两种基本数据类型引用数据类型

12.1. 基本数据类型

包含整数(四个)、浮点数(两个)、字符、布尔

类型名称 关键字 占用内存 取值范围
字节型 byte 1字节 -128~127
短整型 short 2字节 -32768~32767
整型 int 4字节 -2147483648~2147483647
长整型 long 8字节 -9223372036854775808L~9223372036854775807L
单精度浮点型 float 4字节 +/-3.4E+38F(6~7个有效位)
双精度浮点型 double 8字节 +/-1.8E+308(15个有效位)
字符型 char 2字节 ISO单一字符集 0~65536
布尔型 boolean 1字节 true或false

char代表字符型,实际上字符型也是整数类型,相当于无符号整数

###类型默认值

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘u0000’
String(or any object) null
boolean false

整数默认int类型,浮点数默认double类型,想用float得加f(大小写均可)

12.2. 引用数据类型

包含类、数组、接口

12.3. 自动类型转换

整型、实型(常量)字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。

12.4. 强制类型转换

  1. 条件是转换的数据类型必须是兼容的
  2. 格式:(type)value 其中:type是要强制类型转换后的数据类型

[alert color=“red”]注意[/alert]

  1. 不能对boolean类型进行类型转换
  2. 不能把对象类型转换成不相关类的对象
  3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换
  4. 转化过程中可能导致溢出或者损失精度,例如:int i = 128; byte b = (byte)i
  5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入。例如:(int)23.7 == 23; (int)-45.89f == -45
public class Demo {
    public static void main(String[] args) {
        int a = 3;
        int b = (int)(a + 2.3); // 而在C++中直接int b = a + 2.3;
        System.out.println(b);
    }
}

13. 算数运算

13.1. 四则运算

运算符 -
+ 加法运算,字符串连接运算
- 减法运算
* 乘法运算
/ 除法运算
% 取模运算,两个数字相除取余数
++、– 自增自减运算

13.2. 赋值运算符

操作符 描述 例子
= 简单的赋值运算符,将右操作数的值赋值给左侧操作数 C=A+B将把A+B的值赋给C
+= 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 C+=A等价于C=C+A
-= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C-=A等价于C=C-A
*= 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C*=A等价于C=C*A
/= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C/=A,C与A同类型时,等价于C=C/A
(%) 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%=A等价于C=C%A
<<= 左位移赋值运算符 C<<2等价于C=C<<2
>>= 右位移赋值运算符 C>>2等价于C=C>>2
&= 按位与赋值运算符 C&=2等价于C=C&2
^= 按位异或赋值操作符 C=2等价于C=C2
|= 按位或赋值操作符 C|=2等价于C=C|2
public class Demo {
    public static void main(String[] args) {
        byte a = 20;
        a += 10;
        /*
        a = byte + int
        a = int + int
        a = int;
        a = (byte)int;
         */
        System.out.println(a);
    }
}

[admonition title=“注意” color=“red”]混合赋值运算中隐含了一个强制类型转换![/admonition]

13.3. 关系运算符

运算符 描述
== 检查如果2个操作数的值是否相等,如果相等则条件为真
!= 检查如果2个操作数的值是否相等,如果值不相等则条件为真
> 检查左操作数的值是否大于右操作数的值,如果是那么条件为真
< 检查左操作数的值是否小于右操作数的值,如果是那么条件为真
>= 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真
<= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真

13.4. 逻辑运算符

操作数 描述
&& 称为逻辑运算符。当且仅当2个操作数都为真,条件才为真
|| 称为逻辑或操作符。如果任何2个操作数任何一个为真,条件为真。
! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。

13.5. 位运算

Java定义了位运算符,应用于整数类型(int)、长整型(long)、短整型(short)、字符型(char)、和字节型(byte)等类型。

定义:int A = 60; int B = 13;

操作符 描述 例子
& 如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即00001100
| 如果相对应位都是0,则结果为0,否则为1 (A|B)得到61,即00111101
^ 如果相对应位值相同,则结果为0,否则为1 (A^B)得到49,即00110001
~ 按位取反运算符翻转操作数的每一位,即0变成1,1变成0 (~A)得到-61,即11000011
<< 按位左移运算符。左操作数按位左移右操作数指定的位数 A<<2得到240,即11110000
>> 按位右移运算符。左操作数按位右移右操作数指定的位数 A>>2得到15,即1111
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充 A>>>2得到15,即00001111

60的二进制是00111100左移2后为0011110000
右移2后为00001111

二进制运算是以每4位运算,不足要在前面补0

13.6. 条件运算符

条件运算符也被称为三元运算符,或者也叫三目运算符。
该运算符有3个表达式,格式:数据类型 变量名 = 布尔类型的表达式1 ? 表达式2 : 表达式3;

[alert color=“red”]注意[/alert]

  1. 三元运算中,等号左边类型和等号右边的类型必须一致!冒号两边的类型要么相同,要么能够兼容。
  2. 三元运算的结果必须要被使用。

13.7. 运算符的优先级

类别 操作符 关联性
后缀 ()[].(点操作符) ->
一元 a++,a– ->
一元 ++a,–a,~,! <-
乘性 */% ->
加性 ± ->
移位 >>,>>>,<< ->
关系 >,>=,<,<= ->
相等 ==,!= ->
按位与 & ->
按位异或 ^ ->
按位或 | ->
逻辑与 && ->
逻辑或 || ->
条件 ?: <-
赋值 =,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|= <-
逗号 , ->

14. 流程控制

14.1. 顺序结构

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
//        创建一个具有输入功能的对象 API Scanner
        Scanner scan = new Scanner(System.in);
        System.out.println("请您输入商品的数量:");
        int num = scan.nextInt();
        System.out.println("请您输入商品的价格:");
        double price = scan.nextDouble();
//        计算总价
        double total = num * price;
        System.out.println("商品的总价是:" +total);
//        付款
        System.out.println("请您付款:");
        double money = scan.nextDouble();
//        找零
        double change = money - total;
        System.out.println("找零:" + change);
    }
}

14.2. 分支结构

14.2.1. if

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
//        判断年龄是否大于18
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入您的年龄:");
        int age = scan.nextInt();
        int pre = 18;
        if(age < pre) {
            System.out.println("成年");
        } else {
            System.out.println("未成年");
        }
    }
}

14.2.2. switch

import java.util.Scanner;

public class Demo {
//    从JDK7.0开始。switch-case可以支持字符串表达式
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入值:");
        int num = scan.nextInt();
        switch(num) {
            case 1:
                System.out.println("1"); break;
            case 2:
                System.out.println("2"); break;
            case 3:
                System.out.println("3"); break;
            default:
                System.out.println("NULL"); break;
        }
    }
}

[alert color=“red”]注意[/alert]

  1. switch后面的整型表达式的值必须是整型(byte、short、int)或者字符型
  2. case后面的常量值必须不同
  3. switch语句的格式较为灵活,结果不受case顺序的影响,且break和default可以省略。

14.3. 循环结构

14.3.1. while

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入值:");
        int num = scan.nextInt();
        int pre = 10, i = 0;
        while(num < pre) {
            i ++;
            System.out.println("第" + i + "个值是:" + num);
            num ++;
        }
    }
}

14.3.2. do-while

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
//        让数字倒序输出
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入值:");
        int num = scan.nextInt();
        int pre = 0;
        do {
            pre = num % 10 + pre * 10;
            num /= 10;
        }while(num != 0);
        System.out.println("倒序后前方无0的值为:" + pre);
    }
}

14.3.3. for

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
//        求1-num的和
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入值:");
        int num = scan.nextInt();
        int res = 0;
        for(int i = 1; i <= num; i ++) {
            res += i;
        }
        System.out.println("1-num的和为:" + res);
    }
}

14.3.4. 循环嵌套:实现九九乘法表

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

15. 方法

方法用于封装一个特定的功能,定义时需要考虑5个要素:修饰词、返回值类型、方法名、参数列表、方法体。

[alert color=“red”]注意[/alert]

  1. 方法是并列关系,不能嵌套
  2. 方法是没有先后顺序的
  3. 如果一个方法要运行,一定要进行调用

[alert color=“blue”]标准格式[/alert]

/**
 * 修饰词 返回值类型 方法名称(参数列表) {
 *     方法体
 *     return 返回值
 * }
 * 修饰词:public static
 * 返回值类型:方法执行完之后,要给出的结果类型【基本数据类型,引用数据类型】
 * 方法名称:符合java标识符的规则。“见名知意”格式“小驼峰”命名法
 *
 */
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.print("请输入a:");
        int a = scan.nextInt();
        System.out.print("请输入b:");
        int b = scan.nextInt();
        int res = sum(a, b);
        System.out.println("两数之和为:" + res);
    }
    public static int sum(int a, int b) {
        return a + b;
    }
}

15.1. 方法的三种调用方式

15.1.1. 单独调用

方法名称(参数列表);

15.1.2. 打印调用

System.out.println(方法名称(参数列表))

15.1.3. 赋值调用

数据类型 变量名称 = 方法名称(参数列表)

只有定义的方法有返回值得时候才可以使用赋值调用

15.2. 方法的重载

/**
 * 方法的重载:对于功能类似的方法而言,方法名称又太多,会导致不必要的麻烦,所以提出了方法重载的概念!
 * 方法的重载:Overload
 *      多个方法名称相同,参数列表不同!
 *  注意:参数列表不同的体现
 *   1.参数个数的不同
 *   2.参数类型的不同
 *   3.多个参数类型的顺序不同
 *
 *  不可以通过方法的返回值来判断方法是否发生重载【方法的重载和方法的返回值无关】
 *  方法的重载和参数名称无关,只与参数列表相关
 *  方法的重载只与参数列表相关,和返回值无关
 */
public class Demo {
    public static void main(String[] args) {
        System.out.println(sum(1, 3));
    }
    public static int sum(int num1, int num2) {
        return num1 + num2;
    }
    public static double sum(double num1, int num2) {
        return num1 + num2;
    }
    public static double sum(int num2, double num1) {
        return num1 + num2;
    }
}

15.2.1. 方法重载的练习

public class Demo {
    public static void main(String[] args) {
        System.out.println(method(1L, 2L));
    }
//    两个byte
    public static boolean method(byte a, byte b) {
        return a == b;
    }
//    两个short
    public static boolean method(short a, short b) {
        if(a == b) {
            return true;
        }else {
            return false;
        }
    }
//    两个int
    public static boolean method(int a, int b) {
        boolean res;
        if(a == b) {
            res = true;
        }else {
            res = false;
        }
        return res;
    }
//    两个long
    public static boolean method(long a, long b) {
        boolean res = a == b ? true : false;
        return res;
    }
}

16. 数组

特点:

  1. 数组是引用数据类型
  2. 数组要求存储的数据一定相同数据类型
  3. 数组的长度在运行期间是不可以改变的

16.1. 数组初始化

注意:左边的数据类型要和右边的数据类型一致
静态和动态数组的初始化可以分两步进行即先声明后赋值,而静态省略格式不可以

16.1.1. 静态初始化(指定内容)

标准格式:数据类型[] 数组名 = new 数据类型[]{内容1, 内容2, 内容3,…};

public class Demo {
    public static void main(String[] args) {
        int[] arr = new int[]{10, 11, 12, 13};
        String[] arr1 = new String[]{"Hello", "Java", "C++"};
        System.out.println(arr1[1]);
		// 省略格式
		double arr2 = {0.1, 0.2, 0.3};
    }
}

16.1.2. 动态初始化

标准格式:数据类型 [] 数组名 = new 数据类型[大小]

public class Demo {
    public static void main(String[] args) {
        int[] arr = new int[100];
        String[] arr1 = new String[10];
        double[] arr2 = new double[20];
    }
}

16.2. 数组的访问

public class Demo {
    public static void main(String[] args) {
        int[] arr1 = new int[]{1, 3, 5};
        int len = arr1.length;
        System.out.println("arr1的长度:" + len);
        System.out.println(arr1); //直接输出数组的名称得到的是数组内存的地址
        System.out.println(arr1[2]);
    }
}

16.3. 数组中常见的异常

16.3.1. 下标越界

public class Demo {
    public static void main(String[] args) {
        int arr[] = new int[3];
        System.out.println(arr[3]); // ArrayIndexOutOfBoundsException:3下标越界
    }
}

16.3.2. 空指针

public class Demo {
    public static void main(String[] args) {
        int[] arr = null;
        System.out.println(arr[0]); // NullPointerException空指针异常
    }
}

16.4. 数组的遍历

public class Demo {
    public static void main(String[] args) {
        int n = 10;
        int[] arr = new int[n];
        for(int i = 0; i < n; i ++) {
            arr[i] = i + 1;
        }
        for(int i = 0; i < n; i ++) {
            System.out.println(arr[i]);
        }
    }
}

16.5. 数组的最值

16.5.1. 增强的for循环

public class Demo {
    public static void main(String[] args) {
        int[] arr = {119, 21, 34, 45, 52};
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int value : arr) {
            if (value > max) {
                max = value;
            }
            if (value < min) {
                min = value;
            }
        }
        System.out.println("数组中的最大值为:" + max);
        System.out.println("数组中的最小值为:" + min);
    }
}

16.6. 内存管理

Java虚拟机要运行程序,必须要对内存进行空间的分配和管理

区域名称 作用
寄存器 给CPU使用,和我们开发无关
本地方法栈 JVM在使用操作系统功能的时候使用,和我们开发无关
方法区 存储可以运行的class文件【包含成员方法的信息、成员变量的信息】
堆内存 存储对象或者数组,new来创建的,都存储在堆内存
栈内存 方法运行时使用的内存,比如main方法,进入方法栈中进行

[alert color=“red”]注意[/alert]

  1. 堆内存中的内容,都有一个使用16进制表示的地址值,且里面存储的数据,都是有默认值的
  2. 栈内存中存放的是局部变量(方法内部声明的变量、方法的参数),方法运行也是在栈中完成的

[alert color=“blue”]Java虚拟机运行时数据区[/alert]

16.6.1. 内存分配

Java内存分配全面浅析

16.7. 数组作为方法参数传递

public class Demo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        method(arr);
    }
    public static void method(int[] arr) {
        System.out.println("接收到的数组是:" + arr);
    }
}

16.8. 数组作为方法的返回值

三要素:
1. 返回值类型:int/int/int[]
2. 方法名称:getMax/getMin/method
3. 参数列表:int[] arr

import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
        int[] arr = {370, 440, 458, 1902};
        int max = getMax(arr);
        int min = getMin(arr);
        int[] arr1 = method(arr);
        System.out.println("数组中的最大值为:" + max);
        System.out.println("数组中的最小值为:" + min);
//        Arrays表示是数组的操作类,内部含有一个输出所有内容的方法toString
        System.out.println(Arrays.toString(arr1));
    }
//    方法1:求最大值
    public static int getMax(int[] arr) {
        int max = Integer.MIN_VALUE;
        for(int value : arr) {
            if(max < value) {
                max = value;
            }
        }
        return max;
    }
//    方法2:求最小值
    public static int getMin(int[] arr) {
        int min = Integer.MAX_VALUE;
        for(int value : arr) {
            if(min > value) {
                min = value;
            }
        }
        return min;
    }
//    方法3:最大值、最小值、平均值
    public static int[] method(int[] arr) {
        int max = getMax(arr);
        int min = getMin(arr);
        int res = 0;
        for(int value : arr) {
            res += value;
        }
        int avg = res / arr.length;
//        int arr1 = {};
//        return arr1;
//        要用之前的数组不能省略,要new一个对象
        arr = new int[]{max, min, avg};
        return arr;
    }
}

16.9. 数组的复制

数组的扩容
数组的长度一旦被声明固定,在运行期是不可以被改变
采用"倒手的动作",将原数组进行了复制,前提是新声明的数组长度要比原来的数组的长度至少大1

16.9.1. 传统方式

import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
//        原数组
        int[] arr1 = {1, 2, 3, 4, 5};
//        新数组
        int[] arr2 = new int[arr1.length + 1];
        System.out.println(Arrays.toString(arr1));
        System.out.println(Arrays.toString(arr2));
        System.out.println("=================");
        for (int i = 0; i < arr1.length; i++) {
            arr2[i] = arr1[i];
        }
//        为最后一个元素进行赋值
        arr2[arr2.length - 1] = 3;
        System.out.println(Arrays.toString(arr2));
    }
}
16.9.1.1. 代码封装

ctrl+alt+m

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bo8huzk-1640435871934)(https://cdn.jsdelivr.net/gh/zhangying458/CDN/blog/Java-base/amgtv-nlovy.webp)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uCjz2zM4-1640435871934)(https://cdn.jsdelivr.net/gh/zhangying458/CDN/blog/Java-base/afbym-uk3h9.webp)]

import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
//        原数组
        int[] arr1 = {1, 2, 3, 4, 5};
        method1(arr1);
    }

    public static void method1(int[] arr1) {
        //        新数组
        int[] arr2 = new int[arr1.length + 1];
        System.out.println(Arrays.toString(arr1));
        System.out.println(Arrays.toString(arr2));
        System.out.println("=================");
        for (int i = 0; i < arr1.length; i++) {
            arr2[i] = arr1[i];
        }
//        为最后一个元素进行赋值
        arr2[arr2.length - 1] = 3;
        System.out.println(Arrays.toString(arr2));
    }
}

16.9.2. System.arraycopy

import java.util.Arrays;

/**
 * public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);
 * 其中:
 *  .native 是一个修饰词,表示该方法是一个本地方法【跟jvm进行交互的方法】,使用了java的代码调用了非java的功能接口【了解】
 *  .src:原数组arr1
 *  .srcPos:原数组的起始位置 0
 *  .dest:目标数组arr2
 *  .destPos:目标数组的起始位置
 *  .length:要复制的长度
 */
public class Demo {
    public static void main(String[] args) {
//        原数组
        int[] arr1 = {1, 2, 3, 4, 5};
//        新数组
        int[] arr2 = new int[arr1.length + 1];
        System.arraycopy(arr1, 0, arr2, 0, arr1.length);
        arr2[arr2.length - 1] = 3;
        System.out.println("复制之后的数组:" + Arrays.toString(arr2));
    }
}

16.9.3. Arrays.copy

import java.util.Arrays;

/**
 * public static int[] copyOf(int[] original, int newLength) {
 *      int[] copy = new int[newLength];
 *      System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
 *      return copy;
 *  }
 * .original:表示是要被复制的数组arr1
 * .newLength:新的长度【如果新的长度比原长度大,表示扩容;如果长度比原长度小,表示缩容】
 */
public class Demo {
    public static void main(String[] args) {
//        原数组
        int[] arr1 = {1, 2, 3, 4, 5};
//        新数组
//        int[] arr2 = Arrays.copyOf(arr1, arr1.length + 1);
//        System.out.println(Arrays.toString(arr2));
        arr1 = Arrays.copyOf(arr1, arr1.length + 1); // 生成的新的数组,栈中arr1指向的地址发生变化
        System.out.println(Arrays.toString(arr1));
    }
}

17. 面向对象

17.1. 简述

面向对象编程(Object-oriented programming,简称OOP)是指以软件中的"对象个体"为思考方向的编程思想。这里的对象个体泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。它区别于面向过程思想,强调是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

举例:

  1. 吃饭

面向过程:买菜->洗菜->切菜->炒菜->吃菜
面向对象:餐馆->下单->吃菜

  1. 洗衣服

面向过程:衣服->放盆->放洗衣粉->加水->浸泡->揉搓->清洗衣服->拧干->晾
面向对象:衣服->打开全自动洗衣机->扔衣服->按钮->晾起来

[alert color=“blue”]核心思想[/alert]

复用、可以扩展

[alert color=“red”]三大特征[/alert]

  1. 封装(Encapsulation)
  2. 继承(Inheritance)
  3. 多态(Polymorphism)

17.2. 类和对象

17.2.1. 类

是一组相关属性行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。

现实中,描述一类事物:
属性:就是该事物的状态信息
行为:就是该事物能够做什么

举例:小猫

  1. 属性:名字、体重、年龄、颜色
  2. 行为:走、跑、叫

17.2.2. 对象

是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为

现实中,一类事物的一个实例:一只小猫

举例:一只小猫

  1. 属性:tom、5kg、2years、yellow
  2. 行为:溜墙根走、蹦跶的跑、喵喵叫

17.2.3. 类与对象的关系

  1. 类是对一类事物的描述,是抽象的
  2. 对象是一类事物的实例,是具体的
  3. 类是对象的模板,对象是类的实体

17.2.4. 类的创建

public class Emp {
    /**
     * 属性:成员变量
     */
    String name;
    int age;
    double salary;
    int emp;

    /**
     * 方法:成员方法
     */
    public void eat() {} // 吃
    public void run() {} // 跑
    public void work() {} // 工作

}

17.2.5. 对象的创建和访问

/**
 * 类:
 *      描述事物的一个集合,涵盖了事物的属性特征和行为特征
 *      属性特征:是描述事物的状态(是什么)
 *      行为特征:是描述事物的行为(能做什么)
 * 对象:
 *      是类的一个具体实例
 * 关系:
 *      类是抽象的,对象是具体的
 *      类是对象的模板,对象是类的实例
 */
public class Demo {
    public static void main(String[] args) {
//        String name = "张三";
//        int age = 21;
//        double salary = 2.0e4;
//        int emp = 12;
        /*
            对象的创建:就是实例化类的过程
            数据类型:8种基本数据类型;引用数据类型
            格式:
                数据类型 变量名称 = new 数据类型();
         */
        // 访问属性,通过创建好的对象的引用(变量名称),去打点访问成员属性
        Emp emp1 = new Emp();
        emp1.name = "李四";
        emp1.age = 19;
        emp1.salary = 1.0e4;
        emp1.emp = 20;
        println(emp1);
        // 通过通过创建好的对象的引用(变量名)去打点访问成员方法
        emp1.eat(emp1);
        emp1.run(emp1);
        emp1.work(emp1);
    }

    private static void println(Emp emp1) {
        System.out.println(emp1.emp + "号员工" + emp1.name + "现在月薪" + emp1.salary + ",听说是搞Java的,才" + emp1.age + "岁就这么厉害!");
    }

    /**
     * 专门用来打印输出员工信息
     * @param emp 员工的工号
     * @param age 员工的年龄
     * @param name 员工的姓名
     * @param salary 员工的薪水
     */
    public static void println(int emp, int age, String name, double salary) {
        System.out.println(emp + "号员工" + name + "现在月薪" + salary + ",听说是搞Java的,才" + age + "岁就这么厉害!");
    }
}

17.3. 总结

17.3.1. 定义类的总结

  1. 定义类:就是定义类的成员,包括成员变量和成员方法
  2. 定义成员变量:和以前定义变量是一样的,只不过位置发生了变化,现在是再类中,方法外
  3. 定义成员方法:和以前定义方法几乎是一样的,只不过是把static去掉

17.3.2. 使用对象的总结

创建对象:

  • 数据类型 对象名 = new 数据类型();

new的作用:

  • 创建对象的过程中才会使用,用来开辟内存空间的,到底开辟多大算,这个跟当前的数据类型还有jvm的特性相关。

使用对象访问类中的成员:

  • 对象名.成员变量
  • 对象名.成员方法();

17.4. 练习

/**
 * 创建了一个名为Student的类
 */
public class Student {
    // 成员变量
    String name; // 学生名称
    int stuId; // 学生学号
    int age;

    // 成员方法
    public void study() {
        System.out.println("学习!");
    }
    public void run() {
        System.out.println("跑步!");
    }

    public static void main(String[] args) {
        // 创建Student类的对象
        // 数据类型 对象名 = new 数据类型();
        Student stu = new Student();
        System.out.println(stu.age);
        System.out.println(stu.name);
        System.out.println(stu.stuId);
        stu.run();
        stu.study();
    }
}

17.5. 不同包间的访问

// import表示声明或者导入,也就是所需的类(Demo)导入当前(StudentTest)类下
// 这是功能声明的第一种方式,就是在当前类的前面进行import声明
import com.baidu.Demo;

public class StudentTest {
    public static void main(String[] args) {
        // 要访问Student类的信息(成员变量、成员方法)需要实例化一个对象
        Student stu = new Student();
        // 访问对象的信息(成员变量、成员方法)
        stu.age = 21;
        stu.name = "张三";
        stu.stuId = 7;
        System.out.println(stu.age);
        System.out.println(stu.name);
        System.out.println(stu.stuId);
        stu.run();
        stu.study();
		Demo demo = new Demo();
		// 第二种声明方式,不推荐使用每次建对象都需要写包名
		// com.baidu.Demo demo = new com.baidu.Demo();
    }
}

17.6. 对象作为方法参数传递

public class StudentTest {
    public static void main(String[] args) {
        // 要访问Student类的信息(成员变量、成员方法)需要实例化一个对象
        Student stu = new Student();
        // 访问对象的信息(成员变量、成员方法)
        stu.age = 21;
        stu.name = "张三";
        stu.stuId = 7;
        method(stu);
    }
    public static void method(Student stu) {
        System.out.println("接受到的是地址信息:" + stu);
        System.out.println(stu.name);
        System.out.println(stu.age);
        System.out.println(stu.stuId);
    }
 }

17.7. 对象作为方法返回值类型

返回值是对象的地址值

public class StudentTest2 {
    public static void main(String[] args) {
        Student s = getMethod();
        System.out.println(s.name);
        System.out.println(s.age);
        System.out.println(s.stuId);
    }
//    getMethod方法每调用一次,就会生成一个Student对象信息
    public static Student getMethod() {
        Student stu = new Student();
        stu.name = "张三";
        stu.stuId = 12;
        stu.age = 20;
        return stu;
    }
}

17.8. 垃圾回收机制

  垃圾回收器(Garbage Collection,GC)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用所指向的对象。【所谓的垃圾:就是在代码运行的过程中,存在的无用代码(是占内存,但是无法使用它)】

GC线程会从栈中的引用变量开始跟踪,从而判定哪些内容是正在使用的。若GC无法跟踪到某一块堆内存,那么这些就认为这块内存不再使用了即为可回收的。但是Java程序员不用担心内存管理,因为垃圾回收集器会自动进行管理。

17.8.1. Java程序的内存泄露问题(java.lang.OutOfMemoryError:Java heap space)

内存泄漏是指,不再被使用的内存没有被及时的回收,严重的内存泄漏会导致占用过多的内存从而导致程序崩溃,在程序中应该尽量避免不必要的内存浪费

GC线程判断对象是否可以被回收的依据是该对象是否有引用来指向,因此当确定该对象不再使用时,应该及时将其引用设置为null这样,该对象即不再被引用,属于可回收的范围。

17.8.2. System.gc()方法

GC的回收对程序员来说是透明的【这个功能Java成员独享】并不一定一发现有无用的对象就立即回收。一般情况下当我们需要GC线程即刻回收无用对象时,可以调用System.gc()方法。此方法用于建议JVM马上调度GC线程回收资源,但具体的实现策略取决于不同的JVM系统。

[alert color=“red”]注意[/alert]

  1. 不再使用的对象赋null值是必要动作
  2. 一般不会调用gc处理,因为即使我们调用了也不一定马上执行

17.9. 小结

import java.util.ArrayList;
import java.util.List;

/**
 * 类:是对事物的一个抽象
 *      修饰词 class 类名 {}
 *      public class Demo(){}
 * 对象:实例化一个抽象的事物【依据类来创建对象,类型是对象模板】
 *      数据类型 对象名[引用名] = new 数据类型();
 *      Demo demo = new Demo();
 * (1)成员变量
 *      数据类型 成员变量名;
 *      int age;
 *      String name;
 *      注意:成员变量是有默认值的
 * (2)成员方法
 *                     /签名 = 方法名+参数列表/
 *      修饰词 返回值类型 方法名(参数列表){}
 *      public void method(){}
 * 内存管理
 *      Jvm = 方法区、栈、堆
 *      方法区:
 *          存储.class相关的内容
 *          成员变量、成员方法【成员方法是有存储地址值的】
 *      栈【特点:先进后出】:main方法是最先进入的,也是最后关闭的
 *          存储的如果是基本数据类型,直接存储的是值
 *          存储的如果是引用数据类型,存储的是引用的地址值
 *          栈会位每一次执行的方法,开辟一块空间。名叫“栈帧”。生命周期:方法被调用时开始,到方法最后一句代码结束
 *          局部变量也存储在栈中【因为方法在栈中,所以局部变量也在】。生命周期:从方法创建时开始,到方法最后一句代码结束
 *      堆【new出来的】:
 *          存放的是对象的值【每一个存储的对象在堆中都是一个唯一的地址值和栈中的引用地址值一致】
 *          成员变量存储在堆中【因为对象在堆中,所以成员变量也在】。生命周期:从对象被创建开始,到变量回收为止
 *  gc();
 *      是系统基本的线程
 *      gc对程序员来讲是透明的,我们是可以通过建议JVM的方式来实现垃圾的回收:System.gc();
 *      什么是垃圾:
 *          没有引用所指向的对象,就会被视为垃圾
 *          Cell c = new Cell();
 *          c = null;
 *          此时,堆中存储的Cell对象就是一个垃圾!
 *          如果不可用的对象在堆中存储过多,就会出现内存溢出的现象java.lang.OutOfMemoryError:Java heap space
 *          所以,建议在某个对象使用之后,再也不会被用到的情况下,一定要进行null值处理[c=null;]
 */
public class Summary {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        int i = 0;
        while(true) {
            list.add(new byte[100 * 1024 * 1024]);
            System.out.println("分配次数:" + (++ i));
        }
    }
}

18. 访问控制

18.1. 包的概念

18.1.1. package语句

在Java语言中,命名冲突问题是用包(Package)的概念来解决的,也就是说,在定义一个类时,除了定义类的名称一般还要指定一个包的名称,定义包名的语法如下

// 类的全称:包名.类名
// package 包名;  全小写
package test;

public class Test {

}
// 上述的Test类的全称为:test.Test

  在命名包名的时候,包名可以有层次结构,在一个包中可以包含另外一个包,即:

// package 包名1.包名2....包名;
package test1.test2;
18.1.1.1. 规范

[alert color=“blue”]四个部分[/alert]

第一部分:公司或者组织域名的反写
第二部分:项目的名称
第三部分:模块名称
第四部分:类名

如下述定义可以分为4个部分,其中,StringUtil是类名,org.apache.commons.lang是多层包名,其含义如下:org.apache表示公司或组织的信息(是这个公司或组织域名的反写);commons表示项目的名称信息;lang表示模块的名称信息

org.apache.commons.lang.StringUtil

[alert color=“red”]注意[/alert]

  1. package代码一定位于所有代码的第一行
  2. package语句一个类中只能有一个声明

18.1.2. import语句

为了避免类名的冲突问题,在声明类时指定的包名,这时对该类的访问就需要使用如下所示的全称

org.whatisjava.core.Point p = new org.whatisjava.core.Point();

可以看到,如上的书写方式过于繁琐不便于书写,为了解决这个问题,可以通过import语句对类的全称进行声明import语句语法如下:

// import 类的全局限定名(即包名+类名)
import org.whatisjava.core.Point;

这样就可以直接通过类名来访问了
[alert color=“red”]注意[/alert]

  1. 有时在import语句中也可以使用"*"符号,例如:【一般不建议使用】
import org.whatisjava.core.*;
  1. 如果同一个包中的类大都有被使用的到,可以使用.*的形式进行导包,否则不建议使用,因为类被加载的时候,import导入的类都会被拿过来,有些是没有用到的,这样会造成JVM内存资源的浪费。

18.2. 封装特性

在软件系统中,常常通过封装来解决上面的问题。即:将容易变z的、具体的实现细节(卖水果或者钱)封装起来,外界不可访,而对外提供可调用的、稳定的能(店员或者柜员)这样的意义在于:

  1. 降低代码出错的可能性,更便于维护
  2. 当内部实现细节改变时,只要保证对外的功能定义不变,其他的模块不需要更改

面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

[alert color=“blue”]封装体现在两个方面[/alert]

  1. 方法的封装
  2. 关键字private

18.3. 修饰词private

18.3.1. private权限场景使用

18.3.1.1. public封装
18.3.1.2. private封装
/**
 * @author codeWing23
 * private修饰词的封装
 *      1. private可以修饰方法,也可以修饰成员变量,不可以修饰类
 *  所表示的意思是:私有的,不可以公开的,只能供本类使用
 *  如果使用private来修饰,那么该成员变量或者成员方法的使用范围只能是在本类中
 *  如果超出了本类就不能够直接访问,但是可以间接访问,提供了一对方法:getXXX(); setXXX(参数);
 *  对于set和get方法而言,是有一定的规则的
 *  get方法,不能有参数、有返回值类型和成员变量对应
 *  set方法,不能有返回值,有参数彩且类型和成员变量对应
 */
public class PermissionDemo {
    // 成员变量
    String name;
    private int age;
    // 提供一个对私有成员变量age的设置方法
    public void setAge(int num) {
        if(num < 0 || num > 120) {
            System.out.println("年龄不合法!");
        }else {
            age = num;
        }
    }
    // 提供一个对私有成员变量age的访问方法
    public int getAge() {
        return age;
    }
}

public class Test {
    public static void main(String[] args) {
        PermissionDemo permiss = new PermissionDemo();
        permiss.setAge(-60);
        System.out.println(permiss.getAge());
    }
}

18.3.2. private的练习

public class Teacher {
    // 成员变量 写修饰词的快捷方式:alt 拖动鼠标
    private String name;
    private int age;
    private boolean male;
    // 成员方法
    public String getName() {
        return name;
    }
    public void setName(String name1) {
        name = name1;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age1) {
        age = age1;
    }
    // 如果属性是boolean类型,一般get方法名称是写:isXXX();
    public boolean isMale() {
        return true;
    }
    public void setMale(boolean male1) {
        male = male1;
    }

}

public class PermissionDemo2 {
    public static void main(String[] args) {
        Teacher tea = new Teacher();
        tea.setName("张三");
        tea.setAge(20);
        tea.setMale(true);
        System.out.println(tea.getName());
        System.out.println(tea.getAge());
        System.out.println(tea.isMale());
    }
}

18.4. this关键字

/**
 * this:就是表示当前的类的对象,就是指当前的成员是由那个对象调用的
 * 格式:
 *      this.成员变量名
 * 注意:当成员变量和局部变量没有冲突的情况下,this是存在的,只不过没有显示出来,写出来不会编  * 译错误
 */
public class Teacher {
    // 成员变量 写修饰词的快捷方式:alt 拖动鼠标
    private String name;
    private int age;
    private boolean male;
    // 成员方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        // 就近原则,当方法的局部变量和类的成员变量名称相同时,优先使用局部变量
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age1) {
        age = age1;
    }
    // 如果属性是boolean类型,一般get方法名称是写:isXXX();
    public boolean isMale() {
        return true;
    }
    public void setMale(boolean male1) {
        male = male1;
    }

}

19. JavaBean规范

JavaBean是Java语言编写类的一种标准规范,符合JavaBean的类,要求类必须是具体的公共的,并且具有无参数的构造方法,提供用来操作成员变量的set和get方法

public class ClassName {
	// 数据私有,方法公开

	// 成员变量
	
	// 构造方法
	// 无参构造方法【必须】
	// 有参构造方法【建议】
	
	// 成员方法
	// getXXX();
	// setXXX();
}

20. 构造

20.1. 构造方法的使用

当一个对象被创建时,怎么去创建,依据的就是构造方法,且构造方法可实现该对象的初始化。

[alert color=“red”]注意[/alert]
无论你是否显示的定义构造方法,所有的Java类都是有默认的构造方法的,因为Java自动提供一个无参构造方法,值得注意的是,如果一旦自己定义了构造方法,Java自动提供的默认无参构造方法就会失效。

20.1.1. 构造方法的语法结构

构造方法是在类中定义的方法,但不同于其他的方法,构造方法的定义有如下两点规则:

  1. 构造方法的名称必须与类名相同
  2. 构造方法没有返回值,但也不能写void
public class Student {
	// 这个构造方法是无参的,无参构造方法是jdk默认提供的
	public Student() {
		
	}
	
	public static void main(String[] args) {
		// 创建当前类Student的对象
		Student stu = new Student();
	}
}

访问修饰符 类名(参数列表) {
	// 构造方法体
}

20.1.2. 构造方法

/**
 * 构造方法的特点:
 *      构造方法是专门用于创建对象的方法有以下两个特点
 *          1. 方法没有返回值类型
 *          2. 方法的名称和当前类名称相同
 * 格式:
 *      public 类名(参数列表) {
 *          
 *      }
 * 注意:
 *      1. 构造方法是可以发生重载的【重载发生在同一个类中,方法名相同,参数列表不同(顺序不同,个数不同,类型不同 )
 *      2. 如果没有撰写任何的构造方法,jdk会帮我们自动添加一个无参构造方法,一旦显示的添加一个构造方法,默认的失效
 *      3. 构造方法不能return一个具体返回值
 *      4. 构造方法不能有返回值类型,连void都不能写
 *      5. 构造方法的名称一定要和类名相同
 */
public class PermissionDemo3 {
    public static void main(String[] args) {
        Student stu = new Student(100);
        Student stu1 = new Student();
    }
}

public class Student {
    // 成员变量
    String name;
    int age;
    //构造方法
    // 如果自定义一个有参构造,则默认的无参构造将失效
    public Student(int a) {

    }

    public Student() {

    }
    // 成员方法
}

20.1.3. 构造方法的应用

/**
 * 构造方法的特点:
 *      (1)构造方法是专门用于创建对象的方法有以下两个特点
 *          1. 方法没有返回值类型
 *          2. 方法的名称和当前类名称相同
 *      (2)实现对象的初始化【指的是对对象成员变量进行赋初始值】
 * 格式:
 *      public 类名(参数列表) {
 *
 *      }
 * 注意:
 *      1. 构造方法是可以发生重载的【重载发生在同一个类中,方法名相同,参数列表不同(顺序不同,个数不同,类型不同 )
 *      2. 如果没有撰写任何的构造方法,jdk会帮我们自动添加一个无参构造方法,一旦显示的添加一个构造方法,默认的失效
 *      3. 构造方法不能return一个具体返回值
 *      4. 构造方法不能有返回值类型,连void都不能写
 *      5. 构造方法的名称一定要和类名相同
 */
public class PermissionDemo3 {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setName("Lisa");
        stu.setAge(20);
        System.out.println(stu.getName());
        System.out.println(stu.getAge());
        System.out.println("==========================");
        /*
        1. 创建对象的同时,对对象的属性进行赋值操作:Lisa,20
         */
        Student stu1 = new Student("Alice", 50);
        System.out.println(stu1.getAge());
        System.out.println(stu1.getName());
    }
}

public class Student {
    // 成员变量
    private String name;
    private int age;
    // 无参构造方法
    public Student() {

    }
    // 有参构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 成员方法
    public String getName() {
        return name;
    }
    public void setName(String name1) {
        name = name1;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age1) {
        age = age1;
    }
}

21. 继承

21.1. 继承的概念

继承Java面向对象编程技术的一块基石,因为它允许创建分等级层次的类
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有夫类的实例域和方法,或子类从父类继承方法使得子类具有父类相同的行为。

21.2. 类的继承——extends

在Java中通过关键字extends可以声明一个类是从另一个类继承而来的,一般形式如下:

// class 父类 {
	public int a;
	private int b; // private修饰属性和方法只能够在本类中和使用
}
// class 子类 extends 父类 {
	public void sum {
		a ++;
	}
}
class Persion {
	
}
class dog extends Persion { // 不符合常理,就不能实现继承关系!

}
// 这种关系要符合is-a【是一个的关系】

其中多个子类可以称为子类,单独的那个类叫父类超类或者基类

21.2.1. 定义

就是子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。子类是可以直接访问父类中的非私有的属性和行为。

21.2.2. 好处

在面向对象语言中,继承是必不可少的,主要有以下几个优点

  1. 实现了代码的共享,减少了创建类的工作量,使得子类可以拥有父类的方法和属性
  2. 提高了代码的可维护性和可重用性
  3. 提高了代码的可拓展性,更好的实现父类的方法。===方法的使用:重载、重写

21.2.3. 缺点

  1. 继承是有入侵性。只要实现继承,就必须拥有父类的方法和属性
  2. 降低了代码的灵活性。子类拥有了父类的方法和属性后多了些约束
  3. 增强代码了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,有可能会导致大段的代码需要重构!【开发项目的原则:高内聚、低耦合】

21.3. 继承的格式

/**
 * 超类中私有的属性或者方法,是不能被子类继承的,进而无法方法该私有属性和方法
 * 可以通过get、set方法来简介访问
 */
// 定义超类(父类、基类)
public class Person {
    String name; // 名字
    int age; // 年龄
    String id; // 身份证号
    private String slaNO;
    public void eat(String name) {
        System.out.println(name + "吃饭!");
    }
    public String getSlaNO() {
        return slaNO;
    }
    public void setSlaNO(String slaNO1) {
        slaNO = slaNO1;
    }
}

public class Student extends Person{
    public static void main(String[] args) {
        Student stu = new Student();
        // Student类中并没有显示的写任何成员变量和成员方法,但是通过继承关系,就能够继承成员属性和成员方法
        stu.age = 18;
        stu.id = "123456789123456798";
        stu.name = "李四";
        stu.setSlaNO("200000");
        stu.eat(stu.name);
        System.out.println(stu.id);
        System.out.println(stu.name);
        System.out.println(stu.age);
        System.out.println(stu.getSlaNO());
    }
}

21.4. 成员变量访问的特点

/**
 * 这是一个父类(超类、基类)
 */
public class Fu {
    int aFu = 10;
    // 父类的成员变量
    int b = 20;

    public int getB() {
        return b;
    }
}

/**
 * 这是一个子类
 */
public class Zi extends Fu{
    int aZi = 10;
    // 子类的成员变量
    int b = 30;
    public void method() {
        // 局部变量
        int b = 22;
        System.out.println(b);
        // 那个对象调用了当前的method方法,那么this就指代的是那个对象【this可以是很多个】
        System.out.println(this.b);
        // 子类继承了那个父类,那么super就指代的是哪个父类的对象【super只有一个】
        System.out.println(super.b);
    }
}

/**
 * 在继承关系中,对象能够调的东西,优先是在本类中查找
 * ===================
 * 子类成员变量:this.成员变量名
 * 父类成员变量:super.成员变量名
 * 局部变量:直接变量名
 */
public class Test {
    public static void main(String[] args) {
        // 创建Fu类
        /*
            创建父类的对象,通过他的引用(变量名称、对象名)能够调到所有非私有的成员变量,成员方法
         */
        Fu fu = new Fu();
        System.out.println(fu.aFu);
        System.out.println(fu.b);
        // 创建Zi类
        /*
            在父子类继承关系中,如果成员变量的名称相同,在创建了子类对象之后,直接调用到的成员变量是子类的。
            如果创建子类的对象之后,一定要访问父类的成员变量,可以间接的进行访问【get、set】
         */
        System.out.println("==============");
        Zi zi = new Zi();
        System.out.println(zi.aFu);
        System.out.println(zi.aZi);
        System.out.println(zi.b);
        System.out.println(zi.getB());
        System.out.println();
        System.out.println("==================");
        Zi zi1 = new Zi();
        zi1.method();
    }
}

21.5. 方法重写


/**
 * 成员方法的应用
 * 【重写方法】
 * 概念:
 *      在继承关系中,方法名称相同,参数列表也相同的方法
 *
 * 特点:
 *      如果使用父类的引用指向子类的对象的方式类调用重写的方法,要注意看该方法是否在子类中发生重写,如果方法重写,那么得到的结果是子类中重写的
 */
public class Test {
    public static void main(String[] args) {
        /*
         *子类====自己的成员变量、父类非私有的成员变量、自己成员方法、父类非私有的成员方法
         *      变量如果重名,子类可以直接方法自己的,也可以间接访问父类
         *      方法如果重名,子类可以直接访问自己的,也可以间接的访问父类,(将父类重名的方法间接的放置在另一个方法中,子类继承了也就访问了)
         * 父类====能够访问的是自己的成员变量和成员方法
         *      父类能不能访问到子类成员变量? ===不可以
         *
         *      父类能不能访问到子类成员方法? ===只有被重写的方法才可以父类访问
         */
        // 父类调父类
        Fu fu = new Fu();
        System.out.println(fu.getB());
        // 子类调子类
        Zi zi = new Zi();
        System.out.println(zi.getB());
        // 子类间接调父类
        zi.get();
        // 父类调子类
        System.out.println("=============");
        Fu fu1 = new Zi();
        System.out.println(fu1.aFu);
        System.out.println(fu1.b);
        // 此处父类优先也是在自己类中进行了查找,但是又发现,子类对这个方法进行了重写,所以调用的是子类重写的
        System.out.println(fu1.getB());

    }
}

public class Zi extends Fu{
    int aZi = 15;
    // 子类的成员变量
    int b = 30;

    public int getB() {
        return b;
    }

    public void method() {
        // 局部变量
        int b = 22;
        System.out.println(b);
        // 那个对象调用了当前的method方法,那么this就指代的是那个对象【this可以是很多个】
        System.out.println(this.b);
        // 子类继承了那个父类,那么super就指代的是哪个父类的对象【super只有一个】
        System.out.println(super.b);
    }
}

21.5.1. 重写的注意事项

/**
 * 【重写注意细节】
 * 1.要发生重写,必须方法名称相同,参数列表相同。【签名相同】
 * 2. 子类重写的方法前面是可以显示的添加@Override注解,表示这个方法是重写的方法,也可以省略
 * 3. 子类重写的方法,父类的返回值类型如果是void/8种基本数据类型,必须保持一致
 *      父类的返回值如果是引用数据类型,子类重写方法可以返回父类类型或者父类的子类型
 * 4. 修饰词:如果父类的修饰词是public,子类也一定是public,子类方法的修饰词权限要比父类的大或者等于。【父类方法私有,子类无法重写!】
 * 小拓展
 *      权限: public(公开的) > protected(受保护的) > 默认的(default) > private(私有的)
 * 注意:default是默认权限,就是什么都不用写,不能显示的写出来。
 */
public class TestPhone {
    public static void main(String[] args) {
        // 父类Phone的引用p,指向了子类NewPhone的对象
        Phone p = new NewPhone();
        p.call();
        p.sendMessage();
        p.showNum(); // 调到的showNum是子类NewPhone重写之后的
        System.out.println("-------------------------");
        NewPhone newPhone = new NewPhone();
        newPhone.call();
        newPhone.showNum();
        newPhone.sendMessage();
        System.out.println("--------------------------");
        // 父类自己的引用指向的是自己的对象
        Phone phone = new Phone();
        phone.call();
        phone.showNum();
        phone.sendMessage();
    }
}

/**
 * 方法上面添加了@Override注解 表示含义是该方法是重写方法
 * @Override如果报错,两种可能:第一就是该注解放在了不是重写的方法上面,第二个就是单词写错了
 * 方法不写修饰词public,那么就是默认权限(default),并不是关键词default
 * protected 受保护!
 *
 */
// 这是子类

public class NewPhone extends Phone{
    @Override
    public NewPhone showNum() {
        // super 表示父类
        // 当前的对象调用了这个方法,那么也会调用到父类的该show方法
        super.showNum();
        System.out.println("显示照片");
        System.out.println("显示姓名");
//        return new NewPhone(); //如果返回值类型是父类,那么return后面返回的值可以父类类型,也可以是子类类型
        return new NewPhone();
    }
}

21.6. 重载和重写的区别

/**
 *方法的重写与重载
 * 1. 重写:Override
 *      (1)发生在有继承关系的父子类之间
 *      (2)要发生重写,方法名称和参数列表一定要保持一致
 *      (3)返回值类型:
 *              如果方法的返回值类型是:void和8种基本数据类型,返回值类型一定要保持一致!
 *              如果方法的返回值类型是:引用数据类型,子类方法的返回值类型可以小于或者等于父类的返回值类型
 *      (4)修饰词:
 *              子类方法的访问权限要大于或者等于父类访问权限。【父类=public情况,子类也一定是public。父类的private方法子类无法继承,就不可以重写】
 *      ===== 重写遵循“运行期绑定”
 * 2, 重载:Overload
 *      (1)发生在一个类中
 *      (2)要发生重载,方法名一定要相同,参数列表一定要不用【个数的不同、类型的不同、顺序的不同】
 *      (3)对于返回值类型无要求
 *      (4)对修饰词无要求
 *      ===== 重载遵循“编译期绑定”
 */
public class OverLoadAndOverride {
    public static void main(String[] args) {
        // 父类的引用指向子类的对象
        Super obj = new Sub(); // 这样格式的对象创建我们又称为”向上造型“
        obj.f(); // sub.f();
        System.out.println("----------------");

        Goo goo = new Goo();
        goo.g(obj);
    }
}

class Super {
    public void f() {
        System.out.println("super.f()");
    }
}

class Sub extends Super {
    @Override
    public void f() {
        System.out.println("sub.f()");
    }
}

class Goo {
    public void g(Super obj) {
        System.out.println("g(Super)");
    }
    public void g(Sub obj) {
        System.out.println("g(Sub)");
    }
}

21.7. 父子类构造方法的访问

/**
 * 在继承关系中,父子类构造方法访问需要注意:
 *
 * 1. 子类构造方法种是默认一个父类的无参构造的调用【super()】
 *      所以就可以很好的解释:在构造子类的时候,优先构造父类
 * 2. 子类是可以通过关键字super进行父类构造方法的调用。在一个构造方法种,super调用只能调用一次
 * 3. super调用的构造方法,必须位于子类构造方法的第一行
 * 4. 如果父类种没有无参构造,那么子类构造方法就不会隐式提供super()方法
 */
public class ConstructionDemo {
    public static void main(String[] args) {
        // 创建子类的对象
        Zi zi = new Zi();
    }
}

class Fu {
    public Fu(int a) {
        System.out.println("父类的构造方法开始执行");
    }
    int num = 10;

}

class Zi extends Fu{
    public Zi() {
        super(1);
        // 隐藏了一个super();
//        super(); 调用的是父类的无参构造
        System.out.println("子类的构造方法开始执行");
    }
    int num = 20;
}

21.8. super使用的三种情况

/**
 * super关键字主要用于以下三种情况:
 *
 * 1. 在子类的成员方法中,访问父类的成员变量
 * 2. 在子类的成员方法中,访问父类的成员方法
 * 3. 在子类的构造方法中,访问父类的构造方法
 */
public class SuperDemo {
    public static void main(String[] args) {

    }
}

class Fu {
    int num = 1;
    public void method() {
        System.out.println("父类的method方法!");
    }
}

class Zi extends Fu{
    int num = 2;

    public Zi() {
        super();
    }

    public void method1() {
        System.out.println("访问了父类的成员变量" + super.num);
    }

    @Override
    public void method() {
        super.method();
        System.out.println("子类的method方法!");
    }
}

21.9. this的三种使用情况

/**
 *
 * this关键字主要用于以下三种情况:
 *
 * 1. 在本类的成员方法中,可以访问本类的成员变量
 * 2. 在本类的成员方法中,访问本类的其他成员方法
 * 3. 在本类的构造方法中,可以调用本类的其他构造方法【注意多个构造方法间的调用,避免死循环】
 *
 * 注意:构造方法是不可以自己调用自己的构造方法的
 *  super关键字是用来访问父类的内容
 *  this关键字是用来访问本类的内容
 */
public class ThisDemo {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.showMessage();
    }
}

class Fu {
    int num = 1;
}

class Zi extends Fu{
    int num = 2;

    public Zi() {
        this(1);
    }

    public Zi(int num) {
        this.num = num;
    }

    public void showMessage() {
        int num = 3;
        System.out.println(num); // 3
        System.out.println(this.num); // 2
        System.out.println(super.num); // 1
    }

    public void method1() {
        System.out.println("method1()");
    }

    public void method2() {
        this.method1();
        showMessage();
        System.out.println("method2()");
    }

}

21.10. 继承的特点总结

Java继承特性的三个特点

  1. Java语言是单继承性的【单根性】,也就是一个类的直接父类只能有一个

  2. Java可以多级继承,即继承具有传递性,就是:儿子可以有爸爸,爸爸也可以有爸爸

  3. 一个子类的直接父类是唯一的,但是一个父类的子类是可以有多个的

22. 抽象方法和抽象类

22.1. 使用抽象类与抽象方法

  由abstract修饰的方法为抽象方法,抽象方法即只有方法的定义,没有方法体实现,用一个分号结尾,即方法五要素中,抽象方法缺少了一个要素(即:方法体),也可以将抽象方法理解为不完整的方法。
  若将抽象方法包含在类中,即该类也应该为抽象的,可以理解为,该类也不完整,抽象类由abstract关键字声明。
抽象方法:没有方法体的方法

格式:

public abstract 返回值 方法名(){};

抽象类:包含抽象方法的类

格式:

public abstract class 类名{}
public class AbstractDemo {
    public static void main(String[] args) {
        // 创建Animal抽象类的对象
        Animal animal = new Pig();
        animal.eat();
        animal.sleep();
        System.out.println("--------------");
        Pig pig = new Pig();
        pig.eat();
        pig.sleep();
        System.out.println("-------------");
        Dog dog = new Dog();
        dog.eat();
        dog.sleep();
        System.out.println("--------------");
        Animal animal1 = new Dog();
        animal1.sleep();
        animal1.eat();
    }
}

/**
 * 抽象方法:加关键字abstract,并且方法没有具体实现(方法体)
 * 抽象类:含有了抽象方法的类,需要在class关键字之前添加abstract
 * 注意:
 *      1. 抽象类是不能够进行实例化的(不能创建对象)
 *      2. 抽象类必须具有一个子类进行继承
 *      3. 子类继承了抽象类之后,一定要实现抽象类的所有抽象方法,如果不实现,子类也必须是抽象类
 *      4. 抽象类是构造方法的。目的是供子类创建对象时初始化父类的成员变量
 *      5. 抽象类不一定抽象方法,但是包含抽象方法的类是抽象类
 */
public abstract class Animal {
    public abstract void eat();
    public abstract void sleep();
}


public class Pig extends Animal{
    // 普通的成员方法,是对父类抽象方法的覆盖(重写)
    @Override
    public void eat() {
        System.out.println("eat...");
    }

    @Override
    public void sleep() {
        System.out.println("sleep...");
    }


}

public class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("eat1...");
    }

    @Override
    public void sleep() {
        System.out.println("sleep1...");
    }
}

22.2. 抽象类的注意事项

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非子类的对象。

假设创建了抽象类的对象,调用抽象的方法,而抽象的方法没有具体的方法体,没有意义。

  1. 抽象类中,可以由构造方法,是供子类创建对象时,初始化父类成员使用的。

子类的构造方法中,有默认的super(),需要访问父类构造方法。

  1. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

未包含抽象方法的抽象类,目的就是不想让调用者创建该类的对象,通常用于某些特殊的类结构设计。

  1. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则编译无法通过编译,除非该子类也是抽象类

假设不重写所有的抽象方法,则类中可能包含抽象方法,那么创建对象后,调用抽象的方法,没有意义。

23. 接口

23.1. 概述

接口就是一种统一的规范标准,不同的实现厂商可能实现的过程和原理有所差别,但遵守的接口是一致的!有了统一化的标准接口,使得事物间的沟通性增强。

23.1.1. 定义一个接口

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员的变量,构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK7及以前),默认(default)方法和静态(static)方法(JDK8),私有(private)方法(JDK9)

  1. 含有抽象方法和常量【JDK7】

抽象方法:使用abstract关键字修饰,可以省略,没有方法体。该方法供子类实现使用。

  1. 含有默认方法和静态方法【JDK8】

默认方法:使用default修饰,不可省略,供子类调用或者子类重写。

  1. 含有私有方法和私有静态方法【JDK9】

私有方法:使用private修饰,供接口中的默认方法或者静态方法调用。

  接口的定义,它与定义类方法相似,但是使用interface关键字,它也会被编译成class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
  引用数据类型:数组、类、接口
  接口可以看成是特殊的抽象类。抽象方法和常量可以省略public abstract。因其默认就是public abstract的。

  接口的使用,它不能创建对象,但是可以被实现(implements,类似于被继承)。一个实现接口的类(可以看作是接口的子类),需要实现接口中所有的抽象方法,创建该类的对象,就可以调用方法了,否则它必须是一个抽象类。

/**
 * 接口是一个统一的公共规范【接口并不是一个类,同样也是可以编译为.class文件的,是java中的引用数据类型】
 * 1. 对于接口中的常量和抽象方法,可以省略掉public abstract关键字的,因为默认就是public abstract!
 *
 */
public interface Runner {
    // 常量 - JDK7
//    public final static int runBER_1 = 1;
    int runBER_1 = 1;
    // 抽象方法 - JDK7
//    public void run(int a, int b);
    void run(int a, int b);
//    public default void run() {
//        System.out.println("JDK8,接口中的默认方法!");
//    }
//    public static void run(int a) {
//        System.out.println("JDK8,接口的静态方法!");
//    }
//    public static void run(int a, int b) {
//        System.out.println("JDK8,接口的静态方法");
//    }
//    private void method() {
//        System.out.println("JDK9,接口中的私有方法!");
//    }
}

23.2. 实现接口

/**
 * 1. 实现了接口的类,一定要重写/实现抽象方法
 * 2. 实现类是可以继承和重写接口中的默认方法【重写默认方法,需要注意的是,要渠道default关键字】
 * 3. 接口中静态方法的访问方式:接口名.静态方法();
 * 4. 接口中的私有方法分为:普通私有方法和静态私有方法,其作用就是在本接口中使用,不对外暴露
 */
public class MyInterface01 implements Interface01{

    @Override
    public void method01() {
        System.out.println("重写了method01方法");
    }

    @Override
    public void method02() {
        System.out.println("JDK8默认方法的重写");
    }

    public static void main(String[] args) {
        MyInterface01 myInterface01 = new MyInterface01();
        myInterface01.method01();
        System.out.println("---------------");
        // 当前的实现类继承了接口的默认方法!
        myInterface01.method02();
        Interface01.method04();
    }
}

public interface Interface01 {
//    public Interface01() {
//
//    }接口没有构造方法
    public abstract void method01();
    // JDK1.8 default, static
    public default void method02() {
        System.out.println("JDK8,默认方法!");
    }

    public default void method03() {
        System.out.println("JDK8,默认方法!");
    }

    public static void method04() {
        System.out.println("JDK8静态方法");
    }
}

23.3. 接口的实现过程

23.3.1. 使用接口的注意事项

  1. 接口是没有静态代码块或者构造方法的。
  2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。【先继承后实现】

格式:

public class InterfaceImp1 implements InterfaceA, InterfaceB {
	// 覆盖重写所有抽象方法
}
  1. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
  2. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
  3. 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
  4. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。

23.3.2. 接口的多继承关系

  1. 类与类直接是单继承的,直接父类只有一个。
  2. 类与接口之间是多实现的,一个类可以实现多个接口。
  3. 接口与接口之间是多继承的

格式:

public interface Interface extends InterfaceA, InterfaceB {
	// 继承两个接口的所有抽象方法
	// 要实现两个接口的重复的默认方法
}

注意事项:

  1. 多个父接口当中的抽象方法如果重复,没关系
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】。

24. 多态

24.1. 多态的意义

同一行为的不同表现形式。

[admonition title=“注意” color=“red”]当使用多态方式调用方法时,,首先检查父类中是否有该方法,如果没有,则编译错误,如果有,执行的是子类重写后的方法。[/admonition]

24.2. 向上造型

父类的引用指向子类的对象,这个现象就是向上造型。
一个类的对象可以向上造型的类型有:父类的类型及其实现的接口类型,当发生向上造型时,Java编译器会根据类型检查调用方法是否匹配。

public abstract class Emp {
    String name;
    int age;
    double sal;

    public void clockUp() {
        System.out.println("上班打卡!");
    }

    public void clockDown() {
        System.out.println("下班打卡!");
    }

    public abstract void work();
}

public class Teacher extends Emp implements Consultant, Author{

    @Override
    public void work() {
        System.out.println("讲师授课!");
    }

    @Override
    public void edit() {
        System.out.println("编辑文稿");
    }

    @Override
    public void answer() {
        System.out.println("解决企业问题");
    }

    @Override
    public void training() {
        System.out.println("培训企业员工");
    }

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.answer();
        teacher.edit();
        teacher.training();
        teacher.work();
        System.out.println("------------");
        // 老师本身也是员工, 所有使用员工类型也是可以接受老师对象的。
        // 使用向上造型方式来调用方法或者属性,注意一定得是父类或者接口拥有的。
        Emp emp = teacher;
        emp.clockDown();
        emp.clockUp();
        System.out.println("---------------");
        Consultant consultant = teacher;
        consultant.answer();
        consultant.training();
        System.out.println("---------------");
        Author author = teacher;
        author.edit();
    }
}

public class Tutor extends Emp{

    @Override
    public void work() {
        System.out.println("为学生答疑");
    }
}

/**
 * 顾问接口
 */
public interface Consultant {
    public abstract void answer();
    public abstract void training();
}

/**
 * 编辑作者接口
 */
public interface Author {
    public abstract void edit();
}

24.3. 向下转型

public abstract class Animal {
    // 吃
    public abstract void eat();
}

public class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println("狗吃骨头!");
    }

    void dogWork() {
        System.out.println("看大门!");
    }
}

public class Cat extends Animal{

    @Override
    public void eat() {
        System.out.println("猫吃鱼!");
    }

    void catWork() {
        System.out.println("捉老鼠!");
    }
}

public class Demo {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Cat cat = new Cat();
        Dog dog = new Dog();
        demo.showCat(cat);
        demo.showDog(dog);
        System.out.println("-------------");
        Animal animal1 = cat;
        Animal animal2 = dog;
        demo.showAnimal(animal1);
        demo.showAnimal(animal2);

//        animal1.catWork(); //编译错误,Animal中没有catWork
        /*
        向下转型:(强转)
         */
        // 将Animal1向下转型
        System.out.println("----------------");
        Cat c = (Cat)animal1;
        c.catWork();
        Dog d = (Dog)animal2;
        d.dogWork();
        /*
        编译报错:
            java.lang.ClassCastException:类型转换异常
            因为animal2是动物类型,所以是可以把动物类型强制转换为cat类型的,但是在运行过程中出现了业务逻辑问题
            animal2本质是dog对象,是不可以把一只狗转换为一只猫
        解决方案:
            在进行类型转换之前,做一次必要的类型判断:使用关键字instanceof
            格式:
                变量名称 instanceof 类型
                这是一个boolean类型的表达式,如果变量符合后面的类型,返回true,否则返回false
         */
//        Cat c1 = animal2;
//        c1.catWork();
        System.out.println("-----------------");
        if(animal2 instanceof Cat) { // 如果animal2是猫类型,则转换为猫类型
            Cat c1 = (Cat)animal2;
            c1.catWork();
        }else if(animal2 instanceof Dog) { // 如果animal2是狗类型,则转换为狗类型
            Dog d1 = (Dog)animal2;
            d1.dogWork();
        }
    }

    void showAnimal(Animal animal) {
        animal.eat();
    }

    void showCat(Cat cat) {
        cat.eat();
    }

    void showDog(Dog dog) {
        dog.eat();
    }
}

25. 内部类

在所描述的事物,存在包含关系,就可以使用内部类这种结构:汽车类包含了发动机类,那么汽车类就是外部类,而发动机类就是描述汽车类的内部类,其身份就是一个成员。

格式:

class 汽车类 { // 汽车是外部类
	class 发动机 { // 发动机是内部类
		
	}
}

[alert title=“特点” color=“red”][/alert]

  1. 内部类可以直接访问外部类的全部成员,包括私有成员
  2. 外部类要访问内部类的成员,必须创建内部类的对象,通过内部类的对象进行访问。

格式:
[外部类名].内部类名 对象名 - new 外部类型().new 内部类型()

25.1. 内部类的创建和访问&&内部类中变量冲突问题

/**
 * 访问内部类的两种方式:
 *      1. 间接访问:创建一个外部类的成员方法,包含内部类的对象,通过外部类来访问成员方法,进而来访问成员内部类
 *      2. 直接访问:OutClass.InnerClass inner = new OUtClass().new InnerClass();
 *
 * 解决内部类结构中,变量名冲突问题:
 *      1. 访问内部类的局部变量。直接调用即可
 *      2. 访问inner类的变量,需要指定当前对象:this
 *      3. 访问out类的变量,需要指定当前对象:Out.this
 * 对于内部类而言是可以分为:成员内部类和局部内部类的
 * “局部”:指的是在方法中存活的概念
 * 局部内部类:
 *      class Out { // 外部类
 *          public void method() { // 成员方法
 *              class Inner { // 局部内部类
 *  *
 *  *          }
 *          }
 *      }
 */
public class OutClass { // 外部类
    String info = "这是外部类!";

    public void show() { // 外部类的成员方法
        InnerClass innerClass = new InnerClass();
        innerClass.show();
        System.out.println(info);
    }

    public class InnerClass { // 内部类【成员内部类】
        String info = "这是内部类!";

        public void show() {
            String info = "这是内部类的局部变量!";
            System.out.println(info); // 就近原则
            System.out.println(this.info); // this表示当前类的对象,也就是内部类
//            System.out.println(super.info); 编译报错、因为当前super指代的是Object,没有info变量
            System.out.println(OutClass.this.info); // OutClass.this就是指明当前的类的对象是OutClass的
        }
    }

    public static void main(String[] args) {
        // 访问内部类:间接访问
        OutClass outClass = new OutClass();
        outClass.show();
        System.out.println("----------------");
        // 访问内部类:直接访问
        OutClass.InnerClass inner = new OutClass().new InnerClass();
        inner.show();
    }
}

25.2. 局部内部类的使用

/**
 * 局部内部类:
 *      定义在方法中的类,只服务于当前的方法,对外是不可见的。这个类的生命周期对着方法调用开始,方法结束位置,也就是局部内部类供此方法来调用。
 * 使用内部类需要注意:
 *      1. 果内部类是局部内部类,那么修饰不可以添加
 *      2. 在JDK8之后,局部变量在局部内部类中使用的时候,final是可以省略的。
 *解释为什么局部内部类一定要使用的变量具备final性质:
 *      1. 方法被调用,运行在栈(栈帧)
 *      2. 对象运行在堆中
 *      3. 当方法运行完毕,就会出栈(生命周期结束),方法消失了a就会消失,为了保证在堆中的对象能够正确的访问到方法给的值
 *          那么就把这个值设置为常量,保证了数据的不可变性(方法存在和消失不会影响赋值)
 */
public class Out { // 外部类
    public void method() {
        int a = 10; // a放在常量池中
        class Inner { // 局部内部类
            public void method() {
                System.out.println(a);
            }
        }
        Inner inner = new Inner();
        inner.method();
    }
}

public class Test {
    public static void main(String[] args) {
        Out out = new Out();
        out.method();
    }
}

25.3. 匿名内部类

/**
 * 匿名内部类的实现格式:
 *      接口类型 对象名称 = new 接口名称(){
 *          // 实现当前接口中的抽象方法
 *      };
 * 说明:
 *      1. new是一个创建对象的动作【创建的是一个实现类的对象,目前这个实现类是没有名字的】
 *      2. 接口类型就是匿名内部类要去实现的那个接口
 *      3. {
 *          // 匿名内部类要执行的内容
 *      };
 * 注意:
 *      1. 匿名内部类在创建完对象后,只能使用唯一的一次
 *      2. 匿名对象的方式不推荐使用,因为调用的方法也只能够调用一次,而且只能调用第一个实现的抽象方法。
 *
 * 什么时候使用匿名内部类?
 *      当实现类只需要使用一次的时候,就优先考虑匿名内部类的方式
 */
public class MyDemo {
    public static void main(String[] args) {
        // 直接创建实现类的对象
        InterfaceImpl anInterface = new InterfaceImpl();
        anInterface.show();
        // 多态的方式进行调用
        Interface anInterface1 = new InterfaceImpl();
        anInterface1.show();
        System.out.println("-------------------");
        // 匿名内部类的实现方法:anInterface2是对象的名称
        Interface anInterface2 = new Interface() {
            // 这个过程就是对接口中的抽象方法的重写
            @Override
            public void show() {
                System.out.println("匿名内部类:实现类接口中的抽象方法!1111");
            }
        };
        anInterface2.show();
        System.out.println("-----------------");
        // 匿名对象:在调用方法的时候,只能调用唯一的一次,只能调用唯一的方法
        new Interface() {
            // 这个过程就是对接口中的抽象方法的重写
            @Override
            public void show() {
                System.out.println("匿名内部类:实现类接口中的抽象方法!2222");
            }
        }.show();
    }
}

public class InterfaceImpl implements Interface{

    @Override
    public void show() {
        System.out.println("实现了接口中的抽象方法!");
    }
}

public interface Interface {
    public void show();
}

26. 四种权限修饰符

26.1. 访问控制符修饰成员

Java中访问修饰(public、private、protected、default)都可以修饰成员,权限如下:

修饰符 本类 同一个包中的类 子类 其他类
public 可以访问 可以访问 可以访问 可以访问
protected 可以访问 可以访问 可以访问 不能访问
default 可以访问 可以访问 不能访问 不能访问
private 可以访问 不能访问 不能访问 不能访问

[alert title=“注意” color=“red”][/alert]

这里的子类是不同包中的子类

  其中,public修饰符,在任何地方都可以访问;protected可以在本类、同一包中的类、子类中访问,除此之外的其他类不可以访问;默认方式为可以本类及同一包中的类访问,除此之外其他类不可以访问;private只可以在本类中访问,其他任何类都不可以访问。

[alert title=“回顾” color=“blue”][/alert]

  private与public为最最常用的两个访问控制修饰符,其中,private修饰的成员变量和方法仅仅只能在本类中调用,而public修饰的成员变量和方法可以在任何地方调用。

26.2. final关键字使用

  1. final可以修饰一个类:这个类是不可以被继承的。
  2. final可以修饰一个方法:这个方法不能够被重写的。
  3. final可以修饰一个变量【成员变量和局部变量】
    • 修饰局部变量:变量值不可以改变
    • 修饰全局变量:声明的同时需要进行初始化,或需要在构造方法中初始化,值也不能被改变。
  4. abstract和final是不可以同时出现的,因为作用和语义是相互矛盾的。

26.2.1. final修饰变量

  final关键字修饰变量,意为不可改变。final可以修饰成员变量,也可以修饰局部变量,当final修饰成员变量时,可以有两种初始方式:

  1. 声明同时初始化
  2. 构造函数中初始化

final关键字修饰局部变量,在使用之前初始化即可。参加如下示例:

public class Emp {
	private final int no = 100; // final成员变量声明时初始化
	public static void main(String[] args) {
		no = 99;
	}
}

26.3. static关键字使用

  1. static可以修饰成员变量:只有一份,保持在方法区中。【static成员是属于类的,而非对象的】
  2. static可以修饰成员方法:该方法不能被重写。
  3. static块:在类加载时只执行一次,用来初始化静态成员变量。

26.3.1. static修饰成员变量

  static关键字可以修饰成员变量,它所修饰的成员变量不属于对象的数据结构,而是属于类的变量,通常通过:类名来引用static成员。
  当创建对象后,成员变量是存储在堆中的,而static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static成员变量只有“一份”(存储在方法区),无论该类创建了多少对象,看如下示例:

class Cat {
	private int age;
	private static int numOfCats;
	public Cat(int age) {
		this.age = age;
		System.out.println(++ numOfCats);
	}
}
// 在main方法中声明两个Cat类对象;

Cat c1 = new Cat( 2);
Cat c2 = new Cat( 3);

注:如果您通过阅读本文解决了问题,恳请您留下一个宝贵的赞再离开,如果不嫌弃的话,点个关注再走吧,非常欢迎您能够在讨论区指出此文的不足处,博主会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java语言基础 的相关文章

随机推荐

  • jar包手动添加到本地maven仓库详解

    第一步 下载需要添加的jar包 可以在maven库中查找下载 也可以在对应官网下载 maven库网址 需要验证是真人 https mvnrepository com 第二步 将下载的jar包放到指定位置 位置自己指定 用得到 建议放在mav
  • css-将网页全局置灰

    亲测百分之百有效 在一些情况下需要将网页的全局颜色进行置灰处理 但是又不能影响到其他属性 此时我们只需要在主框架的样式中增加该属性即可 filter grayscale 100
  • Java的String相关的加号+和append比较

    上例子 package com smile core public class StringAppend public static void main String args 字符串常量用 连成串 实际上等于一个字符串 效率最高 long
  • C++ STL 互斥体

    mutex 互斥量 非定时的互斥体类 标准库包含两个非定时的互斥体类 std mutex std recursive mutex 每个类都支持下列方法 lock 阻塞直到取到锁 trylock 立刻返回获取锁结果 unlock 已经拥有st
  • linux线程学习(二)

    上一个linux线程学习是不带锁的线程不安全形式 只要不用共享资源还是可以的 但实际工作中我们遇到的往往是需要保障线程的访问的 因此这里实现了一个简单的线程池 为线程池的实现提供思路 Status类封装了环境变量与锁 作为一种状态保障线程的
  • Python错误:UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte...

    错误提示如下 tf2 C Users user gt python Python 3 7 6 packaged by conda forge default Mar 5 2020 14 47 50 MSC v 1916 64 bit AMD
  • libcurl教程

    这个文档是小编在curl官网上使用谷歌翻译翻译的 详细信息看官网 curl 基本描述 本文档试图描述使用 libcurl 编程时要考虑的一般原则和一些基本方法 本文将主要关注 C 接口 但可能会很好地适用于其他接口 也会讲解一些个C接口相似
  • mongodb设置账号密码授权案例

    1 给admin授权管理员权限 gt use admin gt db createUser user admin pwd 123456 roles role root db admin 如果需要修改admin密码 gt db changeU
  • 链表有环(引申题:环在哪里)

    public class Test private static class ListNode int value ListNode next public static boolean hasCycle ListNode head if
  • 采集校园卡消费信息可视化(安徽工商职业学院)

    闲的没事干 分析下我自己的一卡通消费信息 使用了以下技术进行数据分析并且环境已经搭建好 Hadoop hive mysql sqoop centos7 python zeppelin 思路 python爬自己的消费记录 存入到mysql s
  • pthread函数总结

    概括 https www cnblogs com tianzeng p 8934060 html
  • linux 运行scrapy,linux下部署scrapy

    1 如何部署scrapy 下载scrapy client 下载地址 https github com scrapy scrapyd client 并进行安装 安装命令 python setup install 进入项目目录 执行scrapy
  • 【Unity3D日常BUG】Unity3D中出现“动画片段必须标记为Legacy的警告”消除方法

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好 我是佛系工程师 恬静的小魔龙 不定时更新Unity开发技巧 觉得有用记得一键三连哦 一 前言 在开发中可能会遇到下面的警告 The Ani
  • 关于interface(接口继承)extends(接口)interface的问题

    前序 一直以来 我们学习java就被灌输了一个观点 类和抽象类 class 只能被继承 extends 接口 interface 只能被实现 implements 有个概念我们一直没有弄懂的是 类 class 可以实现接口 interfac
  • 刘积仁资本阳谋:东软熙康通过上市聆讯,三年亏损8亿元

    大数据产业创新服务媒体 聚焦数据 改变商业 9月中旬 第四次递表的东软熙康通过港交所上市聆讯 预计在9月30日之前完成IPO 作为国内首家以城市为入口的云医院平台厂商 东软熙康的互联网医疗服务包括互联网医院 远程医疗 智慧家庭医生 互联网居
  • 计算机科学和Python编程导论(二 ) Python简介

    基本概念 1 编程语言的分类 低级编程与高级编程 二者之间的区别是 编写程序时 我们是使用机器层次的指令和数据对象 底层操作 还是使用语言设计者提供的更为抽象的操作 图形用户界面 UI 通用性与专注于某一应用领域 指编程语言中的基本操作是广
  • 【yolo】makesense操作

    https www makesense ai 成功
  • 集团税务共享化、数字化系统如何选型(一)

    随着国家对于企业税务营改增的全面开启 再到国地税合并 金税三期全面建成 同时国家逐级推出发票电子化到专票电子化 标志着国家对发票全面升级为电子化 数字化 伴随着金税四期的开始并逐步落地 国家对企业的税收管理也原有的稽查转变为数字化 智能化的
  • 【100天精通Python】Day45:Python网络爬虫开发_ Scrapy 爬虫框架

    目录 1 Scrapy 的简介 2 Scrapy选择器 3 快速创建Scrapy 爬虫 4 下载器与爬虫中间件 5 使用管道Pielines lt
  • Java语言基础

    文章目录 1 开发环境 1 1 认识开发环境JDK 1 2 JDK JRE JVM关系 1 3 JDK1 8本地安装目录 1 3 1 bin 1 3 2 include 1 3 3 lib 1 3 4 conf 1 3 5 src 1 4