现在距离看完JavaSE基础课程的视频已经过去一段时间了,这段时间想想还是将一些重要的知识点整理到个人博客上,方便以后复习翻阅

Dos常用命令

怎么打开DOS命令窗口

win键 + r (组合键):可以打开“运行”窗口
在运行窗口文本框中输入: cmd
然后回车

什么是DOS命令呢?

在DOS命令窗口中才可以输入并执行DOS命令。
在最初的windows计算机中没有图形界面的,只有DOS命令窗口。
也就是说通过执行DOS命令窗口可以完全完成文件的新建、编辑、保存、删除
等一系列操作。

mkdir abc(这个命令不是必须掌握的)make directory(创建目录)

默认情况下DOS命令窗口打开之后,定位的位置是哪里?

C:\Users\Administrator 这是默认的当前路径

在DOS命令窗口中怎么复制内容?

win7:
任意位置点击右键–>标记–>选中要复制的内容–>点击右键–>此时就到剪贴板里面了
win10:
左键直接选中,然后右键单击一下就到剪贴板里面了。

切换盘符

直接输入盘符就行:
c: 回车
d: 回车
e: 回车
f: 回车
就OK了。

当切换到D盘根下了,那么当前路径就是:D:>
当前路径是当前所在的位置。

切换目录

使用cd命令来完成目录的切换:cd是什么含义?change directory(改变目录)
cd命令怎么用,语法格式是什么?
cd 路径
路径在windows系统上包括:相对路径和绝对路径。

什么是相对路径呢?
一定要注意,从路径形式上来看,相对路径是一定不会以盘符开始的。
相对路径:相对路径一定是相对于当前所在“位置”而言的。
相对路径是相对于当前而言,从当前所在的位置作为起点。

死记:相对路径一定是从当前位置作为起点开始找。

什么是绝对路径呢?
在windows操作系统中凡是路径起点是盘符的都是绝对路径,例如:
C:\Users\Administrator
C:\Users
C:\Users\Public\gakataka
C:\Windows\System32
D:\BaiduNetdiskDownload
D:\course\01-开课\OneNote

注意:
cd .. 回到上级路径。
cd \ 直接回到根路径。
. 一个点,代表当前路径。(cd命令用不着。以后配置环境变量的时候一个点有用处。)

cls 清屏

dir

查看当前目录下有啥东西。

exit

退出DOS命令窗口。

del

删除一个或者多个文件

删除T1.class文件
C:\Users\Administrator>del T1.class
删除所有.class结尾的文件,支持模糊匹配
C:\Users\Administrator>del *.class

T1.class
T1.glass
del *ass 这个命令就会将T1.class和T1.glass都删除。
删除的一定是能匹配上的。

del *.class 这个命令中的那个“.”不要特殊化,这个“.”其实就是一个普通的字母

怎么查看本机的IP地址?

什么是IP地址?有什么用呢?
A计算机在网络当中要想定位到(连接到)B计算机,那么必须要先
知道B计算机的IP地址,IP地址也可以看做计算机在同一个网络当中的
身份证号(唯一标识)。
IP地址就相当于电话号码是一个意思。
ipconfig(ip地址的配置信息。)

ipconfig /all 该命令后面添加一个/all参数可以查看更详细的网络信息。
这个详细信息中包括网卡 的物理地址,例如:70-8B-CD-A7-BA-25
这个物理地址具有全球唯一性。物理地址通常叫做MAC地址。

怎么查看两台计算机是否可以正常通信?

ping命令
语法格式:
ping IP地址
ping 域名

ping www.baidu.com
ping 61.135.169.121 (61.135.169.121是百度的IP地址)
ping 61.135.169.121 -t (-t参数表示一直ping)
一直ping的目的可以查看网络是否稳定。

在一个DOS命令窗口中如果有一个命令一直在执行,想强行终止怎么办?
ctrl + c 组合键

http://www.baidu.com 可以打开百度(这种方式比较方便,域名更容易记忆。)
http://61.135.169.121 也可以打开百度

域名底层最终还是会被解析成IP地址的形式。

文本编辑快捷键

掌握常用的通用的文本编辑快捷键很重要,可以大大提升开发效率。

所以从此刻开始强迫自己少用鼠标,用组合键快捷键的方式。

常用的组合键都有哪些?

复制 ctrl + c
粘贴 ctrl + v
剪切 ctrl + x
保存 ctrl + s

撤销 ctrl + z
重做 ctrl + y

回到行首:home键
回到行尾:end键

当光标在行尾,怎么选中一行?
shift + home键
当光标在行首,怎么选中一行?
shift + end键

回到文件头:ctrl + home
回到文件尾:ctrl + end

全选:ctrl + a

查找:ctrl + f
—————————(以上必须会用)————————–
选中一个单词:鼠标双击
选中一行:鼠标连续击3次

不用鼠标选中一个单词:ctrl + shift + 右箭头/左箭头


Java语言的特性

简单性

在Java语言当中真正操作内存的是:JVM(Java虚拟机)
所有的java程序都是运行在Java虚拟机当中的。
而Java虚拟机执行过程中再去操作内存。

对于C或者C++来说程序员都是可以直接通过指针操作内存的。
C或者C++更灵活,可以直接程序员操作内存,但是要求程序员技术精湛。
C语言或者C++更有驾驭感。

Java语言屏蔽了指针概念,程序员不能直接操作指针,或者说程序员
不能直接操作内存。

这种方式有优点也有缺点:

优点:不容易导致内存泄漏

缺点:效率问题,包括驾驭感比较差

Java语言底层是C++,所以JVM是用C++语言写好的一个虚拟的电脑。
JVM在哪里?安装了JDK之后JVM就安装好了。

java是堪称完全面向对象的。

面向对象更容易让人理解,人类通常是以对象的方式认知世界的。
采用面向对象的方式可以让复杂问题简单化。

健壮性

主要是因为Java中有一种机制:

自动垃圾回收机制(GC机制)。

java语言是健壮的,相对于C语言来说,C语言没有Java健壮。
Java不容易导致内存的泄漏。
C++或者C语言使用不当时很容易导致内存泄漏。

JVM负责调度GC机制。程序员不需要干涉。

java完全/完美支持多线程并发。

可移植性/跨平台

java语言只要编写一次,可以做到到处运行。

例如:java程序编写完之后,可以运行在windows操作系统上,
不需要做任何改动可以直接运行在Linux操作系统上,同样也
可以运行到MaC OS上面。

JVM这种机制实现了跨平台,那么这种机制优点和缺点分别是什么?

优点:一次编写到处运行,可以跨平台。

缺点:麻烦。对于运行java程序来说必须先有一个JVM。

就像你要想在网页上看视频,你必须先安装一个flash是一样的。

Java语言可以编写病毒吗?

可以,没问题。但是很难让用户中毒。
中毒的一般都是java程序员。所以很少有人编写java的病毒脚本。

JDK、JRE、JVM三者之间的关系

JDK:Java开发工具箱

JRE:java运行环境

JVM:java虚拟机

JDK包括JRE,JRE包括JVM。    
JVM是不能独立安装的。    
JRE和JDK都是可以独立安装的。

有单独的JDK安装包。    
也有单独的JRE安装包。
没有单独的JVM安装包。


​ 安装JDK的时候:JRE就自动安装了,同时JRE内部的JVM也就自动安装了。
​ 安装JRE的时候:JVM也就自动安装了。

假设你在软件公司开发了一个新的软件,现在要去客户那边给客户把项目部署一下,把项目跑起来,你需要安装JDK吗?

只需要安装JRE就行了。
JRE体积很小,安装非常便捷快速。

为什么安装JDK的时候会自带一个JRE?

因为java程序员开发完程序之后,要测试这个程序,
让这个程序运行起来,需要JRE。所以JDK安装的时候
内部自带一个JRE。

对Java的加载与执行的理解

java程序从编写到最终运行经历了哪些过程?

java程序非常重要的两个阶段:

编译阶段
运行阶段

注意:java程序员直接编写的java代码(普通文本)是无法执行被JVM
识别的。java程序员编写的java代码这种普通文本必须经过一个编译,
将这个“普通文本代码”变成“字节码”,JVM能够识别“字节码”。
java代码这种普通文本变成字节码的过程,被称为:编译。

java代码这种普通文本被称为:java源代码。(你编写的代码是源代码)
源代码不能直接执行,需要先进行编译,生成源代码对应的“字节码”
JVM可以识别的是字节码。

编译阶段和运行阶段可以在不同的操作系统上完成吗?

在windows上编译
编译之后生成了“字节码”
把“字节码”放到linux上运行
完全可以,因为Java是跨平台的。
可以做到一次编写到处运行。

java源代码一旦编译之后,源代码可以删除吗?只留下字节码可以执行吗?

完全可以执行,因为源代码不参与程序的执行过程。
参与程序执行过程的是字节码。
但是最好不要删除源代码。因为有可能执行结果不是你需要的,
当执行结果不是你需要的时候,你可以重新打开源代码进行修改,
然后重新编译生成新的字节码,再重新执行。这样会有新的执行效果。

.java文件就是源文件,这个文件中编写源代码。
.class文件就是字节码文件,这个文件是编译源代码而得到的。

