快速梳理Java基础知识(详细)


本文旨在使用一篇文字快速梳理Java基础,适合快速复习、学习,本文在介绍过程中会推荐相关的模块介绍文档。因为Java基础知识广泛,本文内容处于动态变更中,我希望这篇文章会对读者有些帮助。

1 基础知识列举

本文中所介绍的各知识点均在该表中有展示,没有详细介绍的部分的介绍栏显示为否,可以移步至后面的文章中进行学习参考。

知识点名称 本文是否介绍 推荐文章
Java基本简介 Java简介推荐文一 ; Java简介推荐文二
Java环境配置 Java环境配置推荐文
基本数据类型 基本数据类型推荐文
对象和类 对象和类推荐文一对象和类推荐文二
条件判断 条件判断推荐文
switch语句 switch语句推荐文
循环结构 循环结构推荐文
运算符 运算符推荐文
修饰符 修饰符推荐文

2 基础梳理阶段一

2.1 Java基本简介

Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。

上面是百科给出的Java的基本介绍,下面介绍我所认识的Java。首先,Java语言从使用人数来看,近年来始终占据着前面几位,从使用人数就可以看出Java语言是具有一定优势的;接着,我感触较多的就是Java的可移植性,通过安装不同平台的JDK,做到了跨平台的效果,而对于我使用Java的情况来看,这样很方便进行部署;在各种博客、代码库中,有大量Java的学习资料,可以说入门较为简单,但是深入学习还是有较大的难度。在这里我分享一份Java基础学习的路线图,该路线图是博主个人学习路线的规划(暂时不完善,后期完善)。
学习大纲图
大纲中的各项介绍都是我自己的看法,仅作为参考。

2.2 Java环境配置

Java的环境配置,在Windows下一般包含JDK安装,环境配置,环境测试,我之前已经有写过一篇关于JDK环境配置的案例,这里就不再赘述,在这篇案例中,包含有JDK的下载资源以及两种操作系统下的JDK安装案例,有需要的读者可以移步:JDK安装及环境配置

2.3 基本数据类型

在Java中,基本数据类型有八种,它们分别是 byte,int,float,double,boolean,long,char,short,接下来用表格的方式进行分别介绍:

数据类型 最小值 最大值 默认值 描述 案例
byte -128(-2^7) 127(2^7-1) 0 byte 数据类型是8位、有符号的,以二进制补码表示的整数 byte a = 100;
short -32768(-2^15) 32767(2^15 - 1) 0 short 数据类型是 16 位、有符号的以二进制补码表示的整数 short s = 1000;
int -2,147,483,648(-2^31) 2,147,483,647(2^31 - 1) 0 int 数据类型是32位、有符号的以二进制补码表示的整数 int a = 100;
long -9,223,372,036,854,775,808(-2^63) 9,223,372,036,854,775,807(2^63 -1) 0L long 数据类型是 64 位、有符号的以二进制补码表示的整数 long a = 100L;
char \u0000(0) \uffff(65535) char类型是一个单一的 16 位 Unicode 字符 char a = ‘A’;
float 3.402823e+38 1.401298e-45 0.0f float 数据类型是单精度、32位、符合IEEE 754标准的浮点数 float a = 234.5f;
double 1.797693e+308 4.9000000e-324 0.0d double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数 double a = 123.4;
boolean 0 boolean数据类型表示一位的信息;只有两个取值:true 和 false boolean a = true;

提到数据类型,就必须提一下数据类型的转换,在Java中,分为强制类型转换以及自动类型转换,首先说明一下自动类型转换,必须满足转换前的数据类型的位数要低于转换后的数据类型,例如: short数据类型的位数为16位,就可以自动转换位数为32的int类型,同样float数据类型的位数为32,可以自动转换为64位的double类型。

转换线路: byte,short,char—> int —> long—> float —> double

接着介绍强制类型转换,通过在转换的数据前面加上括号,填入需要转换的类型即可进行强制转换,强制类型转换过程中转换的类型必须是兼容的。

/**
* 强制转换案例介绍
*/
public class Convert{
    public static void main(String[] args){
        int i1 = 123;
        byte b = (byte)i1;//强制类型转换为byte
        System.out.println("int强制类型转换为byte后的值等于"+b);
    }
}

转换过程中有一些要求:

不能对boolean类型进行类型转换
不能把对象类型转换成不相关类的对象
在把容量大的类型转换为容量小的类型时必须使用强制类型转换
转换过程中可能导致溢出或损失精度