另外需要注意的是:
1个java源文件是可以编译生成多个class文件的。
最终运行的是class文件。

字节码文件是二进制文件吗?

字节码文件不是二进制文件。
如果是二进制的话,就不需要JVM了。
因为操作系统可以直接执行二进制。

java程序从开发到最终运行经历了什么?

编译期:(可以在windows上)

第一步:在硬盘的某个位置(随意),新建一个xxx.java文件

第二步:使用记事本或者其它文本编辑器例如EditPlus打开xxx.java文件

第三步:在xxx.java文件中编写“符合java语法规则的”源代码。

第四步:保存(一定要将xxx.java文件保存一下)

第五步:使用编译器(javac【JDK安装后自带】)对xxx.java文件进行编译。

第六步:如果xxx.java文件中编写的源代码是符合语法规则的,编译会通过,如果xxx.java文件中编写的源代码违背了语法规则,那么编译器会报错,
编译器报错之后class文件是不会生成的,只有编译通过了才会生成class字节码文件。并且一个java源文件是可以生成多个class文件的。(编译实质上是检查语法)

运行期(JRE在起作用):(可以在windows上,也可以在其他的OS上。)

第七步:如果是在Linux上运行,需要将windows上生成的class文件拷贝过去
不需要拷贝源代码,真正运行的是字节码。(但是源代码也不要删除,有用)

第八步:使用JDK自带的一个命令/工具:java(负责运行的命令/工具)执行字节码

第九步:往下的步骤就全部交给JVM了,就不需要程序员干涉了。
JVM会将字节码文件装载进去,然后JVM对字节码进行解释(解释器负责将字节码
解释为1010101010..等的二进制)

第十步:JVM会将生成的二进制码交给OS操作系统,操作系统会执行二进制码和
硬件进行交互。

标识符 关键字 变量

标识符

1、标识符可以标识什么,什么是标识符

2、标识符的命名规则

3、标识符的命名规范

关键字

1、什么是关键字?

在SUN公司开发Java语言的时候,提前定义好了一些具有特殊含义的单词,
这些单词全部小写,具有特殊含义,不能用作标识符。

2、切记:

java语言中的所有关键字都是全部小写。
注意:java语言中是严格区分大小写的。public和Public不一样。
Class和class不一样。static和Static也不一样。

3、那么关键字有哪些呢,我们需要背会吗?需要单独去记忆吗?

关键字:
    public 
    static
    void 
    class
    byte
    short
    int
    long 
    float
    double
    boolean
    char
    true
    false
    if
    while
    for
    private
    protected
    ........
对于这些关键字来说大家不需要单独花费时间去记忆,
随着后面程序的积累,你会接触到所有的关键字。

变量

字面量

字面量就是数据
数据就是字面量
是一个东西。

10 100 123 :整型

1.34 3.14 2.0:浮点型

true false :布尔型

'a' '国':字符型

"a" "abc"  "国" "中国":字符串型

10:整数,是一个数字

"10":它不是数字,是一个字符串,或者说,它属于“文字类”的。

性质完全不同,在计算机中的对应的二进制码也是完全不同的。

变量

变量的理解

什么是变量?
    变量就是一个存数据盒子。(盒子大小谁来决定?数据类型)
    在内存中的最基本的存储单元。
    存数据用的,而且这个数据是可变的,所以叫做变量。

变量的使用

变量的三要素:

数据类型、变量名、值 (值就是数据,就是字面量。)

int i = 100;

java中的变量必须先声明,再赋值才能访问(必须手动赋值。)
int k; System.out.println(k); 这样是不行的。

可以在一行上声明多个变量:
    int a, b, c = 100;
    c变量赋值100,a,b变量只声明了没有赋值。
    int a = 10, b = 20, c = 100;
    可以这样每个都赋值。

声明和赋值可以分开,也可以一起做
    int i;
    i = 100; // 先声明再赋值
    int k = 200; // 声明的同时赋值

在“同一个域”当中,变量名不能重名
但可以重新赋值
    {
        int i = 100;
        //double i = 2.0; // 重名了编译器会报错,不允许。
        i = 300; // 可以重新赋值。
    }

到底什么叫做同一个域?
这个目前不太好解释,大家记住吧:一个大括号代表一个域。
    {A域
        {B域
            {C域
            }
        }
    }
    A域包括B域,B域包括C域。

变量的分类

根据位置进行分类:记住就行
在方法体当中声明的变量叫做局部变量。
    public static void m1(){
        //局部变量,方法执行结束之后内存释放。
        int k = 100;
        int i = 200;
    }
在方法体外以及类体内声明的变量叫做成员变量。
    public class T{
        public static void x(){
        
        }
        // 成员变量
        int i = 200;
        }

变量的作用域

出了大括号就不认识了。别的先别管。

{
    int i = 100;
    {
        在这里可以访问i
    }
}

{
    在这里是无法访问i变量。
}

数据类型

数据类型有什么用?

数据类型用来声明变量,程序在运行过程中根据不同的数据类型分配不同大小的空间。
int i = 10;
double d = 1.23;
i变量和d变量类型不同,空间大小不同。

数据类型在java语言中包括两种:

第一种:基本数据类型

基本数据类型又可以划分为4大类8小种:
    第一类:整数型
        byte,short,int,long (没有小数的)
    第二类:浮点型 
        float,double (带有小数的)
    第三类:布尔型
        boolean:只有两个值true和false,true表示真,false表示假
    第四类:字符型
        char:java中规定字符型字面量必须使用单引号括起来。属于文字。

8小种:
    byte,short,int,long
    float,double
    boolean
    char

第二种:引用数据类型

字符串型String属于引用数据类型。
String字符串不属于基本数据类型范畴。
java中除了基本数据类型之外,剩下的都是引用数据类型。
引用数据类型后期面向对象的时候才会接触。

8种基本数据类型

字符型 char

整数型 byte short int long

浮点型 float double

布尔型 boolean

整数型:byte short int long有什么区别?
浮点型:float和double有什么区别?
区别:占用的空间大小不同。

关于计算机存储单位?
    计算机只能识别二进制。(1001101100...)
    1字节 = 8bit(8比特)--> 1byte = 8bit
    1bit就是一个1或0.
    1KB = 1024byte
    1MB = 1024KB
    1GB = 1024MB
    1TB = 1024GB

    byte b = 2; 在计算机中是这样表示的:00000010
    short s = 2; 在计算机中是这样表示的:00000000 00000010
    int i = 2;在计算机中是这样表示的:00000000 00000000 00000000 00000010
    ...

    类型            占用字节数量(byte)
    ------------------------------------
    byte                1
    short                2
    int                4
    long                8
    float                4
    double            8
    boolean            1  (1byte的1或0,00000001(true)或00000000(false))
    char                2

关于二进制

十进制转换成二进制

二进制转换成十进制

    2的2次方   2的1次方 2的0次方
    1            1            1
    4            2            1

    1*4 + 1*2 + 1*1 = 7


    2的2次方   2的1次方 2的0次方
    1            0            1
    4            2            1

    1*4 + 0*2 + 1*1 = 5

byte类型的取值范围?

byte是 [-128 ~ 127] 共可以标识256个不同的数字。
byte类型的最大值是怎么计算出来的?
    byte是1个字节,是8个比特位,所以byte可以存储的最大值是:
        01111111
注意:在计算机当中,一个二进制位最左边的是符号位,当为0时表示正数,
当为1时表示负数。所以byte类型最大值是:01111111
那么是不是2的7次方-1呢?
    是不是:10000000(前边是一个二进制) - 1

byte类型最大值是:2的7次方 - 1.

有几个取值范围需要大家记住:
    (1个字节)byte: [-128 ~ 127]
    (2个字节)short:[-32768 ~ 32767] 可以表示65536个不同的数字
    (4个字节)int: [-2147483648 ~ 2147483647]
    (2个字节)char: [0~65535]  可以表示65536个不同的数字

    short和char实际上容量相同,不过char可以表示更大的数字。
    因为char表示的是文字,文件没有正负之分,所以char可以表示
    更大的数字。

对于8种基本数据类型来说:

其中byte,short,int,long,float,double,boolean,这7种类型计算机表示起来
比较容易,因为他们都是数字。其中布尔类型只有两个值true和false,实际上
true和false分别在C++中对应的是1和0,1为true,false为0。

对于char类型来说计算机表示起来比较麻烦,因为char对应的是文字,每一个国家
的文字不一样,文字不能直接通过“自然算法”转换成二进制。这个时候怎么办?
字符编码诞生了。

什么是字符编码?
字符编码是人为的定义的一套转换表。
在字符编码中规定了一系列的文字对应的二进制。
字符编码其实本质上就是一本字典,该字段中描述了文字与二进制之间的对照关系。
字符编码是人为规定的。(是某个计算机协会规定的。)

字符编码涉及到编码和解码两个过程,编码和解码的时候必须采用同一套字符编码
方式,不然就会出现乱码。

关于字符编码的发展过程?
起初的时候计算机是不支持文字的,只支持科学计算。实际上计算机起初是为了
战争而开发的,计算导弹的轨道….

后来随着计算机的发展,计算机开始支持文字,最先支持的文字是英文,英文
对应的字符编码方式是:ASCII码。

ASCII码采用1byte进行存储,因为英文字母是26个。(键盘上所有的键全部算上也
超不过256个。1byte可以表示256种不同的情况。所以英文本身在计算机方面就占有
优势。)

'a' --(采用ASCII码进行编码)-> 01100001
01100001 --(采用ASCII码进行解码)-> 'a'
如果编码和解码采用的不是同一个编码方式,会出现乱码。
'b' ---> 98
'c' ---> 99...
'a' ---> 97

'A' ---> 65
'B' ---> 66
...

'0' ---> 48  (这个'0'不是那个0,是文字'0')
'1' ---> 49

随着计算机语言的发展,后来国际标准组织制定了ISO-8859-1编码方式,
又称为latin-1编码方式,向上兼容ASCII码。但不支持中文。

后来发展到亚洲,才支持中文,日文,韩文….
中文这块的编码方式:GB2312<GBK<GB18030 (容量的关系)
以上编码方式是简体中文。

繁体中文:big5(台湾使用的是大五码。)

在java中,java语言为了支持全球所有的文字,采用了一种字符编码方式
叫做unicode编码。unicode编码统一了全球所有的文字,支持所有文字。
具体的实现包括:UTF-8 UTF-16 UTF-32….

在类型转换的时候需要遵循哪些规则?

第一条:八种基本数据类型中,除 boolean 类型不能转换,剩下七种类型之间都可以
进行转换;

第二条:如果整数型字面量没有超出 byte,short,char 的取值范围,可以直接将其赋
值给byte,short,char 类型的变量;

第三条:小容量向大容量转换称为自动类型转换,容量从小到大的排序为:
byte < short(char) < int < long < float < double,其中 short和 char 
都占用两个字节,但是char 可以表示更大的正整数;

第四条:大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,
但运行时可能出现精度损失,谨慎使用;

第五条:byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算;

第六条:多种数据类型混合运算,各自先转换成容量最大的那一种再做运算;

所有的笔试题都超不出以上的6条规则。死记硬背。

运算符

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

关系运算符:
    > >= < <= == != 

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

赋值运算符:
    = += -= *= /= %=

三目运算符:
    布尔表达式 ? 表达式1 : 表达式2

字符串连接运算符:
    +    

控制语句

控制语句包括几类?

3类:

选择语句
循环语句
转向语句

选择语句(也可以叫做分支语句)

if语句
switch语句

选择语句/分支语句 if

四种写法。

语法机制:
    if(布尔表达式){
    }
    
    if(布尔表达式){
    }else{
    }

    if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }

    if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }else if(布尔表达式){
    }else{
    }

    if语句嵌套:
        if(布尔表达式){ //前提条件
            if(布尔表达式){
                if(布尔表达式){
                
                }else{
                
                }
            }
        }else{
        
        }
    
执行原理:
    对于一个if语句来说,只要有1个分支执行,整个if语句结束。
    当布尔表达式的结果为true时,分支才会执行。
    分支当中只有一条java语句,大括号可以省略。
    带有else的可以保证肯定会有一个分支执行。

选择语句/分支语句 switch

完整语法结构:
    switch(值){ //值允许是String、int,(byte,short,char可以自动转换int)
    case 值1: case 值x:
        java语句;
        break;
    case 值2:
        java语句;
        break;
    case 值3:
        java语句;
        break;
    default:
        java语句;
    }

循环语句(主要循环反复的去执行某段特定的代码块)

for循环
while循环
do..while..循环

for循环

for循环语法机制:
    for(初始化表达式;条件表达式;更新表达式){
        循环体;
    }

    for(int i = 0; i < 10; i++){
        System.out.println(i);
    }

for循环执行原理:
    1、先执行初始化表达式,并且只执行1次。
    2、然后判断条件表达式
    3、如果为true,则执行循环体。
    4、循环体结束之后,执行更新表达式。
    5、继续判断条件,如果条件还是true,继续循环。
    6、直到条件为false,循环结束。

while循环

while(布尔表达式){
    循环体;
}
执行次数:0~N次。

do..while循环

do{
    循环体;
}while(布尔表达式);

执行次数:1~N次。

转向语句

break
continue
return

break;

默认情况下,终止离它最近的循环。
当然,也可以通过标识符的方式,终止指定的循环。

for(int i = 0; i < 10; i++){
    if(i == 5){
        break;
    }
    code1;
    code2;
    code3;
    code4;
    ....
}

continue;

终止当前“本次”循环,直接跳入下一次循环继续执行。
for(int i = 0; i < 10; i++){
    if(i == 5){
        continue;
    }
    code1;
    code2;
    code3;
    code4;
    ....
}

方法

方法是什么?

方法(method)是可以完成某个特定功能的并且可以被重复利用的代码片段。
在C语言中被称为“函数”。在java中不叫函数,叫做方法。

方法是一段可以完成某个特定功能的并且可以被重复利用的代码片段。
方法的出现,让代码具有了很强的复用性。

方法的定义

[修饰符列表] 返回值类型 方法名(形式参数列表){
    方法体; 
}

方法的每一个细节学习

1、修饰符列表:可选项,目前先写成:public static 
2、怎么理解返回值?返回值是一个方法执行结束之后的结果。
3、返回值类型都可以指定哪些类型?
4、返回值和“return语句”的关系。
5、方法名只要是合法的标识符就行,首字母小写,后面每个单词首字母大写。见名知意。
6、形式参数列表
7、方法体:方法体当中的代码遵循自上而下的顺序依次逐行执行。
8、方法怎么调用?“类名.”什么时候可以省略?
    实际参数列表,简称实参。(调用方法时传递的实际数据。)
    实参和形参的关系是一一对应。

JVM的内存结构中三块比较重要的内存空间。

方法区:
    存储代码片段,存储xxx.class字节码文件,这个空间是最先有数据的,
    类加载器首先将代码加载到这里。
堆内存:
    后面讲(面向对象)
栈内存:
    stack栈当中存储什么?
        每个方法执行时所需要的内存空间(局部变量)。

关于数据结构中的栈数据结构

原则:
    后进先出
    先进后出
栈数据结构相关的术语:
    栈帧:永远指向栈顶部的元素(栈顶元素具有活跃权。)
    栈顶元素
    栈底元素
    压栈,入栈,进栈,push
    弹栈,出栈,pop

分析程序运行过程中的内存变化

方法只定义不调用是不会执行的。
方法调用时:压栈 (在栈中给该方法分配空间)
方法执行结束时:弹栈(将该方法占用的空间释放,局部变量的内存也释放。)

方法重载overload

1、什么情况下我们考虑使用方法重载机制?
    当功能相似的时候,建议将方法名定义为一致的,
    这样代码美观,又方便编程。

    注意:如果功能不相似,坚决要让方法名不一致。

2、代码满足什么条件的时候构成了方法重载?
    条件1:在同一个类当中
    条件2:方法名相同
    条件3:形式参数列表不同(类型、个数、顺序)

    注意:
        方法重载和返回值类型无关,和修饰符列表无关。

3、方法重载的优点?
    代码美观
    方便代码的编写

方法递归

1、需要理解什么是方法递归?
    方法自身调用自身。

2、使用递归的时候,必须添加结束条件,没有结束条件,会发生栈内存溢出错误。
    StackOverflowError
    原因:一直压栈,没有弹栈,栈内存不够用。

3、会画出递归方法的内存结构图。
    递归的过程当中可以将图画出来。

4、能够使用循环代替递归的尽量使用循环,循环的执行耗费内存少一些,
递归耗费内存相对多一些,另外递归使用不当很容易内存溢出,JVM停止工作。
当然,只有极少数情况下,只能用递归,其它代码解决不了问题。

5、当递归有结束条件,并且结束条件合法的时候,就一定不会内存溢出吗?
    也不一定。可能递归的太深了。

6、分享了一些递归方面的经验
    在实际的开发中遇到递归导致的栈内存溢出错误是怎么办?
        第一步:先检查结束条件是否正确。
        第二步:如果正确,可以调整JVM的栈内存大小。(java -X)

面向对象

面向过程和面向对象有什么区别?

从语言方面出发:

对于C语言来说,是完全面向过程的。
对于C++语言来说,是一半面向过程,一半是面向对象。(C++是半面向对象的)
对于Java语言来说,是完全面向对象的。

什么是面向过程的开发方式?

面向过程的开发方式主要的特点是:

注重步骤,注重的是实现这个功能的步骤。
第一步干什么
第二步干什么
....
另外面向过程也注重实现功能的因果关系。
    因为A所有B
    因为B所以C
    因为C所以D
    .....
面向过程中没有对象的概念。只是实现这个功能的步骤以及因果关系。

面向过程有什么优点?

快速开发。

对于小型项目(功能),采用面向过程的方式进行开发,效率较高。
不需要前期进行对象的提取,模型的建立,采用面向过程
方式可以直接开始干活。一上来直接写代码,编写因果关系。
从而实现功能。

面向过程有什么缺点?

耦合度高,扩展力差。