2.4 对象和类

这里介绍的对象和类只是简单的介绍使用的方式以及一些基本的注意事项,更加全面的介绍可以参照后续的面向对象介绍中的介绍。

类(class)和对象(object)是两种以计算机为载体的计算机语言的合称。对象是对客观事物的抽象,类是对对象的抽象。类是一种抽象的数据类型。它们的关系是,对象是类的实例,类是对象的模板。对象是通过new classname产生的,用来调用类的方法;类的构造方法 。

同样,先给出了百科的介绍,这里我说一下我所认知的类和对象的关系,类是具有相同属性和服务的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和服务两个主要部分。在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属性说明和服务说明两个主要部分。

对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。从更抽象的角 度来说,对象是问题域或实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属性和有权对这些属性进行操作的一组服务的封装体。客观世界是由对象和对象之间的联系组成的。

类实际上就是对一个事物的抽象,这个抽象显然很难用具体的语言来形容,这里简单的用图来介绍:
类和对象举例图
这张图有些草率,但是能够看得出类和对象之间的关系,对象是类实例化(New)产生的具体的事物。类与对象的关系就如模具和铸件的关系,类的实例化结果就是对象,而对一类对象的抽象就是类。类描述了一组有相同特性(属性)和相同行为(方法)的对象。

这样说可能还是十分抽象,下面直接举例,用代码来阐述类和对象的关系:

/**
 * @Author darryl
 * @Date 2020/6/12 15:19
 * 学生实体类
 */
@Data
public class Student {
    /**
     * 编号
     */
    private String id;
    /**
     * 学号
     */
    private String sno;
    /**
     * 密码
     */
    private String password;
    /**
     * 状态
     */
    private Boolean status;
}

上述代码就表示了一个学生的实体类,这个实体类里面包含有四个属性(编号、学号等,@Data是Lombok插件用法),这时候这个学生类还没有更加具体的意义。接下来看下面这段代码:

public static void main(String[] args) {
        Student xMing = new Student();
        xMing.setSno("Z09316101");
        System.out.println(xMing.getSno());
}

这里的代码逻辑也很简单,通过new关键字,实例化了一个Student对象xMing,我们可以看到实例化出的xMing拥有了Student对象里面的属性,并且通过get,set可以实现对对象属性的值的设置与读取,这点后面会继续说明,这里介绍一下Java中的new关键字:
new关键字

Student xMing = new Student();
这一段代码中的new关键字,实际上为Student的对象创建了一个实例,在内存里开辟了两个空间:一块空间在堆,存放new Student()这个对象;另一块空间在栈,存放xMing ,xMing 的值即new Student()这个对象的内存地址。

new运算符实例化一个类对象,通过给这个对象分配内存并返回一个指向该内存的引用。
new运算符也调用了对象的构造函数。注意:“实例化一个类的对象”的意思就是“创建对象”。创建对象时,你正在创造一个类的“实例”,因而“实例化”一个类的对象。
new运算符需要一个单一的,后缀参数,需要调用构造函数。构造函数的名称提供了需要实例化类的名称。
new运算符返回它所创建的对象的引用。此引用通常被分配给一个合适的类型的变量。由new运算符返回的引用可以不需要被赋值给变量。它也可以直接使用在一个表达式中。

至此,初步介绍了类和对象的基本关系,也就不在深入进行介绍,简要介绍了new关键字,后面会有介绍栈、堆时,会再次对new关键字进行介绍,也会单独开辟一个新的模块介绍new关键字在运行时具体做了哪些事情。

2.5 条件判断

在这一个小模块中,我会介绍在Java中作条件判断的语句使用方法与相关的注意事项。这里首先介绍下什么是条件语句。

条件语句可以给定一个判断条件,并在程序执行过程中判断该条件是否成立,根据判断结果执行不同的操作,从而改变代码的执行顺序,实现更多的功能
写程序时,常常需要指明两条或更多的执行路径,而在程序执行时,允许选择其中一条路径,或者说当给定条件成立时,则执行其中某语句。在高级语言中,一般都要有条件语句

Java作为高级语言,条件语句也是必不可少的一部分。这里直接使用代码和注释先对条件语句进行介绍:

public static void main(String[] args) {
     int codeA = 12;
     int codeB = 13;
     //在Java中,条件语句的结构和C相同,都是if,else组成,使用方式也和C语言类似
     if (codeA == codeB) {
         System.out.println("codeA == codeB");
     }
     //上面的结构为纯if结构,在程序执行过程中,if内的语句为true时,执行if下包含的程序块,否则跳过该程序块
     if (codeA == codeB) {
        System.out.println("codeA == codeB");
     } else {
        System.out.println("codeA != codeB");
     }
     //上面的结构为if,else结构,当else上对应的if内语句为false时,会执行else中包含的语句
     //这里建议对各程序块使用{}进行包裹(包括只有一条语句),这样语句逻辑不易出错
     if (codeA == codeB) {
        System.out.println("codeA != codeB");
     } else if (codeA > codeB) {
        System.out.println("codeA > codeB");
     } else {
        System.out.println("codeA < codeB");
     }
     //在这里引入了else if 结构,可以对多种结果进行判别而分别处理
}

通过上面的代码,实际上我们可以看出,总共有三种结构,接下来重点阐述这三种结构:

if结构

仅有关键字if组成,其结构最为简单

if(条件表达式)
 	语句1;

if-else结构

该结构是比较常见的结构,其基本格式如下:

if(条件表达式)
 	语句1;
else
	语句2;

在执行该判断语句前,都是先执行了条件表达式的语句,条件表达式的返回结果必须是布尔值(boolean),根据条件表达式的返回,如果是true,那么就执行语句1的内容,如果是false就执行else后面的语句2

if- else if- else结构

该结构可以对执行情况进行更加详细的划分

if(条件表达式)
 	语句1;
else if(条件表达式)
	语句2;
	...
else
	语句3;

这样的语句在我们以后的编程中会经常用到,判断的过程是从上往下的判断条件表达式,如果第一个返回的是false就会判断第二个,依次类推,但是如果其中一个返回了true,那么就会执行后面的语句,然后整个else-if语句就会退出,后面如果还有else-if语句也不会在去判断执行的了。我们常常也会在最后面添加一个else语句,当然这也是可选的,这样的效果就是如果上面的所有的if判断都是false,那么就会执行else后面的语句

条件判断中还有一种结构,我也放在这一个模块中进行讲述,首先引入一个案例:

public static void main(String[] args) {
    int codeA = 100;
    int codeB = 200;
    System.out.println("codeA与codeB两者之间的最大值为:"+returnTheMaxCode(codeA,codeB));
}
public static int returnTheMaxCode(int codeA, int codeB) {
    return codeA > codeB ? codeA : codeB;
}

上面的案例实际上逻辑非常简单,就是调用了一个方法(函数),然后获取两数之间的较大值,而在方法中,使用了 条件语句 ? 语句1 : 语句2 这样一种结构,这种结构实际上使用起来也是十分方便,通过判断条件语句是否为真,如果为真那么就执行语句1,如果不为真,那么执行语句2。这种结构在实际运用中还是比较常见的,可以缩减代码数量,提升代码可读性。

2.6 switch语句

这里还是首先介绍一个代码案例:

public static void main(String[] args) {
        int code = 2;
        switch (code) {
            case 1: {
                //执行语句
                System.out.println("code = 1");
                break;
            }
            case 2: {
                //执行语句
                System.out.println("code = 2");
                break;
            }
            default: {
                //执行语句
                System.out.println("code != 2 && code != 1");
            }
        }
}

这段代码执行的逻辑相对来讲是很简单的,就是判别code的值,然后根据不同的值执行不同的语句,看完这些代码,接下来引出switch语句,switch语句,判断一个变量与一系列值中某个值是否相等,每个值称为一个分支,其语法格式如以下:

switch(expression){
    case value :
       //语句
       break; //可选
    case value :
       //语句
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
}

这里建议每一个case以及default后面的语句均加上代码块{},这样代码结构更加清晰。switch语句的相关注意事项如下:

  1. switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
  2. switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
  3. case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
  4. 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
  5. 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
  6. switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。

这里需要特别注意,如果 case 语句块中没有 break 语句时,JVM 并不会顺序输出每一个 case 对应的返回值,而是继续匹配,匹配不成功则返回默认 case。当匹配成功时,会继续执行直到遇到 break 语句为止。也就是说,如果当前匹配成功的 case 语句块没有 break 语句,则从当前 case 开始,后续所有 case 的值都会输出,如果后续的 case 语句块有 break 语句则会跳出判断。