面向过程最主要是每一步与每一步的因果关系,其中A步骤因果关系到B
步骤,A和B联合起来形成一个子模块,子模块和子模块之间又因为因果
关系结合在一起,假设其中任何一个因果关系出现问题(错误),此时
整个系统的运转都会出现问题。(代码和代码之间的耦合度太高,扩展力
太差。)

螺栓螺母拧在一起:耦合度高吗?
    这是耦合度低的,因为螺栓螺母可以再拧开。(它们之间是有接口的。)
螺栓螺母拧在一起之后,再用焊条焊接在一起,耦合度高吗?
    这个耦合度就很高了。耦合度就是黏连程度。
    往往耦合度高的扩展力就差。

耦合度高导致扩展力差。(集成显卡:计算机显卡不是独立的,是集成到主板上的)
耦合度低导致扩展力强。(灯泡和灯口关系,螺栓螺母关系)    

什么是面向对象的开发方式?

采用面向对象的方式进行开发,更符合人类的思维方式。(面向对象成为主流的原因)
人类就是以“对象”的方式去认识世界的。
所以面向对象更容易让我们接受。

面向对象就是将现实世界分割成不同的单元,然后每一个单元
都实现成对象,然后给一个环境驱动一下,让各个对象之间协
作起来形成一个系统。

采用面向对象的方式进行开发:
    耦合度低,扩展力强。

当我们采用面向对象的方式贯穿整个系统的话,涉及到三个术语:

OOA:面向对象分析
OOD:面向对象设计
OOP:面向对象编程

整个软件开发的过程,都是采用OO进行贯穿的。

实现一个软件的过程:
    分析(A) --> 设计(D) --> 编程(P)

面向对象包括三大特征

封装 继承 多态

任何一个面向对象的编程语言都包括这三个特征
例如:
    python也有封装 继承 多态。
    java也有封装 继承 多态。

    注意:java只是面向对象编程语言中的一种。
    除了java之外,还有其它很多很多的编程语言也是面向对象的。

类和对象的概念

面向对象当中最主要“一词”是:对象。

什么是类?

    类实际上在现实世界当中是不存在的,是一个抽象的概念。
    是一个模板。是我们人类大脑进行“思考、总结、抽象”的一个
    结果。(主要是因为人类的大脑不一般才有了类的概念。)

    类本质上是现实世界当中某些事物具有共同特征,将这些共同
    特征提取出来形成的概念就是一个“类”,“类”就是一个模板。

什么是对象?

    对象是实际存在的个体。(真实存在的个体)

在java语言中,要想得到“对象”,必须先定义“类”,“对象”是通过“类”
这个模板创造出来的。

类就是一个模板:类中描述的是所有对象的“共同特征信息”
对象就是通过类创建出的个体。

类:不存在的,人类大脑思考总结一个模板(这个模板当中描述了共同特征。)
对象:实际存在的个体。
实例:对象还有另一个名字叫做实例。
实例化:通过类这个模板创建对象的过程,叫做:实例化。
抽象:多个对象具有共同特征,进行思考总结抽取共同特征的过程。

类 --【实例化】--> 对象(实例)
对象 --【抽象】--> 类

类是一个模板,是描述共同特征的一个模板,那么共同特征包括什么呢?

潘长江对象:
    名字:潘长江
    身高:165cm

    打篮球:非专业的,自己玩儿呢,无所谓了
    学习:考试80分

姚明对象:
    名字:姚明
    身高:240cm
    打篮球:NBA专业球员,打篮球非常棒
    学习:考试100分

共同特征包括哪些?

    名字、身高都属于名词(状态特征)
    打篮球、学习都属于动词(动作特征)

类 = 属性 + 方法

属性来源于:状态

方法来源于:动作

public class 明星类{

    //属性-->状态,多见于名词
    名字属性;
    身高属性;

    //方法-->动作,多见于动词
    打篮球方法(){
    
    }
    学习方法(){
    
    }
}

类的定义

怎么定义一个类,语法格式是什么?

[修饰符列表] class 类名 {
    //类体 = 属性 + 方法
    // 属性在代码上以“变量”的形式存在(描述状态)
    // 方法描述动作/行为
}

注意:修饰符列表可以省略。

为什么属性是“以”变量的形式存在的?

假设我们要描述一个学生:
    学生包括哪些属性:
        学号: 110
        姓名:"张三"
        性别:'男' (true/false)
        住址:"深圳宝安区"
答案:是因为属性对应的是“数据”,数据在程序中只能放到变量中。
结论:属性其实就是变量。

变量根据出现位置进行划分:

    方法体当中声明的变量:局部变量。
    方法体外声明的变量:成员变量。(这里的成员变量就是“属性”)

在语法级别上是怎么完成对象创建的呢?

类名 变量名 = new 类名();
这样就完成了对象的创建。

什么是实例变量?

对象又被称为实例。

实例变量实际上就是:对象级别的变量。

public class 明星类{
    double height;
}

身高这个属性所有的明星对象都有,但是每一个对象都有“自己的身高值”。
假设创建10个明星对象,height变量应该有10份。
所以这种变量被称为对象级别的变量。属于实例变量。

对象和引用的区别?

对象是通过new出来的,在堆内存中存储。
引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的。

封装 继承 多态

封装

面向对象的三大特征:

封装 继承 多态

有了封装,才有继承,有了继承,才能说多态。

面向对象的首要特征:封装 。

什么是封装?有什么用?

现实生活中有很多现实的例子都是封装的,例如:
手机,电视机,笔记本电脑,照相机,这些都是外部有一个坚硬的壳儿。
封装起来,保护内部的部件。保证内部的部件是安全的。另外封装了之后,
对于我们使用者来说,我们是看不见内部的复杂结构的,我们也不需要关心
内部有多么复杂,我们只需要操作外部壳儿上的几个按钮就可以完成操作。

封装的作用有两个:

    第一个作用:保证内部结构的安全。
    第二个作用:屏蔽复杂,暴露简单。

在代码级别上,封装有什么用?

一个类体当中的数据,假设封装之后,对于代码的调用人员来说,
不需要关心代码的复杂实现,只需要通过一个简单的入口就可以访问了。
另外,类体中安全级别较高的数据封装起来,外部人员不能随意访问,
来保证数据的安全性。

怎么进行封装,代码怎么实现?

第一步:属性私有化(使用private关键字进行修饰。)
第二步:对外提供简单的操作入口。

封装的代码实现两步:

第一步:属性私有化

第二步:1个属性对外提供两个set和get方法。外部程序只能通过set方法修改,只能通过get方法读取,
可以在set方法中设立关卡来保证数据的安全性。

强调:
    set和get方法都是实例方法,不能带static。
    不带static的方法称为实例方法,实例方法的调用必须先new对象。

set和get方法写的时候有严格的规范要求:
    set方法长这个样子:
        public void set+属性名首字母大写(1个参数){
            xxx = 1个参数;
        }
    get方法长这个样子:
        public 返回值类型 get+属性名首字母大写(无参){
            return xxx;
        }

继承(extends)

什么是继承,有什么用?

继承:在现实世界当中也是存在的,例如:父亲很有钱,儿子不用努力也很有钱。
继承的作用:
    基本作用:子类继承父类,代码可以得到复用。(这个不是重要的作用,是基本作用。)
    主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。

继承的相关特性

B类继承A类,则称A类为超类(superclass)、父类、基类,
B类则称为子类(subclass)、派生类、扩展类。
    class A{}
    class B extends A{}
    我们平时聊天说的比较多的是:父类和子类。
    superclass 父类
    subclass 子类

java 中的继承只支持单继承,不支持多继承,C++中支持多继承,
这也是 java 体现简单性的一点,换句话说,java 中不允许这样写代码:
    class B extends A,C{ } 这是错误的。

虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,
例如:class C extends B,class B extends A,也就是说,C 直接继承 B,
其实 C 还间接继承 A。

java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。
但是私有的属性无法在子类中直接访问。(父类中private修饰的不能在子类中
直接访问。可以通过间接的手段来访问。)

java 中的类没有显示的继承任何类,则默认继承 Object类,Object类是 
java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有 
Object类型中所有的特征。

继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它们之间的耦合度非常高,
Account 类发生改变之后会马上影响到 CreditAccount 类

子类继承父类之后,能使用子类对象调用父类方法吗?

可以,因为子类继承了父类之后,这个方法就属于子类了。
当然可以使用子类对象来调用。

在实际开发中,满足什么条件的时候,我可以使用继承呢?

凡是采用“is a”能描述的,都可以继承。

例如:
    Cat is a Animal:猫是一个动物
    Dog is a Animal:狗是一个动物
    CreditAccount is a Account:信用卡账户是一个银行账户
    ....

假设以后的开发中有一个A类,有一个B类,A类和B类确实也有重复的代码,
那么他们两个之间就可以继承吗?不一定,还是要看一看它们之间是否能够
使用is a来描述。

    class Customer{
        String name; // 名字
        // setter and getter
    }

    class Product{
        String name; // 名字
        // setter and getter
    }

    class Product extends Customer{
    
    }

    以上的继承就属于很失败的。因为:Product is a Customer,是有违伦理的。

任何一个类,没有显示继承任何类,默认继承Object,那么Object类当中有哪些方法呢?