2.7 循环结构

在Java中,循环结构主要有三种结构,这里首先给出这三种结构:

while循环

while( 布尔表达式 ) {
  //循环内容
}

while循环,只要布尔表达式值为真,那么就会一直执行下去,下面介绍一个代码案例:

public static void main(String[] args) {
        int code = 12;
        while(code > 1){
            System.out.println("code == 1");
            code --;
        }
}

do while循环

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

对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。下面介绍一个代码案例:

public static void main(String[] args) {
        int code = 12;
        do{
            System.out.println("code == 1");
            code --;
        } while(code > 1);
}

for循环

for(初始化; 布尔表达式; 更新) {
    //代码语句
}

关于 for 循环有以下几点说明:

最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
执行一次循环后,更新循环控制变量。
再次检测布尔表达式。循环执行上面的过程。

下面介绍一个代码案例:

public static void main(String[] args) {
        for (int code = 0; code < 12; code++) {
            System.out.println("code=" + code);
        }
}

在Java中还有增强型for循环,Java5 引入了一种主要用于数组的增强型 for 循环。
Java 增强 for 循环语法格式如下:

for(声明语句 : 表达式)
{
   //代码句子
}

声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

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

相比而言,这一增强型for循环使用的频率较高,常用来循环一个List,代码结构更加清晰。下面介绍一个代码案例:

public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        for (int code = 1; code < 10; code++) {
            Student student = new Student();
            student.setId(code);
            list.add(student);
        }
        for (Student student : list) {
            System.out.println(student.getId());
        }
}

2.8 运算符

Java中运算符数量众多,我们可以将众多的运算符进行简要的分类,所包含的分类有:

算术运算符
关系运算符
位运算符
逻辑运算符
赋值运算符
其他运算符

下面一一介绍这些运算符具体的含义与使用案例。

算术运算符

算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。其中包含有加、减、乘、除、取余、自增、自减,具体的解释使用表格来进行说明(表中A=10,B=5):

运算符名称 运算符符号 运算符说明 使用案例
+ 加法 - 相加运算符两侧的值 A+B
- 减法 - 左操作数减去右操作数 A-B
* 乘法 - 相乘操作符两侧的值 A*B
/ 除法 - 左操作数除以右操作数 A/B
取余 % 取余 - 左操作数除以右操作数的余数 A%B
自增 ++ 自增: 操作数的值增加1 A++;–B
自减 自减: 操作数的值减少1 A–;--B

这里解说一下自增自减的用法,也就是++i与i++的区别(自减同样),自增符号在操作数前,首先完成自增,然后再进行赋值操作,自增符号在操作数后,首先完成赋值,然后再自增操作。

//这里举例说明自增符号位置的不同的区别
int codeA = 10;
System.out.println(++codeA);
//此时控制台输出结果为11
System.out.println(codeA++);
//此时控制台输出结果为11
System.out.println(codeA);
//此时控制台输出结果为12

关系运算符

下面介绍Java中的关系运算符,还是使用表格进行初步介绍:

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

关系运算符执行结果为boolean型,即不是true就是false,下面运用案例说明关系运算符使用结果:

public static void main(String[] args) {
        int codeA = 10;
        int codeB = 20;
        System.out.println(codeA == codeB);
        //此处输出结果为false
        System.out.println(codeA != codeB);
        //此处输出结果为true
        System.out.println(codeA > codeB);
        //此处输出结果为false
        System.out.println(codeA < codeB);
        //此处输出结果为true
        System.out.println(codeA >= codeB);
        //此处输出结果为false
        System.out.println(codeA <= codeB);
        //此处输出结果为true
}

位运算符

这里介绍位运算符,在实际开发中很少使用到位运算符,但是位运算符还是很重要,Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。位运算符作用在所有的位上,并且按位运算。

依旧以表格形式列举Java中的位运算符:

运算符符号 运算符描述 使用案例
如果相对应位都是1,则结果为1,否则为0 A & B
| 如果相对应位都是 0,则结果为 0,否则为 1 A | B
^ 如果相对应位值相同,则结果为0,否则为1 A ^ B
~ 按位取反运算符翻转操作数的每一位,即0变成1,1变成0 ~ B
<< 按位左移运算符。左操作数按位左移右操作数指定的位数 A << 2
>> 按位右移运算符。左操作数按位右移右操作数指定的位数 A >> 2
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充 A >>> 2

位运算符理解起来相对困难一点,因为是对位的操作,下面我具体进行解释:

为了解释好位运算,这里先假设两个值
byte A = 10;
byte B = 20;
首先我们将A和B处理为二进制数,我们知道byte类型是有符号8位整数,这时A和B的值表示为:
A = 0000 1010
B = 0001 0100

这里首先表示按位与&操作符:A&B
也就是 0000 1010 & 0001 0100
这时操作如果相对应位都是1,则结果为1,否则为0
也就是说
    0000 1010
& 0001 0100
=  0000 0000
即A&B结果为0

接着介绍按位或|操作符:A|B
也就是说 0000 1010 | 0001 0100
这时操作如果相对应位都是 0,则结果为 0,否则为 1
也就是说
    0000 1010
|   0001 0100
=   0001 1110
即A|B的结果为30

接着介绍按位异或操作:A^B
也就是说 0000 1010 ^ 0001 0100
这时操作如果相对应位值相同,则结果为0,否则为1
也就是说
   0000 1010
^  0001 0100
=  0001 1110
即A^B的结果为30

接着介绍按位取反操作:~A
也就是 ~ 0000 1010
这时操作按位取反运算符翻转操作数的每一位,即0变成1,1变成0
也就是说
~ 0000 1010
= 1111 0101
即~A的结果为-11

接着介绍按位左移操作符:A<<2
这时操作左操作数按位左移右操作数指定的位数
也就是 0000 1010左移两个操作数   这时结果为
0010 1000   即A<<2结果为40

接着介绍按位右移操作符:A>>2
这时操作左操作数按位右移右操作数指定的位数
也就是 0000 1010右移两个操作数   这时结果为
0000 0010   即A>>2结果为2

最后介绍按位右移补零操作符:A>>>2
左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充
也就是 0000 1010 >>> 2 = 0000 0010
即A>>>2结果为2

这里对位操作符进行了初步的解释,在实际开发中这样的操作符使用的并不是很多。但是有效利用位操作符可以提升代码执行效率。

逻辑运算符

这里介绍一下Java中的逻辑运算符,逻辑运算符包含三个运算,分别是逻辑与(&&)、逻辑或(||)
逻辑非(!),这三个运算的运算结果同样是boolean型:

运算符名称 运算符符号 运算符说明 使用案例
逻辑与 && 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真 A&&B
逻辑或 || 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真 A
逻辑非 ! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false !A

逻辑运算符会存在短路效应:
当逻辑与运算时,只要第一个逻辑为false,则整体为false,不会再继续执行
逻辑或运算时,只要第一个为true,那么整体为true,不会再继续执行

赋值运算符

赋值运算符就是经过一些操作,给操作数赋值的运算符,这里直接列举出来:

运算符名称 运算符符号 运算符说明
等于 = 简单的赋值运算符,将右操作数的值赋给左侧操作数
加等于 += 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数
减等于 -= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数
乘等于 *= 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数
除等于 /= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数
取模等于 %= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数
左移等于 <<= 左移位赋值运算符
右移等于 >>= 右移位赋值运算符
按位与等于 &= 按位与赋值运算符
按位异或等于 ^= 按位异或赋值操作符
按位或等于 |= 按位或赋值操作符

其他运算符

众多赋值符中,还有一个条件赋值符(?:),这个赋值符在前面已经有讲过,这里就不再强调。

2.9 修饰符

Java中修饰符也是十分重要的一个知识模块,修饰符分为了访问控制修饰符和非访问修饰符,下面一一介绍这两种修饰符:

访问控制修饰符

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限,它们分别是default,private,protected,public,下面详细进行介绍:

修饰符名称 是否可访问当前类 是否可访问当前包 是否可访问同一包下子类 是否可访问非同一包下子类 是否可访问不同包
public
protected
default
private

接下来详细对每个访问修饰符进行介绍,首先是public修饰符:

公有访问修饰符-public,被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

这里需要提到一点,Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。

接着介绍一下protected修饰符:

受保护的访问修饰符-protected,子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
protected 可以修饰数据成员,构造方法,方法成员,不能修饰类(内部类除外)。接口及接口的成员变量和成员方法不能声明为 protected

接下来介绍default修饰符,这个修饰符说起来基本不使用:

默认访问修饰符-不使用任何关键字,使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的

最后介绍私有访问修饰符-private:

私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。
声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。
Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

非访问修饰符

Java中非访问修饰符大概包含有以下几个:

static 修饰符,用来修饰类方法和类变量。
final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
abstract 修饰符,用来创建抽象类和抽象方法。
synchronized 和 volatile 修饰符,主要用于线程的编程

这里只是简单的说明,具体的会在关键字梳理里面进行详细的介绍。下面分享极端带有解释性的代码:

/**
 * static关键字
 * @author darryl
 *
 */
public class Stustatic {

	/**
	 * static关键字修饰的方法表示为静态方法,不能调用非静态变量和方法,不依赖于对象,只要对象加载,那么就可以调用该静态方法
	 */
	private static String str="我是一个静态的字符串";
	
	private String nonString="我是一个非静态的字符串";
	
	public void getString() {
		System.out.println("这是一个public方法,我要调用"+str);
		//在非静态方法中可以调用静态方法
		staticString();
	}
	
	public static void staticString() {
		//在静态方法中可以调用静态变量,不能调用非静态变量
		System.out.println("这是一个public static方法,我要调用"+str);
		//在静态方法中不能调用非静态方法
		//在静态方法中可以调用静态方法
		staticString2();
	}
	
	public static void staticString2() {
		System.out.println("这是一个public static方法,我要调用"+str);
	}
	
	public static void main(String[] args) {
		//Stustatic.staticString();
	}
	public String getNonString() {
		return nonString;
	}

	public void setNonString(String nonString) {
		this.nonString = nonString;
	}
	/**
	 * 我们常见的main方法就是一个静态的方法,因为在调用时,不需要创建一个对象
	 * 
	 * static代码块,当且仅当在类加载时执行一次
	 */
	static {
		System.out.println("这是静态代码块执行的结果!!");
	}
	//static不允许用来修饰局部变量
}

/**
 * abstract关键字
 * 
 * @author darryl
 *
 */
public class AbstractStu {

	/**
	 * 使用abstract修饰的类表示为抽象类,抽象类的方法只需要给出原型说明,然后必须由子类实现
	 * 
	 * 跟接口不同,抽象类可以有自己的方法实现
	 * 
	 * 抽象类不能有final修饰
	 * 
	 * @author darryl
	 *
	 */
	abstract class Stu{
		//抽象类是有构造方法的,如果不写会有默认的无参构造
		abstract String getString();
		String setString() {
			return "我是抽象类中的方法!";
		}
	}
	//抽象类派生出的子类会实现父类的抽象方法
	class Stu2 extends Stu{

		@Override
		String getString() {
			// TODO Auto-generated method stub
			return null;
		}
	}
}
/**
 * 使用final修饰的类,不能再派生子类,已经到达类层次中的最底层
 * @author darryl
 *
 */
public class FinalStu {
	
	//final修饰成员变量,只能修饰一次,且必须要赋初值,直接赋值或者构造器赋值
	
	//final修饰成员变量为基本类型时,值不能变
	private final int number=12121;
	private final String string;
	
	//当final修饰的成员变量为引用类型时,可以修改他的值,但是不能修改其地址
	//如果修饰的成员变量是一个引用类型,则是说这个引用的地址的值不能修改,但是这个引用所指向的对象里面的内容还是可以改变的
	public FinalStu() {
		this.string="我就要修改你";
	}
	public FinalStu(String str) {
		this.string=str;
	}
	final class Stu{
		//该类不能派生子类
		//final类中的成员都被隐式的定义为final,非final类中private方法也被隐式定义为final
	}
	/*
	 * 如下所示,不能继承自一个final修饰的类
		class Stu2 extends stu{
		}
	*/
	class Stu2 {
		/*
		 *被final修饰的方法不能被重写 
		 */
		public final void getSting() {
		}
	}
	class Stu3 extends Stu2{
		//Cannot override the final method from FinalStu.Stu2
		//不能重写final修饰的方法
	}
}

上面的三个例子,没有对synchronized 和 volatile 两个进行介绍,后期在多线程中会提及。

3 写在后面

至此,第一阶段的Java基础知识梳理到此为止,本文只是简要的说明了Java基础知识的相关内容,适合快速梳理。文中若有不当之处,希望读者可以不吝指教。后期还会有进阶版知识梳理文章,希望读者可以多提出宝贵意见。

猜你喜欢

转载自blog.csdn.net/weixin_43071717/article/details/106636057