以后慢慢的大家一定要适应看JDK的源代码(多看看牛人写的程序自己才会变成牛人。)
先模仿后超越。

java为什么比较好学呢?
    是因为Java内置了一套庞大的类库,程序员不需要从0开始写代码,程序员可以
    基于这套庞大的类库进行“二次”开发。(开发速度较快,因为JDK内置的这套库
    实现了很多基础的功能。)

    例如:String是SUN编写的字符串类、System是SUN编写的系统类。
    这些类都可以拿来直接使用。

JDK源代码在什么位置?
    C:\Program Files\Java\jdk-13.0.2\lib\src.zip

你现在能看懂以下代码了吗?
    System.out.println("Hello World!");
    System.out 中,out后面没有小括号,说明out是变量名。
    另外System是一个类名,直接使用类名System.out,说明out是一个静态变量。
    System.out 返回一个对象,然后采用“对象.”的方式访问println()方法。

我们研究了一下Object类当中有很多方法,大部分看不懂,其中有一个叫做toString()
的,我们进行了测试,发现:
    System.out.println(引用);
    当直接输出一个“引用”的时候,println()方法会先自动调用“引用.toString()”,然后
    输出toString()方法的执行结果。

多态

向上转型和向下转型的概念

向上转型:子--->父 (upcasting)
    又被称为自动类型转换:Animal a = new Cat();

向下转型:父--->子 (downcasting)
    又被称为强制类型转换:Cat c = (Cat)a; 需要添加强制类型转换符。
    什么时候需要向下转型?
        需要调用或者执行子类对象中特有的方法。
        必须进行向下转型,才可以调用。

向下转型有风险吗?

容易出现ClassCastException(类型转换异常)

怎么避免这个风险?

instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象
是否为某一种类型。
养成好习惯,向下转型之前一定要使用instanceof运算符进行判断。

不管是向上转型还是向下转型,首先他们之间必须有继承关系,这样编译器就不会报错。

什么是多态

多种形态,多种状态,编译和运行有两个不同的状态。
编译期叫做静态绑定。
运行期叫做动态绑定。
Animal a = new Cat();
// 编译的时候编译器发现a的类型是Animal,所以编译器会去Animal类中找move()方法
// 找到了,绑定,编译通过。但是运行的时候和底层堆内存当中的实际对象有关
// 真正执行的时候会自动调用“堆内存中真实对象”的相关方法。
a.move();

多态的典型代码:父类型的引用指向子类型的对象

什么时候必须进行向下转型?

调用子类对象上特有的方法时。

多态在开发中有什么作用?

多态在开发中的作用是:
降低程序的耦合度,提高程序的扩展力。

    public class Master{
        public void feed(Dog d){}
        public void feed(Cat c){}
    }
    以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。

    public class Master{
        public void feed(Pet pet){
            pet.eat();
        }
    }
    以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
    这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。

面向对象的三大特征:

    封装、继承、多态

    真的是一环扣一环。

    有了封装,有了这种整体的概念之后。
    对象和对象之间产生了继承。
    有了继承之后,才有了方法的覆盖和多态。

这里提到了一个软件开发原则:
    七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
    目的是:降低程序耦合度,提高程序扩展力。
    面向抽象编程,不建议面向具体编程。

构造方法

1、当一个类中没有提供任何构造方法,系统默认提供一个无参数的构造方法。
这个无参数的构造方法叫做缺省构造器。

2、当一个类中手动的提供了构造方法,那么系统将不再默认提供无参数构造方法。
建议将无参数构造方法手动的写出来,这样一定不会出问题。

3、无参数构造方法和有参数的构造方法都可以调用。

Student x = new Student();
Student y = new Student(123);

4、构造方法支持方法重载吗?

构造方法是支持方法重载的。
在一个类当中构造方法可以有多个。
并且所有的构造方法名字都是一样的。

方法重载特点:
在同一个类中,方法名相同,参数列表不同。

5、对于实例变量来说,只要你在构造方法中没有手动给它赋值,
统一都会默认赋值。默认赋系统值。

方法覆盖

什么时候考虑使用方法覆盖?

父类中的方法无法满足子类的业务需求,子类有必要对继承过来的方法进行覆盖。

什么条件满足的时候构成方法覆盖?

第一:有继承关系的两个类
第二:具有相同方法名、返回值类型、形式参数列表
第三:访问权限不能更低。
第四:抛出异常不能更多。

关于Object类中toString()方法的覆盖?

toString()方法存在的作用就是:将java对象转换成字符串形式。
大多数的java类toString()方法都是需要覆盖的。因为Object类中提供的toString()
方法输出的是一个java对象的内存地址。

至于toString()方法具体怎么进行覆盖?
    格式可以自己定义,或者听需求的。

方法重载和方法覆盖有什么区别?

1
方法重载发生在同一个类当中。

方法覆盖是发生在具有继承关系的父子类之间。


方法重载是一个类中,方法名相同,参数列表不同。

方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致:
    方法名一致、参数列表一致、返回值类型一致。

this关键字

1、this是一个关键字,是一个引用,保存内存地址指向自身。

2、this可以使用在实例方法中,也可以使用在构造方法中。

3、this出现在实例方法中其实代表的是当前对象。

4、this不能使用在静态方法中。

5、this. 大部分情况下可以省略,但是用来区分局部变量和实例变量的时候不能省略。

6、this() 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用。

Super关键字

super能出现在实例方法和构造方法中。

super的语法是:

“super.”、“super()”

super不能使用在静态方法中。

super. 大部分情况下是可以省略的。

super.什么时候不能省略呢?

父类和子类中有同名属性,或者说有同样的方法,
想在子类中访问父类的,super. 不能省略。

super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中
的构造方法,

目的是:创建子类对象的时候,先初始化父类型特征。

super的使用:

super.属性名                【访问父类的属性】
super.方法名(实参)        【访问父类的方法】
super(实参)                    【调用父类的构造方法】

final关键字

1、final修饰的类无法继承。

2、final修饰的方法无法覆盖。

3、final修饰的变量只能赋一次值。

4、final修饰的引用一旦指向某个对象,则不能再重新指向其它对象,但该引用指向的对象内部的数据是可以修改的。

5、final修饰的实例变量必须手动初始化,不能采用系统默认值。

6、final修饰的实例变量一般和static联合使用,称为常量。

public static final double PI = 3.1415926;

关于IDEA工具

字体设置

file --> settings --> 输入font --> 设置字体样式以及字号大小。

快速生成main方法

psvm

快速生成System.out.println()

sout 

注意:IDEA是自动保存,不需要ctrl + s

删除一行

ctrl + y

怎么运行:

代码上右键-->run
或者点击左侧的绿色箭头。
ctrl + shift + F10

左侧窗口中的列表怎么展开?怎么关闭?

左箭头关闭。
右箭头展开。
上下箭头移动。

idea中退出任何窗口,都可以使用esc键盘。(esc就是退出)

任何新增/新建/添加的快捷键是:

alt + insert

窗口变大,变小:

ctrl + shift + F12

切换java程序:从HelloWorld切换到User

alt + 右箭头 
或者
alt + 左箭头

切换窗口:

alt + 标号
alt + 1(打开,关闭)
alt + 2

提示方法的参数:ctrl + p

注释:

单行注释:ctrl + /
多行注释:ctrl + shift + /

idea中怎么定位方法/属性/变量?

光标停到某个单词的下面,这个单词可能是:
    方法名、变量名
停到单词下面之后,按ctrl键,出现下划线,点击跳转。

idea当中复制一行是

ctrl + d

接口

抽象类和接口以及抽象类和接口的区别

抽象类

第一:抽象类怎么定义?

在class前添加abstract关键字就行了。

第二:抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。

第三:final和abstract不能联合使用,这两个关键字是对立的。

第四:抽象类的子类可以是抽象类。也可以是非抽象类。

第五:抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的。

第六:抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中。

第七:抽象方法怎么定义?

public abstract void doSome();

第八:一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现。

面试题(判断题):

java语言中凡是没有方法体的方法都是抽象方法。

不对,错误的。
Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们
都不是抽象方法,例如:
    public native int hashCode();
    这个方法底层调用了C++写的动态链接库程序。
    前面修饰符列表中没有:abstract。有一个native。表示调用JVM本地程序。

接口的基础语法

1、接口是一种“引用数据类型”。

2、接口是完全抽象的。

3、接口怎么定义:

[修饰符列表] interface 接口名{}

4、接口支持多继承。

5、接口中只有常量+抽象方法。

6、接口中所有的元素都是public修饰的

7、接口中抽象方法的public abstract可以省略。

8、接口中常量的public static final可以省略。

9、接口中方法不能有方法体。

10、一个非抽象的类,实现接口的时候,必须将接口中所有方法加以实现。

11、一个类可以实现多个接口。

12、extends和implements可以共存,extends在前,implements在后。

13、使用接口,写代码的时候,可以使用多态(父类型引用指向子类型对象)。

接口在开发中的作用

注意:接口在开发中的作用,类似于多态在开发中的作用。

多态:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力。

/*
public class Master{
    public void feed(Dog d){}
    public void feed(Cat c){}
    //假设又要养其它的宠物,那么这个时候需要再加1个方法。(需要修改代码了)
    //这样扩展力太差了,违背了OCP原则(对扩展开放,对修改关闭。)
}
*/

public class Master{
    public void feed(Animal a){
        // 面向Animal父类编程,父类是比子类更抽象的。
        //所以我们叫做面向抽象编程,不要面向具体编程。
        //这样程序的扩展力就强。
    }
}

我们要求:面向抽象编程

面向抽象编程这句话以后可以修改为:面向接口编程。

总结一句话:三个字“解耦合”

面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。
接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)

以后进行大项目的开发,一般都是将项目分离成一个模块一个模块的,
模块和模块之间采用接口衔接。降低耦合度。

类型和类型之间的关系:

is a(继承)、has a(关联)、like a(实现)

is a:
    Cat is a Animal(猫是一个动物)
    凡是能够满足is a的表示“继承关系”
    A extends B

has a:
    I has a Pen(我有一支笔)
    凡是能够满足has a关系的表示“关联关系”
    关联关系通常以“属性”的形式存在。
    A{
        B b;
    }

like a:
    Cooker like a FoodMenu(厨师像一个菜单一样)
    凡是能够满足like a关系的表示“实现关系”
    实现关系通常是:类实现接口。
    A implements B

抽象类和接口有什么区别?

在这里我们只说一下抽象类和接口在语法上的区别。
至于以后抽象类和接口应该怎么进行选择,通过后面的项目去体会/学习。

抽象类是半抽象的。
接口是完全抽象的。

抽象类中有构造方法。
接口中没有构造方法。

接口和接口之间支持多继承。
类和类之间只能单继承。

一个类可以同时实现多个接口。
一个抽象类只能继承一个类(单继承)。

接口中只允许出现常量和抽象方法。

这里先透露一个信息:

以后接口使用的比抽象类多。一般抽象类使用的还是少。
接口一般都是对“行为”的抽象。

package和import

package

第一:package出现在java源文件第一行。

第二:带有包名怎么编译?javac -d . xxx.java

第三:怎么运行?java 完整类名

补充:以后说类名的时候,如果带着包名描述,表示完整类名。
如果没有带包,描述的话,表示简类名。

java.util.Scanner 完整类名。
Scanner 简类名

import

import什么时候不需要?

java.lang不需要。
同包下不需要。 
其它一律都需要。

怎么用?

import 完整类名;
import 包名.*;

import java.util.Scanner; // 完整类名。

// 同学的疑问:这样是不是效率比较低。
// 这个效率不低,因为编译器在编译的时候,会自动把*变成具体的类名。
import java.util.*;

import java.*; 这是不允许的,因为在java语言中规定,这里的*只代表某些类的名字。

访问控制权限

访问控制权限都有哪些?

4个。

private    私有
public 公开

protected    受保护
默认

以上的4个访问控制权限:控制的范围是什么?

private 表示私有的,只能在本类中访问

public 表示公开的,在任何位置都可以访问

“默认”表示只能在本类,以及同包下访问。

protected表示只能在本类、同包、子类中访问。

访问控制修饰符            本类            同包            子类            任意位置
---------------------------------------------------------------------------
public                    可以            可以            可以            可以
protected                可以            可以            可以            不行
默认                        可以            可以            不行            不行
private                    可以            不行            不行            不行


范围从大到小排序:public > protected > 默认 > private

访问控制权限修饰符可以修饰什么?

属性(4个都能用)

方法(4个都能用)

类(public和默认能用,其它不行。)

接口(public和默认能用,其它不行。)

…..

关于Object类

这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。

任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。

Object类当中有哪些常用的方法?

我们去哪里找这些方法呢?

第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
第二种方法:去查阅java的类库的帮助文档。

什么是API?

应用程序编程接口。(Application Program Interface)
整个JDK的类库就是一个javase的API。
每一个API都会配置一套API帮助文档。
SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)

目前为止我们只需要知道这几个方法即可:

protected Object clone()   // 负责对象克隆的。
int hashCode()    // 获取对象哈希值的一个方法。
boolean equals(Object obj)  // 判断两个对象是否相等
String toString()  // 将对象转换成字符串形式
protected void finalize()  // 垃圾回收器负责调用的方法

toString()方法

以后所有类的toString()方法是需要重写的。

重写规则:越简单越明了就好。

System.out.println(引用); 这里会自动调用“引用”的toString()方法。

String类是SUN写的,toString方法已经重写了。

equals()方法

以后所有类的equals方法也需要重写,因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。

重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。

基本数据类型比较实用:==

对象和对象比较:调用equals方法

String类是SUN编写的,所以String类的equals方法重写了。
以后判断两个字符串是否相等,最好不要使用==,要调用字符串对象的equals方法。

注意:重写equals方法的时候要彻底。

finalize()方法

这个方法是protected修饰的,在Object类中这个方法的源代码是?

protected void finalize() throws Throwable { }

数组

数组的优点和缺点,并且要理解为什么。

第一:空间存储上,内存地址是连续的。

第二:每个元素占用的空间大小相同。

第三:知道首元素的内存地址。

第四:通过下标可以计算出偏移量。

通过一个数学表达式,就可以快速计算出某个下标位置上元素的内存地址,
直接通过内存地址定位,效率非常高。

优点:检索效率高。
缺点:随机增删效率较低,数组无法存储大数据量。
注意:数组最后一个元素的增删效率不受影响。

一维数组的静态初始化和动态初始化

静态初始化:
    int[] arr = {1,2,3,4};
    Object[] objs = {new Object(), new Object(), new Object()};
动态初始化:
    int[] arr = new int[4]; // 4个长度,每个元素默认值0
    Object[] objs = new Object[4]; // 4个长度,每个元素默认值null

一维数组的遍历

for(int i = 0; i < arr.length; i++){
    System.out.println(arr[i]);
}

二维数组的静态初始化和动态初始化

静态初始化:

    int[][] arr = {
                            {1,2,34},
                            {54,4,34,3},
                            {2,34,4,5}
                        };

    Object[][] arr = {
                            {new Object(),new Object()},
                            {new Object(),new Object()},
                            {new Object(),new Object(),new Object()}
                        };

动态初始化:

    int[][] arr = new int[3][4];
    Object[][] arr = new Object[4][4];
    Animal[][] arr = new Animal[3][4];
    // Person类型数组,里面可以存储Person类型对象,以及Person类型的子类型都可以。
    Person[][] arr = new Person[2][2];
    ....

二维数组的遍历

for(int i = 0; i < arr.length; i++){ // 外层for循环负责遍历外面的一维数组。
    // 里面这个for循环负责遍历二维数组里面的一维数组。
    for(int j = 0; j < arr[i].length; j++){
        System.out.print(arr[i][j]);
    }
    // 换行。
    System.out.println();
}

main方法上“String[] args”参数的使用

(非重点,了解一下,以后一般都是有界面的,用户可以在界面上输入用户名和密码等参数信息。)

数组的拷贝:

System.arraycopy()方法的使用

数组有一个特点:长度一旦确定,不可变。
所以数组长度不够的时候,需要扩容,扩容的机制是:新建一个大数组,
将小数组中的数据拷贝到大数组,然后小数组对象被垃圾回收。

对数组中存储引用数据类型的情况,要会画它的内存结构图

常见的算法

排序算法:
    冒泡排序算法
    选择排序算法

查找算法:
    二分法查找

以上算法在以后的java实际开发中我们不需要使用的。
因为java已经封装好了,直接调用就行。

只不过以后面试的时候,可能会有机会碰上。

例如:java中提供了一个数组工具类:

java.util.Arrays

Arrays是一个工具类。

其中有一个sort()方法,可以排序。静态方法,直接使用类名调用就行。

常用类

String类

对String在内存存储方面的理解

第一:字符串一旦创建不可变。

第二:双引号括起来的字符串存储在字符串常量池中。

第三:字符串的比较必须使用equals方法。

第四:String已经重写了toString()和equals()方法。

String的构造方法

String s = “abc”;

String s = new String(“abc”);

String s = new String(byte数组);

String s = new String(byte数组, 起始下标, 长度);

String s = new String(char数组);

String s = new String(char数组, 起始下标, 长度);

StringBuffer/StringBuilder

2.1、StringBuffer/StringBuilder可以看做可变长度字符串。
2.2、StringBuffer/StringBuilder初始化容量16.
2.3、StringBuffer/StringBuilder是完成字符串拼接操作的,方法名:append
2.4、StringBuffer是线程安全的。StringBuilder是非线程安全的。
2.5、频繁进行字符串拼接不建议使用“+”

包装类(对应八种基本数据类型)

包装类存在有什么用?

方便编程。

八种包装类的类名是什么?

Byte
Short
Integer
Long
Float
Double
Boolean
Character

什么是自动装箱和自动拆箱,代码怎么写?

Integer x = 100; // x里面并不是保存100,保存的是100这个对象的内存地址。
Integer y = 100;
System.out.println(x == y); // true

Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false

日期类

获取系统当前时间

Date d = new Date();

日期格式化:Date –> String

yyyy-MM-dd HH:mm:ss SSS
SimpleDateFormat sdf = new SimpleDate("yyyy-MM-dd HH:mm:ss SSS");
String s = sdf.format(new Date());

String –> Date

SimpleDateFormat sdf = new SimpleDate("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("2008-08-08 08:08:08");

获取毫秒数

long begin = System.currentTimeMillis();
Date d = new Date(begin - 1000 * 60 * 60 * 24);

数字类

DecimalFormat数字格式化

###,###.## 表示加入千分位,保留两个小数。
###,###.0000 表示加入千分位,保留4个小数,不够补0

BigDecimal

财务软件中通常使用BigDecimal

随机数 枚举

随机数

1、怎么产生int类型随机数。

    Random r = new Random();
    int i = r.nextInt();

2、怎么产生某个范围之内的int类型随机数。

    Random r = new Random();
    int i = r.nextInt(101); // 产生[0-100]的随机数。

枚举

1、枚举是一种引用数据类型。

2、枚举编译之后也是class文件。

3、枚举类型怎么定义?

enum 枚举类型名{
    枚举值,枚举值2,枚举值3
}

4、当一个方法执行结果超过两种情况,并且是一枚一枚可以列举出来
的时候,建议返回值类型设计为枚举类型。

异常处理机制

apkSHK.jpg

关于异常:

java中异常的作用是:增强程序健壮性。

java中异常以类和对象的形式存在。

异常在java中以类和对象的形式存在。那么异常的继承结构是怎样的?

我们可以使用UML图来描述一下继承结构。
画UML图有很多工具,例如:Rational Rose(收费的)、starUML等….

Object

Object下有Throwable(可抛出的)

Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)

Exception下有两个分支:
    Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错)
    RuntimeException:运行时异常(在编写程序阶段程序员可以预先处理,也可以不管)

编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。

编译时异常因为什么而得名?

因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。

所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。

因为异常的发生就是new异常对象。

编译时异常和运行时异常的区别?

编译时异常一般发生的概率比较高。

运行时异常一般发生的概率比较低。

假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常,
所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果呢?

首先,如果这样的话,程序肯定是绝对的安全的。但是程序员编写程序太累,代码到处都是处理异常的代码。

编译时异常还有其他名字:

受检异常:CheckedException
受控异常

运行时异常还有其它名字:

未受检异常:UnCheckedException
非受控异常

再次强调:所有异常都是发生在运行阶段的。

Java语言中对异常的处理包括两种方式:

第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
谁调用我,我就抛给谁。抛给上一级。

第二种方式:使用try..catch语句进行异常的捕捉。
这件事发生了,谁也不知道,因为我给抓住了。

思考:
异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要
对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。

注意

Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序的执行

集合

前置知识

迭代器:

迭代器迭代元素的过程中不能使用集合对象的remove方法删除元素,要使用迭代器Iterator的remove方法来删除元素,防止出现异常:

ConcurrentModificationException

泛型:

第一:集合使用泛型来减少向下转型的操作。
第二:怎么使用泛型?
第三:怎么自定义泛型?

foreach:

对数组怎么遍历?
    for(int i : arr){
        System.out.println(i);
    }

对集合怎么遍历?
    for(String s : list){
        System.out.println(s);
    }

钻石表达式:

List<String> list = new ArrayList<>();
类型自动推断

集合概述

什么是集合?有什么用?

数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。

集合为什么说在开发中使用较多?

集合是一个容器,是一个载体,可以一次容纳多个对象。

在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。

集合中存储的是引用

集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。

list.add(100); //自动装箱Integer

注意:

集合在java中本身是一个容器,是一个对象。

集合中任何时候存储的都是“引用”。

在java中每一个不同的集合,底层会对应不同的数据结构

往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:数组、二叉树、链表、哈希表…以上这些都是常见的数据结构。

你往集合c1中放数据,可能是放到数组上了。
你往集合c2中放数据,可能是放到二叉树上了。
…..
你使用不同的集合等同于使用了不同的数据结构。

你在java集合这一章节,你需要掌握的不是精通数据结构。java中已经将数据结构
实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择
哪一种合适的集合去使用即可。

new ArrayList(); 创建一个集合,底层是数组。

new LinkedList(); 创建一个集合对象,底层是链表。

new TreeSet(); 创建一个集合对象,底层是二叉树。
…..

集合在java JDK中哪个包下?

java.util.*;
所有的集合类和集合接口都在java.util包下。

在java中集合分为两大类:

一类是单个方式存储元素

单个方式存储元素,这一类集合中超级父接口:

java.util.Collection;

一类是以键值对的方式存储元素

以键值对的方式存储元素,这一类集合中超级父接口:

java.util.Map;

集合体系

apkk3d.jpg

apkF9H.jpg

List集合

List是Collection接口的子接口,所以List接口中有一些特有的方法

void add(int index, Object element)
Object set(int index, Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)

ArrayList

ArrayList集合初始化容量10

扩容为原容量1.5倍

底层是数组

Vector

Vector初始化容量是10

扩容为原容量的2倍

底层是数组

Vector底层是线程安全的

怎么得到一个线程安全的List:

Collections.synchronizedList(list);

Map集合

遍历Map集合的两种方式

第一种:获取所有key,遍历每个key,通过key获取value.
第二种:获取Set<Map.Entry>即可,遍历Set集合中的Entry
    调用entry.getKey() entry.getValue()

且存放在HashMap集合key部分和HashSet集合中的元素需要同时重写hashCode和equals

HashMap和Hashtable的区别

HashMap:
    初始化容量16,扩容2倍。
    非线程安全
    key和value可以为null。

Hashtable:
    初始化容量11,扩容2倍+1
    线程安全
    key和value都不能是null。

Properties类的常用两个方法

setProperty
getProperty

自平衡二叉树数据结构(了解)

左小右大原则存储
中序遍历方式

TreeMap的key或者TreeSet集合中的元素要想排序,有两种实现方式:

第一种:实现java.lang.Comparable接口。
第二种:单独编写一个比较器Comparator接口。

IO流

IO流,什么是IO?

I : Input
O : Output
通过IO可以完成硬盘文件的读和写。

IO流的分类

有多种分类方式:

一种方式是按照流的方向进行分类:

以内存作为参照物,
往内存中去,叫做输入(Input)。或者叫做读(Read)。
从内存中出来,叫做输出(Output)。或者叫做写(Write)。

另一种方式是按照读取数据方式不同进行分类:

有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等

有的流是按照字符的方式读取数据的,一次读取一个字符
这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。
只能读取纯文本文件,连word文件都无法读取。


综上所述:

流的分类

输入流、输出流
字节流、字符流

java IO流四大家族:

java.io.InputStream  字节输入流
java.io.OutputStream 字节输出流

java.io.Reader        字符输入流
java.io.Writer        字符输出流

四大家族的首领都是抽象类。(abstract class)

所有的流都实现了:

java.io.Closeable接口

都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,
不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。

所有的输出流都实现了:

java.io.Flushable接口

都是可刷新的,都有flush()方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()
刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据
强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()可能会导致丢失数据。


注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。

java.io包下需要掌握的流有16个

文件专属:

java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter

转换流:(将字节流转换成字符流)

java.io.InputStreamReader
java.io.OutputStreamWriter

缓冲流专属:

java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream

数据流专属:

java.io.DataInputStream
java.io.DataOutputStream

标准输出流:

java.io.PrintWriter
java.io.PrintStream(掌握)

对象专属流:

java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)

java.io.File类

关于对象流

ObjectInputStream
ObjectOutputStream

重点:

参与序列化的类型必须实现java.io.Serializable接口

并且建议将序列化版本号手动的写出来

private static final long serialVersionUID = 1L;

IO + Properties联合使用

IO流:文件的读和写。

Properties:是一个Map集合,key和value都是String类型。

多线程

apk9AO.jpg

什么是进程?什么是线程?

进程是一个应用程序(1个进程是一个软件)。
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程。

对于java程序来说,当在DOS命令窗口中输入:

java HelloWorld 

回车之后会先启动JVM,而JVM就是一个进程。

JVM再启动一个主线程调用main方法。

同时再启动一个垃圾回收线程负责看护,回收垃圾。

现在的java程序中至少有两个线程并发:
一个是垃圾回收线程,一个是执行main方法的主线程。

进程和线程是什么关系?

阿里巴巴:进程
    马云:阿里巴巴的一个线程
    童文红:阿里巴巴的一个线程

京东:进程
    强东:京东的一个线程
    妹妹:京东的一个线程

进程可以看做是现实生活当中的公司。
线程可以看做是公司当中的某个员工。

注意:
    进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
        魔兽游戏是一个进程
        酷狗音乐是一个进程
        这两个进程是独立的,不共享资源。

在java语言中:
    线程A和线程B,堆内存和方法区内存共享。
    但是栈内存独立,一个线程一个栈。

假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
互不干扰,各自执行各自的,这就是多线程并发。

火车站,可以看做是一个进程。
火车站中的每一个售票窗口可以看做是一个线程。
我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
所以多线程并发可以提高效率。

java中之所以有多线程机制,目的就是为了提高程序的处理效率。

思考一个问题:使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束?

main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弹栈。

分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

多核的CPU:
    真正的多线程并发。
    4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。

    什么是真正的多线程并发?
        t1线程执行t1的。
        t2线程执行t2的。
        t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。

单核的CPU:
    不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
    对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
    CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
    同时在做

实现线程的两种方式

java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。

// 定义线程类
public class MyThread extends Thread{
    public void run(){
    
    }
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();

第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。

// 定义一个可运行的类
public class MyRunnable implements Runnable {
    public void run(){
    
    }
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

关于线程对象的生命周期

新建状态
就绪状态
运行状态
阻塞状态
死亡状态

关于线程的调度(了解)

常见的线程调度模型有哪些?

    抢占式调度模型:
        那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
        java采用的就是抢占式调度模型。

    均分式调度模型:
        平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
        平均分配,一切平等。
        有一些编程语言,线程调度模型采用的是这种方式。

java中提供了哪些方法是和线程调度有关系的呢?

    实例方法:
        void setPriority(int newPriority) 设置线程的优先级
        int getPriority() 获取线程优先级
        最低优先级1
        默认优先级是5
        最高优先级10
        优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)
    
    静态方法:
        static void yield()  让位方法
        暂停当前正在执行的线程对象,并执行其他线程
        yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
        yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
        注意:在回到就绪之后,有可能还会再次抢到。
    
    实例方法:
        void join()  
        合并线程
        class MyThread1 extends Thread {
            public void doSome(){
                MyThread2 t = new MyThread2();
                t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
            }
        }

        class MyThread2 extends Thread{
            
        }

关于多线程并发环境下数据的安全问题

为什么这个是重点?

以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。

最重要的是:你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。

什么时候数据在多线程并发的环境下会存在安全问题呢?

三个条件:

条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。

满足以上3个条件之后,就会存在线程安全问题。

怎么解决线程安全问题呢?

使用“线程同步机制”。

线程同步就是线程排队,线程排队了就会牺牲一部分效率,但是,数据安全第一位,只有数据安全了,我们才可以谈效率。

线程同步涉及到这两个专业术语:

异步编程模型:

    线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
    谁也不需要等谁,这种编程模型叫做:异步编程模型。
    其实就是:多线程并发(效率较高。)

    异步就是并发。


​ 同步编程模型:

​ 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
​ 结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
​ 两个线程之间发生了等待关系,这就是同步编程模型。
​ 效率较低。线程排队执行。

​ 同步就是排队。

当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,怎么解决这个问题?

线程排队执行。(不能并发)

用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。

专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。

Java三大变量【重要】

实例变量:在堆中。

静态变量:在方法区。

局部变量:在栈中。

以上三大变量中:

局部变量永远都不会存在线程安全问题。
因为局部变量不共享。(一个线程一个栈。)
局部变量在栈中。所以局部变量永远都不会共享。

实例变量在堆中,堆只有1个。
静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。

局部变量+常量:不会有线程安全问题。
成员变量:可能会有线程安全问题。

使用局部变量建议使用StringBuilder,因为局部变量不存在线程安全问题。选择StringBuilder,StringBuffer效率比较低。

ArrayList是非线程安全的。
Vector是线程安全的。
HashMap HashSet是非线程安全的。
Hashtable是线程安全的。

synchronized三种写法

第一种:同步代码块

灵活。

synchronized(线程共享对象){
    同步代码块;
}

第二种:在实例方法上使用synchronized

表示共享对象一定是this
并且同步代码块是整个方法体。

第三种:在静态方法上使用synchronized

表示找类锁。
类锁永远只有1把。
就算创建了100个对象,类锁也只有一把。

对象锁:1个对象1把锁,100个对象100把锁。

类锁:100个对象,也可能只是1把类锁。

开发中解决线程安全问题

是一上来就选择线程同步(synchronized)吗?

不是,synchronized会让程序的执行效率降低,用户体验不好。
系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择
线程同步机制。

第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
对象不共享,就没有数据安全问题了。)

第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
就只能选择synchronized了。线程同步机制。

线程的剩余内容

守护线程

java语言中线程分为两大类:

一类是:用户线程
一类是:守护线程(后台线程)
其中具有代表性的就是:垃圾回收线程(守护线程)。

守护线程的特点:

一般守护线程是一个死循环,所有的用户线程只要结束,
守护线程自动结束。

注意:主线程main方法是一个用户线程。

守护线程用在什么地方呢?

例如:每天00:00的时候系统数据自动备份。
这个需要使用到定时器,并且我们可以将定时器设置为守护线程。
一直在那里看着,没到00:00的时候就备份一次。所有的用户线程
如果结束了,守护线程自动退出,没有必要进行数据备份了。

定时器

定时器的作用:间隔特定的时间,执行特定的程序。

在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的,
那么在java中其实可以采用多种方式实现:

可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行
任务。这种方式是最原始的定时器。(比较low)

在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。
不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持
定时任务的。

在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架,
这个框架只要进行简单的配置,就可以完成定时器的任务。

实现线程的第三种方式:实现Callable接口(JDK8新特性)

这种方式实现的线程可以获取线程的返回值。
之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。

思考:
系统委派一个线程去执行一个任务,该线程执行完任务之后,可能
会有一个执行结果,我们怎么能拿到这个执行结果呢?

使用第三种方式:实现Callable接口方式。

关于Object类中的wait和notify方法(生产者和消费者模式)

第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象
都有的方法,因为这两个方式是Object类中自带的。

即:wait方法和notify方法不是通过线程对象调用

第二:wait()方法作用

Object o = new Object();
o.wait();

表示:
让正在o对象上活动的线程进入等待状态,无期限等待,
直到被唤醒为止。
o.wait();方法的调用,会让“当前线程(正在o对象上活动的线程)”进入等待状态。

第三:notify()方法作用

Object o = new Object();
o.notify();

表示唤醒正在o对象上等待的线程。

还有一个notifyAll()方法:    
这个方法是唤醒o对象上处于等待的所有线程

反射机制

反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件

优点类似于黑客(可以读和修改字节码文件)

通过反射机制可以操作代码片段(class文件)

反射机制的相关类在哪个包下?

java.lang.reflect.*;

反射机制相关的重要的类有哪些?

java.lang.Class:代表整个字节码,代表一个类型,代表整个类。

java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。

java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法

java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

java.lang.Class:

    public class User{
        // Field
        int no;

        // Constructor
        public User(){
        
        }
        public User(int no){
            this.no = no;
        }

        // Method
        public void setNo(int no){
            this.no = no;
        }
        public int getNo(){
            return no;
        }
    }

类加载器

什么是类加载器?

专门负责加载类的命令/工具:
ClassLoader

JDK中自带的3个类加载器

启动类加载器:
rt.jar

扩展类加载器:
ext/*.jar

应用类加载器:
classpath

加载过程

假设有这样一段代码:

String s = "abc";

代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找String.class
文件,找到就加载,那么是怎么进行加载的呢?

首先通过“启动类加载器”加载。
    注意:启动类加载器专门加载C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
    rt.jar中都是JDK最核心的类库。

如果通过“启动类加载器”加载不到的时候,
会通过"扩展类加载器"加载。
    注意:扩展类加载器专门加载C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar


如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。
    注意:应用类加载器专门加载classpath中的类。

双亲委派机制

java中为了保证类加载的安全,使用了双亲委派机制。

优先从启动类加载器中加载,这个称为“父”。

“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。

双亲委派。

如果都加载不到,才会考虑从应用类加载器中加载。

直到加载到为止。

注解

注解,或者叫做注释类型,英文单词是:Annotation

注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。

怎么自定义注解呢?语法格式?

 [修饰符列表] @interface 注解类型名{

 }

注解怎么使用,用在什么地方?

第一:注解使用时的语法格式是:

@注解类型名

第二:注解可以出现在类上、属性上、方法上、变量上等….
注解还可以出现在注解类型上。

JDK内置了哪些注解呢?

java.lang包下的注释类型:

掌握:

Deprecated 用 @Deprecated 注释的程序元素,
不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。

掌握:

Override 表示一个方法声明打算重写超类中的另一个方法声明。

了解:

SuppressWarnings 指示应该在注释元素(以及包含在该注释元素中的
所有程序元素)中取消显示指定的编译器警告。

元注解

什么是元注解?

用来标注“注解类型”的“注解”,称为元注解。

常见的元注解有哪些?

Target
Retention

关于Target注解

这是一个元注解,用来标注“注解类型”的“注解”
这个Target注解用来标注“被标注的注解”可以出现在哪些位置上。

@Target(ElementType.METHOD):表示“被标注的注解”只能出现在方法上。
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
    表示该注解可以出现在:
        构造方法上
        字段上
        局部变量上
        方法上
        ....
        类上...

关于Retention注解

这是一个元注解,用来标注“注解类型”的“注解”
这个Retention注解用来标注“被标注的注解”最终保存在哪里。

@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中。
@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中。
@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。

Retention的源代码

        //元注解    
        public @interface Retention {
            //属性
            RetentionPolicy value();
        }
        
    RetentionPolicy的源代码:
        public enum RetentionPolicy {
             SOURCE,
             CLASS,
             RUNTIME
        }

        //@Retention(value=RetentionPolicy.RUNTIME)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface MyAnnotation{}