˗ˋˏ♡ˎˊ˗

爱你所爱,行你所行,听从你心,无问西东。

0%

 
 

异常处理

程序错误.png
程序错误1.png

 

一、 Java异常基础

1.1 为什么要引入异常处理机制?

程序的错误分为:
编译错误:程序员编写程序时语法上出现的错误;
运行错误:程序员编写的程序在语法上没有错误,但是程序在运行时出现错误,本章就主要针对该类错误讲解 - - 即异常、异常类和异常处理机制。

 

1.2 以往的错误处理方法

**主要缺陷:**  程序复杂  可靠性差  返回信息有限  返回代码标准化困难  以往的程序开发过程中,常常采用**返回值**进行处理。例如,在编写一个方法,可以返回一个状态代码,调用者 根据状态代码判断出错与否。若状态代码表示一个错误,则调用该错误的处理程序进行相应的处理,或显示一 个错误页面或错误信息。

举例 以往的错误处理方法:采用返回值进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
举例:实现将一个文件从硬盘加载近来,导致加载可能失败的运行错误有硬盘错误、文件无法找到等
int status=loadTextfile();
If (status!=1){
//something unusual happened, describe it
switch(status) {
case 2:
//file not found
break;
case 3:
//disk error
default:
//other error}
}else {
//file loaded OK, continue with program}
```

 
 
#### <font color="#20B2AA" face="Comic sans MS">1.3 Java异常处理方法</font>
<table><tr><td bgcolor=#ADD8E6><font face="Comic sans MS">Java异常处理方法:Java为运行错误引入了异常、异常类和异常处理机制。
**异常:**特殊的运行错误,是在程序运行过程中发生的、会打断程序正常执行的错误
 例如:
 除0溢出
 文件找不到
数组元素下标越界 

**异常类:**Java用面向对象的方法处理异常,Java的异常类是处理运行时错误的特殊类,每一种异常类对应一种
特定的运行错误,每一个异常事件由一个异常类的对象来代表。
*例如:除0溢出(ArithmeticException)
   文件找不到(FileNotFoundException)
   数组元素下标越界(ArrayIndexOutofBoundsException)*

**异常处理机制:抛出异常——捕捉异常:**
1. 当出现了一些错误,方法都会产生一个<font color="red">异常对象</font>,这个异常对象将交由运行系统来处理。此过程就称为抛出
<font color="red">(throwing)异常</font>。**包括:系统抛出、用户自定义抛出。**
2. 接着,运行系统开始寻找合适的处理方法,来处理这个异常。如果系统找到了一个适合的处理该异常方法,
这一过程就叫<font color="red">捕获异常</font>。
</font></td></tr></table>

&emsp;
&emsp;
#### <font color="#20B2AA" face="Comic sans MS">1.4 异常处理的语法支持</font>
<table><tr><td bgcolor=#ADD8E6><font face="Comic sans MS"> **-try,catch,throws,throw,finally**
(1) try包含可能出现异常的语句块;
(2) 一个或多个catch块紧随try{}块,每个catch块通常处理指定类型的异常;
(3) finally引导块紧随catch块后,主要用于清理现场(可有可无)。
格式:
try
{ ......
}catch( ExceptionName1 e )
{ ......}
catch( ExceptionName2 e )
{ ......}
finally
{ ......}
注意:finally总是执行,catch块不一定执行
</font></td></tr></table>

&emsp;
&emsp;
#### <font color="#20B2AA" face="Comic sans MS">1.5 总结</font>
<table><tr><td bgcolor=#EEE0E5><font face="Comic sans MS"> (1) Java的异常处理把错误集中起来统一处理。程序员只需要说明何处可能出现异常,如何处理即可;
(2) 采用面向对象的思想标准化了各种错误的类型;
(3) Java把程序运行过程中可能遇到的问题分为两类,一类是致命性的,即程序遇到了非常严重的不正常状
态,不能简单地恢复执行,这就是<font color="red">**错误(对应Error类)**</font>,如程序运行过程中内存耗尽。另一类是非致命性的,
通过某种处理后程序还能继续运行,这就是<font color="red">**异常(对应Exception类)**</font>。
</font></td></tr></table>


&emsp;
&emsp;
### <font color="#FF6347" face="Comic sans MS">二、 异常类的层次</font>
#### <font color="#20B2AA" face="Comic sans MS">2.1 异常分类</font>

<font face="Comic sans MS"><font color="red">**☕异常类:**</font>异常在Java中都是作为类的实例(对象)的形式出现的。
&emsp; 如 Throwable类, Exception类, Error类……

<font color="red">**☕Java中异常分类:**</font>
(1) <font color="#00B2EE">**Error类及其子类:**</font>描述Java运行时刻系统内部的错误或资源枯竭导致的错误,无法恢复和抛出,发生几率小;
(2) <font color="#00B2EE">**Exception类及其子类:**</font>普通程序可以从中恢复,分为<font color="red">**运行时异常**</font>和<font color="red">**非运行时异常**</font>。

&emsp;异常在Java中也是作为类的实例的形式出现的。Java中的所有的异常类都是从Throwable类派生出来的。<font color="red">**Throwable类有两个直接子类:**</font><font color="#00B2EE">**java.lang.Error**</font> 和 <font color="#00B2EE">**java.lang.Exception**</font>。


异常类的层次结构如下图所示。
![58.png](https://i.loli.net/2018/12/28/5c2621a200c43.png)
**(1) 🔺Error类及其子类主要用来描述一些Java运行时刻系统内部的错误或资源枯竭导致的错误。**普通的程序不能从这类错误中恢复,也无法抛出这种类型的错误,这类错误出现的几率是很小的。
**(2) 🔺另一个异常类的子类是Exception类和它的子类。**在编程中错误的处理主要是对这类错误的处理,如除数为零、数组下标越界等。类Exception是普通程序可以从中恢复的所有规范了的异常的父类。
</font>

&emsp;
&emsp;
#### <font color="#20B2AA" face="Comic sans MS">2.2 Exception类的子类</font>

<font face="Comic sans MS"><font color="red">**☕Exception类子类有两种:**</font>
**运行时异常**和**非运行时异常(一般异常)** &emsp;&emsp;&emsp; *区别???*
<font color="#00B2EE">**(1)运行时异常:**</font>RuntimeException类及其所有子类。 运行时异常是程序员编写程序不正确所导致的异常,理论上,程序员经过检查和测试可以查出这类错误。如**除数为零等,错误的强制类型转换、数组越界访问、空引用**。
<font color="#00B2EE">**(2)非运行时异常(一般异常):**</font>指可以由编译器在编译时检测到的、可能会发生在方法执行过程中的异常,如找不到指定的文件等,这不是程序本身的错误,如果这些异常情况没有发生,程序本身仍然是完好的。  
<font color="red">**注意:**</font>**编译器强制要求Java程序必须**<font color="#00B2EE">**捕获**</font>**或**<font color="#00B2EE">**声明抛出**</font>**所有非运行时异常,但对运行时异常不作要求。运行时异常编译可以通过但是运行时出现异常;非运行时异常编译的时候就通不过。**

*例如:格式不正确的URL、试图为一个不存在的类找到一个代表它的类的对象。除了runtimeexception及其子类以外,其他exception类的子类都是非运行时异常。*
</font>

```java
//运行时异常和非运行时异常区别举例
[例5-1] RuntimeExceptionDemo1.java
class RuntimeExceptionDemo1{
public static void main(String args[]){
int i=0;
System.out.println(2/0);
}}

该程序能编译通过,而在运行时,出现如下提示:
> javac RuntimeExceptionDemo1.java
> java RuntimeExceptionDemo1
Exception in thread "main" java.lang.ArithmeticException: / by zero at
RuntimeExceptionDemo1.main(RuntimeExceptionDemo1.java:4)
//这里的"ArithmeticException"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[例5-2] NonRuntimeExceptionDemo1.java
import java.io.*;
class NonRuntimeExceptionDemo1
{
public static void main(String args[]){
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}
}
会出现如下的错误提示:
javac NonRuntimeExceptionDemo1.java
NonRuntimeExceptionDemo1.java:5: unreported exception java.io.FileNotFoundException; must
be caught or declared to be thrown
FileInputStream in=new FileInputStream("text.txt");
^
NonRuntimeExceptionDemo1.java:7: unreported exception java.io.IOException; mustbe
caught or declared to be thrown
while((s=in.read())!=-1) System.out.print(s);
^
NonRuntimeExceptionDemo1.java:8: unreported exception java.io.IOException; mustbe caught
or declared to be thrown
in.close();

&emsp;
☕对运行时异常的说明:由于运行时异常可能会出现在程序的任何地方,而且出现的可能性非常大,因而由程序本身去检测运行异常出现与否,将会使程序的开销过大,所以
运行时异常是由Java运行时系统在程序的运行过程中检测到的,它可能在程序中任意部位发生,而且其数目可能很大,因此Java编译器允许程序不对它进行处理。这时,java运行时系统会把生成的运行时异常对象交给默认的异常处理,在标准输出设备上显示异常的内容以及发生异常的位置
建议对于运行时异常用户不要去捕获,捕获就是承认这个错误,但是一般我们编程的希望是发生错误能改正就尽量改正,除非不是由程序员自己意愿决定的错误。
&emsp;

☕对运行时异常和非运行时异常的总结:

(1) 当出现java.lang.ArithmeticException运行时异常时,不需要用户在程序中对其进行处理,而直接由Java运行时系统进行处理;
(2) 对于非运行时异常,Java编译器对程序进行编译的时候,便指出用户需要①捕获该类异常或者②声明抛出。即对于非运行时异常,用户需要在程序中进行处理,否则编译时无法通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
① 捕获该类异常
[例5-2] NonRuntimeExceptionDemo1.java
import java.io.*;
class NonRuntimeExceptionDemo1
{
public static void main(String args[]){
try{
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}catch(Exception e){…}
}
}


② 声明抛出
[例5-2] NonRuntimeExceptionDemo1.java
import java.io.*;
class NonRuntimeExceptionDemo1
{
public static void main(String args[])throws Exception{
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}
}

&emsp;
&emsp;

2.3 对于运行时异常和非运行时异常的一些说明

1. ☕编译器和异常:编译器强制要求程序员捕获或声明抛出非运行时异常
到底为什么要这么做呢???
对于运行时异常,编译器不强制要求,但用户也可以自己去捕获
这个时候会出现什么效果呢???

2. ☕运行时系统和异常:
(1) 异常都有抛出的轨迹;
(2) 对于所有异常,某个方法产生的异常 ,如果没有被捕获,就会自动抛给方法的调用者(但是对非运行时异常一定要声明抛出),如果调用者还没有捕获,再抛给调用者的调用者,以此类推,直到main方法里发现还没有捕获,那么运行时系统就会来处理这个异常,把异常信息和异常的轨迹信息打印给用户。

3. ☕两点建议:
(1) 建议对于运行时异常用户不要去捕获,捕获就是承认这个错误,但是一般我们编程者希望是发生错误能改正就尽量改正,除非不是由程序员自己意愿决定的错误;
(2) 建议对于非运行时异常,用户应该去捕获,以交代程序员的处理该错误的代码,实在不行,就声明抛出给方法的调用者。

3. ☕异常类常用的方法:
public Exception()
public Exception(String s)&emsp; :该参数一般表示该异常对应的错误的描述
public String toString()&emsp;:返回描述当前异常对象信息的字符串
public String getMessage()&emsp;:返回描述当前异常对象信息的详细信息。
public void printStackTrace()&emsp;:打印当前异常对象使用堆栈的轨迹。

5S.png

4DI6.png

&emsp;
&emsp;

三、 try-catch-finally异常处理

3.1 概括try-catch-finally语句

用户处理异常的三种方法:
(1)用户可以用try-catch-finally语句进行抛出捕获处理
(2)如果不想捕获和处理异常,可以通过throws语句声明要抛出的异常
(3) 用户可以定义自己的异常类,并用throw语句来抛出。

运行时异常是由Java运行时系统在程序的运行过程中检测到的,它可能在程序中任意部位发生,而且其数目可能很大,因此Java编译器允许程序不对它进行处理。这时,java运行时系统会把生成的运行时异常对象交给默认的异常处理,在标准输出设备上显示异常的内容以及发生异常的位置。即:运行时异常:会输出到设备显示哪里存在错误,请更正;非运行时异常:编译报错时提示必须添加非运行时异常处理,比如加try{}…catch(类名 对象){}或者使用throws来抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
try-catch-finally语句对程序运行进行监控,捕获和处理异常通常形式: 
try{
调用可能产生异常的方法及其它java语句;
}
catch(异常类名1 异常对象名e){
异常处理语句块;
}
catch(异常类名2 异常对象名e){
异常处理语句块;
}
finally{
最终处理;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[例5-3]访问文本文件text.txt,并将其在屏幕上打印出来。
import java.io.*;
class TryCatchFinally
{
public static void main(String args[]){
try{
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}
catch(FileNotFoundException e){
System.out.println(“捕获异常:”+e); //e会调用toString()方法回显异常信息给程序员
}
catch(IOException e){
System.out.println("捕获异常:"+e);
}
finally{
System.out.println("finally块总是执行!");
} }
}

运行结果:
捕获异常:java.io.FileNotFoundException: text.txt (系统找不到指定的文件。)
finally块总是执行!

&emsp;
&emsp;

3.2 try语句

try{}:将可能抛出一个或者若干个异常的代码放入try语句块中。
注意:应当尽量减小try代码块的大小,不要将整个程序代码全部放入try语句块中,而是应当仔细分析代码,在可能出现异常情况的地方用try进行监控。

因为当发生异常时,程序控制由try块转到catch块,Java将跳过try中后面的语句,且永远不会从catch块返回到try块。因此若将整个程序代码都放在try中,若一开始发生异常,则后面的语句将永远不会被执行,从而影响了程序的实现。

&emsp;

3.3 catch语句

**📒(1)** **try语句后面必须跟有一个或多个catch语句来处理try中产生的异常事件**。如果try语句中未产生异常, 那么catch语句将不执行。 **📒(2)catch语句需要一个参数:****一个异常类名和该异常类的对象。注意该异常类必须是Throwable类的子类**. **📒(3)** try块中发生了一个异常,try-catch语句就会自动在try块后面的各个catch块中,找出与该异常类相 匹配的参数。当参数符合以下3个条件之一时,就认为这个参数与产生的异常相匹配: (1)参数与产生的异常属于一个类; (2)参数是产生的异常的父类; (3)参数是一个接口时,产生的异常实现了这一接口。 **📒(4) 注意:** (1) 当产生的异常找到了第一个与之相匹配的参数时,就执行包含这一参数的catch语句中的Java代码,执 行完catch语句后,程序恢复执行,但不会回到异常发生处继续执行,而是执行try-catch结构后面的代码。 (2) 可以用一个catch块来处理多个异常类型,此时catch的参数应该是这多个异常的父类。 (3) 有多个catch块时,要细心安排catch块的顺序。 **将子类的catch块放在前面,父类的catch块放在后面。**

&emsp;

3.4 finally子句

**1.finally语句:** 无论在try块中是否产生异常,也不管产生的异常是否会被捕获,finally中的语句最终都会 被执行。 **2.作用:** 为异常处理事件提供一个清理机制,例如清理打开文件、Socket、JDBC连接之类的资源。

如果程序用到了文件、Socket、JDBC连接之类的资源,即使遇到了异常,也要正确释放占用的资源。
&ensp;finally语句可以说是为异常处理事件提供的一个清理机制. 一般是用来关闭文件或释放其他的系统资源,作
为try-catch-finally结构的一部分,可以没有finally语句,如果存在finally语句,不论try块中是否发生
了异常,是否执行过catch语句,都要执行finally语句。

3. 带有finally子句的try-catch-finally语句的形式如下:

1
2
3
4
5
6
try { … }           //…是正常执行的代码, 可能产生异常
catch (异常类1 e) { … } //…是异常类1的处理代码
catch (异常类2 e) { … } //…是异常类1的处理代码
……
catch (异常类n e) { … } //…是异常类2的处理代码
finally { … } //…是执行清除工作的语句
**4.执行过程:** **(1) try块中的语句没有产生异常。**在这种情况下,Java首先执行try块中的所有的语句,然后执行finally子句 中的代码,最后执行try…catch..finally块后面的语句; **(2) try块中的语句产生了异常,而且此异常在方法内被捕获(有catch匹配)。** 在这种情况下,Java首先执行try 块中的语句,直到产生异常处,然后跳过此try块中剩下的语句,执行捕获此异常的catch子句的处理代码; 然后执行finally子句中的代码; **(3) 如果在catch子句又重新抛出了异常。**也会执行finally,然后将这个异常抛出给方法的调用者; **(4) try块中产生了异常,而此异常在方法内没有被捕获(没有catch匹配) 。**在这种情况下,Java将执行try块 中的代码直到产生异常,然后跳过try块中的代码而转去执行finally子句中的代码,最后将异常抛出给方法 的调用者。

&emsp;
&emsp;

四、 throws-throw抛出异常

4.1 throws抛出异常

📒throws说明:
在设计可能会抛出异常的方法时,可以有两个选择:
(1) 使用try-catch-finally处理方法中的异常
(2) 声明抛出异常:不捕获异常(没有throws方法的创建异常类的实例和抛出异常。是个空语句。),而是将异常交由上一层处理,在其他地方捕获异常。如果使用后者,那么应该(在某些情况下)向编译器表明:此方法可能会抛出异常,但方法本身不会捕获它。可以在方法头中用throws子句来实现此功能。

(2.1)带throws异常说明的方法说明形式如下:
… 方法名(…) [throws 异常类列表]
{ 方法体 }

&emsp;
📒注意:
(1)方法抛出的异常类是throws子句中指定的异常类或其子类。
(2)并不是所有可能发生的异常都要在方法的说明中指定,从Error类中派生出的异常和从RuntimeException类中派生的异常就不用在方法声明中指定。

📒在下列情况下Java方法可以声明抛出异常:
(1)调用的方法抛出了异常;
(2)检测到了错误并使用throw语句抛出异常;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1:调用的方法抛出了异常 
class Test {……
public String getInput() throws IOException
{……
System.in.read();
}
}



2:检测到了错误并使用throw语句抛出异常
import java.io.*;
class Test {……
public String getInput() throws IOException
{……
IOException ae =new IOException("buffer is full");
throw ae;
}



3throws多个异常
class Animation
{
public Image loadImage(String s) throws
EOFException, MalformURLException
{
……
}
}

&emsp;
&emsp;

4.2 throw抛出异常

📒throw说明:
要使用throw,则必须在方法名后面指出throws;但是使用throws,抛出可以是空语句不用写,交给上一层处理!!
在捕获一个异常前,必须有一段Java代码来生成和抛出一个异常对象。Java用throw语句抛出异常。throw语句的格式如下:
throw ThrowableObject;

&emsp;
异常对象的生成和抛出可以有以下三种情况:
   (1)Java运行时系统
   (2)JDK中某个类
   (3)在程序中创建异常对象抛出

使用throw语句应注意:
(1)一般这种抛出异常的语句应该在满足一定条件执行,例如把throw语句if分支中
(2)含有throw语句的方法,应该在方法头定义中用throws语句声明所有可能抛出的异常

抛出异常有这样三步:
(1)确定异常类;
(2)创建异常类的实例;
(3)抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
举例
static String getInput() throws IOException{
char[] buffer =new char[20];
int counter = 0;
boolean flag =true;
while(flag) {
buffer[counter] =(char)System.in.read();

if(buffer[counter]=='\n') flag = false;
counter++;
if(counter>=20){
} IOException ae =new IOException("buffer is full");
throw ae; }
return new String(buffer);
}

"//IOException ae =new IOException(""buffer is full""); throw ae;"使用throw

&emsp;
&emsp;

五、 正确地使用异常

由于异常使用起来非常方便,以至于在很多情况下可能会滥用异常。但是,使用异常处理会降低程序运行的 速度,几点建议:

(1) 在可以使用简单的测试就能完成的检查中,不要使用异常来代替它。例如:
if (ins!=null) //使用ins引用对象
{ … }

(2) 不要过细地使用异常。最好不要到处使用异常,更不要在循环体内使用异常处理, 可以将它包裹在循环
体外面。
(3)不要捕获了一个异常而又不对它做任何的处理。
try
{
…… //正常执行的代码
}
catch(Exception e) { }

(4) 将异常保留给方法的调用者并非不好的做法。
对于有些异常,将其交给方法的调用者去处理是一种更好的处理办法。没有类似这样if(counter>=20){
IOException ae =new IOException(“buffer is full”);
throw ae; }的语句,是个空语句。



&emsp;
&emsp;
&emsp;
&emsp;

&emsp;
&emsp;

变量及其传递 & 内部类

&emsp;

一、 Java变量及其传递

1.1 基本数据变量与引用型变量

1. 📒Java变量:
&emsp;(1). 基本型变量(8种)
&emsp;&emsp; char,byte,short,int,long,float,double,boolean
&emsp;(2). 引用型变量:对象,接口,数组

T2.png
N.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
举例:
public class MyDate{
private int day=12;
private int month=6;
private int year=1900;
public MyDate(int y,int m,int d){
year=y;
month=m;
day=d;}
void addYear()
{ year++;}
public void display(){
System.out.println(year+"-"+month
+"-"+day);
}

public static void main(String[] args){
MyDate m,n;
m=new MyDate(2003,9,22);
n=m; "//n和m指向同一个地址"
n.addYear();
m.display();
n.display();
System.out.println(m.toString());
System.out.println(n.toString());
}
}
/*运行结果
2004-9-22
2004-9-22
MyDate@11a698a
MyDate@11a698a
*/

&emsp;
&emsp;

1.2 成员变量与局部变量

**成员变量与局部变量有没有区别?** 成员变量若为static的称为类变量,否则称为成员变量,而局部变量是在方法体内的变量。 (1) 📒从语法形式上看,成员变量是属于类或接口的,而局部变量是在方法中定义的变量或方法的参变量; 成员变量可以被public,private,static等修饰,而局部变量则不能被访问控制符及static修饰;成员 变量及局部变量都可以被final修饰。 (2) 📒从变量在内存中的存储方式看,成员变量是对象的一部分,而对象是存在于堆中的,而局部变量是 存在于栈中的。 (3) 📒从变量在内存中的存在时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变 量随着方法的调用而产生,随着方法调用结束而自动消失。 (4) 📒成员变量如果没有赋初值,则会自动以该类型的默认值(0,false,null等)赋值;而局部变量则不会 自动赋值,必须显示地赋值后才能使用。
1
2
3
4
5
6
7
8
class A{
int a;
void m(){
int b;
System.out.println(a); //合法的,a的值为0
System.out.println(b); //不合法,编译不通过
}
}

&emsp;
&emsp;

1.3 变量的传递

1. 📒按值传递:当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。(传递8种基本型变量)
2. 📒按引用传递:当将一个参数传递给一个函数时,函数接收的原始值的内存地址,而不是值的副本,因此,如果修改了该参数,调用代码中的原始值也随之改变。(传递对象,接口,数组)
Java中的参数传递比C++简单,按值传递所有参数,制作所有参数的副本,而不管它们的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
例1:
class TestTransOfValue
{
public static void main(String args[])
{
int val;
StringBuffer sb1, sb2;
val = 10;
sb1 = new StringBuffer("apples");
sb2 = new StringBuffer("pears");
System.out.println("val is " + val);
System.out.println("sb1 is " + sb1);
System.out.println("sb2 is " + sb2);
System.out.println("");

System.out.println("calling modify");
//按值传递所有参数
modify(val, sb1, sb2);
System.out.println("returned from modify");
System.out.println("");
System.out.println("val is " + val);
System.out.println("sb1 is " + sb1);
System.out.println("sb2 is " + sb2);
}

public static void modify(int a, StringBuffer r1,
StringBuffer r2)
{
System.out.println("in modify...");
//在方法中修改基本类型变量的值
a = 0;
//在方法中修改对象引用值的副本
r1 = null; //1
//在方法中修改副本的对象实体值
r2.append(" taste good");
System.out.println("a is " + a);
System.out.println("r1 is " + r1);
System.out.println("r2 is " + r2);
}
}
运行后输出:
val is 10
sb1 is apples
sb2 is pears
calling modify in modify...
a is 0
r1 is null
r2 is pears taste good
returned from modify
val is 10
sb1 is apples
sb2 is pears taste good
/*关于这里的sb1和r1:
当参数传到modify后,r1和sb1指向同一个地址,若改变了r1指向的地址,并不会更改sb1指向的地址。这个时候就
相当于看作两个指针了。
关于sb2和r2:
sb2和r2指向同一个地址,这个地址指向的值(实体值)被改变了,则结束后sb2和r2的实体值也就相应的改变了。
*/

FX.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
例2:
class Swap
{
public static void main(String args[])
{
Integer a, b;
a = new Integer(10);
b = new Integer(50);
System.out.println("before swap...");
System.out.println("a is " + a);
System.out.println("b is " + b);
swap(a, b);
System.out.println("after swap...");
System.out.println("a is " + a);
System.out.println("b is " + b);
}

public static void swap(Integer a, Integer b)
{
Integer temp = a;
a = b;
b = temp;
System.out.println("in swap...");
System.out.println("a is " + a);
System.out.println("b is " + b);
}
}
/*
运行后输出:
before swap...
a is 10
b is 50
in swap…
a is 50
b is 10
after swap...
a is 10
b is 50
*/
**参数传递总结:** (1) Java按值传递所有参数,制作所有参数的副本,而不管它们的类型 (2) 对于基本类型的变量形参修改了并不能反映到函数外面的实参 (3) 对于引用类型的变量,在形参中修改了对象实体值可以反映到实参,在形参中修改了对象引用值, 不能反映到实参

&emsp;
&emsp;

1.4 引用型变量的实体值、引用值比较

1. 📒参数传递总结:
(1) 比较基本类型的数据: ==和!=
(2) 如果要比较引用型变量是否相同,可以直接用==和!=吗? 答:不能

==和!=用来比较引用型变量时(只能比较引用值(地址)是不是相等),只能判断运算符两边引用的是不是同一个对象,即对象的地址值(或对象引用值)!!!!!
如何比较两个对象的内容(对象实体值)是否相同?
用equls()方法:

**2. 📒引用型变量比较总结:** (1) 比较两个变量是否同一个对象(即对象引用值是否相同),用==和!= (2) 比较两个变量的内容是否相同用equals方法 (3) **自己定义的类如果要支持equals方法必须重写从Object类继承来的equals方法(比如前面的String类 是java自己的类就不需要重写equals,但是Employee类就是自己定义的类则需要重写equals)**

Object类中的equals方法:
public boolean equals(Object obj){
return (this==obj);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
equals示例1:
class EqualsTest{
public static void main(String[] arguments)
{
String str1,str2;
str1="Free the bound periodicals.";
str2=str1;
System.out.println("String1: "+str1);
System.out.println("String2: "+str2);
System.out.println("Same object? "+(str1==str2)); //true
str2=new String(str1);
System.out.println("String1: "+str1);
System.out.println("String2: "+str2);
System.out.println("Same object? "+(str1==str2)); //false
System.out.println("Same value? "+str1.equals(str2)); //true
String str3,str4;
str3="busy";
str4="busy"; //str4=new String("busy")或str4=new String(str3)
System.out.println("String3: "+str3);
System.out.println("String4: "+str4);
System.out.println("Same object? "+(str3==str4)); //true
System.out.println("Same value? "+str3.equals(str4)); //true
String str5,str6;
str5="str5busy";
str6="str6busy";
System.out.println("String5: "+str5);
System.out.println("String6: "+str6);
System.out.println("Same object? "+(str5==str6)); //false
System.out.println("Same value? "+str5.equals(str6)); //false
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
equals示例2:
import java.util.*;
public class EqualsTest {
    public EqualsTest() {    }   
public static void main(String[] args){
     Employee alice1=new Employee("Alice",75000,1987,12,15);
     Employee alice2=alice1;
     Employee alice3=new Employee("Alice",75000,1987,12,15);
    Employee bob=new Employee("Bob",50000,1989,10,1);
    System.out.println("alice1==alice2: "+(alice1==alice2)); //true
    System.out.println("alice1==alice3: "+(alice1==alice3)); //false
     System.out.println("alice1.equals(alice3): "+alice1.equals(alice3)); //true
     System.out.println("alice1.equals(bob): "+alice1.equals(bob)); //false
    System.out.println("bob.toString(): "+bob);
}
}

class Employee{
private String name;
 private double salary;
 private Date hireDay;
  public Employee(String n,double s,int year,int month,int day){
   name=n;
   salary=s
    hireDay=new Date(year,month,day);
  }
 public String getName(){  return name;  }
  public double getSalary(){  return salary; }
  public Date getHireDay(){  return hireDay; }
public void raiseSalary(double byPercent){
 double raise=salary*byPercent/100;
 salary+=raise;
 }

public boolean equals(Object otherObject){ //子类覆盖了Object的equals方法
  if(this==otherObject) return true;
  if(otherObject==null) return false;
  if(getClass()!=otherObject.getClass())
  return false;
Employee other=(Employee)otherObject;
  return name.equals(other.name)&&
salary==other.salary&&hireDay.equals(other.hireDay);
//子类覆盖了Object的toString方法
 }
 public String toString(){
  return getClass().getName()+
"[name="+name+",salary="+salary+",hireDay="+hireDay+"]";
 }
 private String name;
 private double salary;
 private Date hireDay;

&emsp;
&emsp;

二、 内部类

2.1 内部类的定义与使用

1. 📒内部类的定义:将类的定义置入一个用于封装它的类(外部类)里。
注意:
内部类不能与外部类同名(否则,编译器无法区分内部类与外部类),如果内部类还有内部类,内部类的内部类不能与它的任何一层外部类同名。
作用:
逻辑分组, 隐藏细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*内部类结合多态可以很方便的隐藏类的细节(包括类名)
*/
interface Contents{
int value();
}
interface Destination {
String readLabel();
}
class Goods{
private class Content implements Contents{ //"private class Contentimplements Contents"
private int i=11;
public int value(){
return i;
}
}
protected class GDestination //"protected class GDestination implements Destination"
implements Destination{
private String label;
private GDestination(String whereTo){
label=whereTo;
}
public String readLabel(){
return label;}}
public Contents cont(){
return new Content();
}
public Destination dest(String s){
return new GDestination(s);
}
}
public class TestGoods { //"public class TestGoods"
public static void main(String[] args){
Goods g=new Goods();
Contents c=g.cont();
Destination d=g.dest("Beijing");
}
}

2. 📒内部类对象的创建:创建非静态内部类的对象时一定要确保已经有一个外部类对象。
(1) 利用外部类的方法创建并返回,因为方法是由外部类对象调用的,那创建该内部类对象时,一定已经拥有了所属的外部类对象了
public Contents cont(){ &emsp;&emsp;&emsp;&emsp; Goods g=new Goods();
return new Content(); &emsp;&emsp;&emsp;&emsp;&ensp; Contents c=g.cont();
}

(2) 创建内部类还可以在除外部类中的其它类中,但是要确保该类具有访问内部类的权限,并且已经创建了一个外部类对象。格式如下:

1
2
3
4
5
6
7
8
outerObject=new outerClass(Constructor Parameters);
outerClass.innerClass innerObject = outerObject.new InnerClass(Constructor Parameters);

Goods g1=new Goods();
Goods.Content c1=g.new Content();
OR
Goods g1=new Goods();
Goods.GDestination d1=g1.new GDestionation();

&emsp;
3. 📒内部类的修饰符:
(1)public、protected、private和缺省:访问权限修饰符,用来限定内部类的访问权限,
&emsp;&emsp;一般的外部类是不能用protected和private修饰的。访问权限修饰符的限定和成员变量的限定一样。
(2)final:表明内部类不能继承。
(3)abstract:抽象内部类,不能被实例化。
(4)static:表明一个静态内部类。

4. 📒静态内部类:
和普通的非静态内部类有较大的不同,使用时要遵循如下原则:
(1)实例化static内部类时,在new前面不需要用对象变量;
(2)static内部类中不能访问其外部类的非static属性及方法,即只能访问static成员;
(3)static方法中不能访问非static的属性及方法,也不能不带前缀地new一个非static的内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class A
{
private int x;
void m(){
new B();
}

static void sm(){
//在外部类的静态方法中
//不能直接创建内部类对象
//new B();//!!error
}
class B
{
B(){x=5;}
}//B类结束
}//A类结束
class Outer
{
static class Inner
{
}
}

class TestInnerStatic
{
public static void main(String[] args)
{
//创建非静态内部类B的对象a_b和ab
A.B a_b=new A().new B();
A a=new A();
A.B ab=a.new B();

//创建静态内部类Inner的对象oi,不需要外部类Outer的对象
Outer.Inner oi=new Outer.Inner();
//Outer.Inner oi2=Outer.new Inner();//!!error
//Outer.Inner oi3=new Outer().new Inner();//!!error
}
}

&emsp;
5. 📒内部类中访问外部类的成员:
(1) 内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。
(2) 如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法:
outerClass.this

&emsp;
&emsp;

2.2 方法和作用域中的内部类

内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。
(1) 内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。
(2) 如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法:
outerClass.this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
例1: 方法中的内部类:
interface Destination {
String readLabel();
}

public class Goods2 {
String a="Goods2";
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
System.out.println("access outer class:"+a);
}
public String readLabel() { return label; }
}
return new GDestination(s);
}
public static void main(String[] args) {
Goods2 g= new Goods2();
Destination d = g.dest("Beijing");
}
}
/*在方法dest()中定义了一个内部类,最后由这个方法返回这个内部类的对象。如果在用一个内部类的时候仅需要创建它的一个对象并传给外部,就可以这样做。
*/

&emsp;
方法中定义内部类的注意点:
(1) 方法中定义的类,在其它地方使用时,没有类的名字,一般用其父类来引用这样的变量。
(2) 同局部变量一样,方法中的内部类前面不能用public,private,protected修饰,也不能用static修饰,但可以被final或abstract修饰。
(3) 方法中的内部类,可以访问其外部类的成员;若是static方法中的内部类,可以访问外部类的static成员。
(4) 方法中的内部类中,不能访问该方法的局部变量,除非是final的局部变量。

&emsp;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
例2: 作用域中的内部类:
public class Goods3{
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Goods3 g= new Goods3();
g.track();
}
}

&emsp;
&emsp;

2.3 匿名内部类

匿名类: 类或方法中定义的一种没有类名的特殊内部类。
作用:当需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。
语法规则如下:
new interfacename(){……};
或new superclassname(){……};

注意:
(1) 这种类不取名字,而直接用其父类的名字或者它所实现的接口的名字;
(2) 类的定义与创建该类的一个对象同时进行,即类的定义前面有一个new,没有类的首部,对象的创建和类体共同构成一个匿名类表达式,后面以“;”结束;
(3) 类中不能定义构造方法,因为它没有名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Contents{
int value();
}
public class Goods4 {
public Contents cont(){
//返回匿名类对象,该匿名类继承了Contents接口
return new Contents(){
private int i = 11;
public int value() {
return i;
}
};
}
public static void main(String[] args)
{
Goods4 g=new Goods4();
Contents c=g.cont();
}
}


&emsp;
&emsp;
&emsp;
&emsp;

&emsp;
&emsp;

接口 & 包

&emsp;

一、 接口

1.1 接口的概念

1. 📒使程序设计和实现相互分离:
在单继承的继承树中,设计和实现不可避免地要纠缠在一起。在设计的时候,人们也许只想提供一个类的抽象的接口,而不希望去具体实现它,因为那是实现阶段的事情。使用接口类型可以很完美地解决这个问题。

2.📒弥补Java只支持单重继承的不足:
Java的类型层次结构具有一定的局限——它只支持单继承,一个类只能有一个父类,而不能交叉继承树的其它分支中有用的部分。这样就给面向对象的程序设计带来了一些困难。但是多继承的引入使得继承层次结构变得混乱,更加容易出错和产生二义性。

3.📒约束实现接口的类:
接口只是约束实现了该接口的类必须满足接口的要求。
当定义一个接口时,实际上是在编写一个契约,用来描述实现其的类能够做什么,能够充当什么角色,具体怎么做和接口无关。

所以,Java抛弃了多继承,引入一种新的层次结构——接口来达到同样的功能。

📒**接口和类的区别:** (1) 类只能单继承,而接口可以多继承。 (2) 类中的方法可以是具体的,也可以抽象的。 接口中的方法都是抽象的。 (3) 接口中的方法要用类来实现,一个类可以实现多个接口。 📒**注意:** (1) 接口可以继承:一个接口可以继承多个其它的接口, (2) 接口中的方法要用类来实现 。 (3) 一个类可以实现多个接口 。 (4) Java接口反映了对象较高层次的抽象,为描述相互似乎没有关系的对象的共性提供了一种有效的手段。

&emsp;
&emsp;

1.2 接口的说明

1
2
3
4
[修饰符] interface 接口名[extends] [接口列表]
{
接口体
}

&emsp;
1. 📒首部:
修饰符:或者不使用修饰符(同包访问),或者只能使用public(任意访问)。归结为只能使用public或者缺省.
extends:定义父接口,支持多重继承(多个父接口用逗号分隔)
&emsp;&emsp;&emsp;&emsp;public interface Cookable extends Foodable,Printable

2.📒接口体:定义常量和抽象方法:
&emsp;接口的成员:成员变量和方法
(1) 接口中的成员变量:都是隐含public、static、final的——静态最终变量(常量),例如在接口中有下面这行代码:
int STEP=5;
等同于:
public static final int STEP=5;
(2) 接口中的方法:接口中说明的方法都是抽象方法,所有方法隐含public和abstract的 ,例如 int increment(int x);
注意, 接口中的方法不能使用下面的修饰符:
static
native或synchronized
final

&emsp;
&emsp;

1.3 接口的使用

&emsp;接口自己不能提供方法的实现,接口中的方法必须由类实现。Java语言用关键字implements声明类中将实现的接口。声明接口的形式:
&emsp;&emsp;[类修饰符] class类名 [extends子句] [ implements 子句]
注意:
在implements子句中可以包含多个接口类型,各个接口类型之间用逗号隔开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[例4-19]  TestInterface.java 接口的使用示例
interface Runner{ public void run();}
interface Swimmer{ public void swim();}
abstract class Animal {abstract public void eat();}

class Person extends Animal implements Runner,Swimmer {
//Person是能跑和游泳的动物,所以继承了Animal,同时实现了Runner和Swimmer两个接口
public void run() {System.out.println("run");}
public void swim(){System.out.println("swim");}
public void eat(){System.out.println("eat");}
}
public class TestInterface{
public static void main(String args[]){
TestInterface t=new TestInterface();
Person p=new Person();
t.m1(p);
t.m2(p);
t.m3(p);
}
public void m1(Runner f){f.run();}
public void m2(Swimmer s){s.swim();}
public void m3(Animal a){a.eat();}
}

&emsp;
&emsp;

1.4 对接口的引用

&emsp;(1) 接口可以作为一种引用类型来使用。任何实现该接口的类的实例都可以存储在该接口类型的变量中,通过这些变量可以访问类所实现的接口中的方法。Java运行时系统动态地确定该使用哪个类中的方法。

&emsp;(2) 把接口作为一种数据类型可以不需要了解对象所对应的具体的类,而着重于它的交互界面。例如,[例4-18]中Person类实现了Runner接口,可以如下赋值,和子类对象赋给父类对象是类似的:
Runner r=new Person();

&emsp;
&emsp;

1.5 使用接口实现多重继承

&emsp;程序设计有时需要表达这样的意思:
&emsp;&emsp; “x从属于a,也从属于b,也从属于c”
C++:多重继承,每个类都有具体的实施细节
Java:接口,只有一个类有具体的实施细节

图片1.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[例4-20]  Swan.java 接口实现多重继承
/* Swan表示天鹅类,它继承了animal基础类,并实现了接口canWalk、canSwim、canFly,即天鹅具有
animal的功能,同时也具有行走、游泳和飞行的功能。这就实现了多重继承。
*/
interface canWalk{ void walk();}
interface canSwim{ void swim();}
interface canFly{void fly();}
abstract class animal{abstract void eat();}
public class Swan extends animial implements canWalk,canSwim,canFly
{ public void walk()
{ System.out.println("swan walking!");
}
public void swim()
{ System.out.println("swan swimming!");}
public void fly()
{ System.out.println("swan flying!");}
void eat(){ System.out.println("swan eating!");}

public static void main(String[] args)
{
Swan swan=new Swan();
swan.walk();
swan.swim();
swan.fly();
swan.eat();
}
}

&emsp;
&emsp;

1.6 抽象类和接口的选择

问题:接口和抽象类都可以使设计和实现分离,那么继承结构中的基础类应该定义成一个普通类、抽象类还是接口 ??
(1) 在语法上都是可以的,但是把基础类定义成抽象类或接口往往会增加程序的灵活性和容错性;
(2) 如果基础类只是定义一些接口,根本不需要具体的实现, 那么首先优先选择使用接口,接口的抽象程度比抽象类更高;如果基础类必须实现方法或者定义成员变量的时候,才考虑采用抽象类。

&emsp;
&emsp;

1.7 多态

📒多个类中的多态:在具有继承关系的多个类中,子类对父类方法的覆盖(不能是重载父类的方法),即子类和父类可以有相同首部的方法,运行的时间决定每个对象到底执行哪个特定的版本。
📒判断程序中是否存在多态:继承(覆盖)、向上转型、动态绑定。

&emsp;1. 覆盖:前面已经讲过,如下:
20-1.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[例4-21] Shapes.java 多态性示例1
class Shape {
void draw() {}
void erase() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()"); }
void erase() {
System.out.println("Circle.erase()");
}}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()"); }
void erase() {
System.out.println("Square.erase()");
}}
public class Shapes {
static void doShape(Shape s){
s.draw();
s.erase(); }
public static void main(String[] args) {
Circle c=new Circle();
Square s=new Square();
doShape(c);
doShape(s);
}}
运行结果:
Circle.draw()
Circle.erase()
Square.draw()
Square.erase()

&emsp;
&emsp;2. 向上转型:子类对象可以直接转换成父类对象,即一个对象除了可以当作自己的类型,还可以作为它的基础类型对待,这种把子类型当作它的基本类型处理的过程,就叫作向上转型(即“Upcasting”)
例如:
Shape s=new Circle();
Circle c=new Circle();
Square s=new Square();
doShape(c);
doShape(s);

&emsp;3. 动态绑定:
&emsp;&emsp;绑定:将一个方法调用同一个方法体连接起来
&emsp;&emsp;早期绑定:程序运行以前执行绑定;
&emsp;&emsp;动态绑定(或后期绑定):在运行期间执行绑定。
Java支持动态绑定:能在运行期间判断参数的实际类型,并分别调用适当的方法体,从而实现了多态性。在Java中所有非final和非static的方法都会自动地进行动态绑定。

如下代码块:
21

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Shape { 
void draw() {}
void erase() {}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()"); }
void erase() {
System.out.println("Triangle.erase()"); }
}
public class Shapes
{
static void doShape(Shape s)
{
s.draw();
s.erase();
}
public static void main(String[] args)
{
Circle c=new Circle();
Square s=new Square();
Triangle t=new Triangle();
doShape(c);
doShape(s);
doShape(t) ;
}
}

&emsp;
&emsp;

二、 包

2.1 包的说明和作用

1. 📒包的说明:
• 包是用于组织类的一种方式,可以对类进行分组,一个包中可以包含任意数量的类和接s口,本身是一种命名机制,具体的表现就是一个文件夹
• 包是一种松散的类的集合:一般不要求处于同一个包中的类有明确的相互关系,如包含、继承等,但是由于同一个包中的类在默认情况下可以相互访问,所以为了方便编程和管理,通常把需要在一起工作的类放在一个包里。

2. 📒包的作用:
(1)包能够让程序员将类组织成单元,通过文件夹或目录来组织文件和应用程序;
(2)包减少了名称冲突带来的问题,可以防止同名的类发生冲突;
(3)包能够更大面积的保护类、变量和方法,而不是分别对每个类进行保护;
(4)包可以用于标示类。

&emsp;
&emsp;

2.2 包的创建

1. 📒无名包:
&emsp;&emsp;系统为每个没有明确指明所属包的.java文件默认创建的包
&emsp; &emsp; 无名包中的类无法被引用和复用

2. 📒有名包:
(1)说明格式 package 包名;
&emsp;&emsp; package SubClass;
&emsp;&emsp; package MyClass.SubClass;
(2)说明语句必须放在整个.java文件的第一行

3. 📒创建包的含义:
&emsp;创建包就是在当前文件夹下创建一个子文件夹,以便存放这个包中包含的所有类的.class文件.

4. 📒可以在不同的文件中使用相同的包说明语句,这样就可以将不同文件中的类都包含到相同的程序包中.

1
2
3
4
package SubClass;         package MyClass.SubClass;
class s1{ class s2{
…… ……
} }

&emsp;
&emsp;

2.3 包的引用

1. 📒使用全名引用:
&emsp;(1) 同包的类相互引用时:
&emsp;&emsp;&emsp; 在使用的属性或方法名前加上类名作为前缀即可.
&emsp;(2) 不同包中的类相互引用时:
&emsp;&emsp;&emsp; 在类名的前面再加上包名——类的全名.
&emsp;&emsp;&emsp; 例如:Class myDate extends java.util.Date{
&emsp;&emsp;&emsp; java.util.Date d=new java.util.Date();
&emsp;&emsp;&emsp; ……
&emsp;&emsp;&emsp; }

2. 📒使用import:
&emsp;&emsp;import可以加载整个包中的文件或包中的某一个文件。import语句的格式为:
&emsp;&emsp;&emsp;&emsp;import package1[.package2…].(classname|*);

1
2
3
4
5
6
7
8
9
10
11
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import java.util.Date;
class myDate extends Date{
Date d=new Date();
……
}

注意:java编译器会为所有程序自动引入包java.lang,因此不必用import语句引入它包含的所有的类,但是若需要使用其他包中的类,必须用import语句引入。

&emsp;
&emsp;

2.4 编译和运行包中的类

当程序中用package语句指明一个包,在编译时产生的字节码文件(.class文件)需要放到相应的以包名为名称的文件夹目录下:
(1)手工建立子目录,以包名命名该目录,再将.class文件复制到相应目录下。
(2)在编译时,使用“javac –d”命令

&emsp;
&emsp;

2.5 Java包

&emsp;&emsp;Java自带了一些包。这些包以“java.”开头,作为java的一个标准部分发布。学习Java,必须学习Java常用包中的类。从第六章开始,主要内容是介绍Java常用包的使用。

包的使用参考。
输入流的使用参考。
&emsp;
&emsp;
&emsp;
&emsp;

&ensp;
&ensp;

继承

&ensp;
&ensp;

一、 构造方法

📒定义: 构造方法是一种特殊的、与类同名的方法,专门用于创建对象,完成初始化工作。

&emsp;

**📒构造方法的特殊性:** **(1)构造方法的方法名与类名相同。** **(2)构造方法没有返回类型,也不能写void。** **(3)构造方法的主要作用是完成对类对象的初始化工作。** **(4)构造方法一般不能由编程人员显式地直接调用,而是用new来调用。** **(5)在创建一个类的新对象的同时,系统会自动调用该类的构造方法为新对象初始化。** **(6)在Java中,每个类都至少有一个构造方法,如果没有显示地定义构造方法,Java 会自动提供一个缺省的构造方法。**

&emsp;
&emsp;

1.1 构造方法说明

📒构造方法说明形式如下:
[构造方法修饰符] 方法名([形式参数列表])[throws异常列表] {方法体}

**📒当构造一个类的实例时,编译器主要完成以下3件事情:** (1) 为对象分配内存空间(堆); (2) 按缺省值初始化对象中的实例变量的值; (3) 调用对象的构造方法(可以在构造方法中初始化其他的值); (4) 对象变量都是引用型变量。

&emsp;
&emsp;

1.2 构造方法的重载

(1) 缺省的构造方法:如果没有定义构造方法,则Java自动提供了一个缺省的构造方法,如下: public Person(){};//对象成员变量的初值按Java规定 (2) 带参数的构造方法:按需要将一些指定的参数传递给构造方法 例如:public Person(String n){Name=n;} **📒定义构造方法注意:**  只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去缺省 构造方法。
1
2
3
4
5
6
7
8
9
10
PersonC(String n ,int a){
name=n;
age=a;
System.out.println("The Person's name is "+name+",age is "+age);}
public static void main(String args[]){
PersonC per1=new PersonC();
PersonC per2=new PersonC("wang li");
PersonC per3=new PersonC("li ming",24);
}
}

&emsp;
&emsp;

1.3 对象的创建

**1.3.1 📒对象说明:** 对象说明的语法形式为:    类名 对象名; **1.3.2. 📒对象的实例化和初始化:** **对象构造的语法形式为:**    对象名=new 类构造方法名([实参表]); **由new操作符和相应的构造方法完成:** new以类为模板,开辟空间并执行相应的构造方法,完成对象的实例化和初始化,并返回该对象的一个引 用(即该对象所在的内存首地址)

&emsp;
&emsp;

1.4 this的使用

**1.4.1 📒使用this来访问成员变量及方法** 在方法及构造方法中,可以使用this来访问对象的属性和方法 . **1.4.2. 📒构造方法中,用this调用另一构造方法:** **1.4.3. 📒使用this的注意事项:** (1)通过this不仅可以引用该类中定义的变量和方法,还可以引用该类的父类中定义的成员变量和方法。 (2)在所有的非static方法中,都隐含了一个参数this。而static方法中,不能使用this。
1
2
3
4
5
6
7
8
9
10
1.4.1
Person(String n,int a){
name=n;
age=a;
}
等价于如下:
Person(String name,int age)
{this.name=name;
this.age=age;
}
1
2
3
4
5
6
7
8
9
10
11
12
1.4.2 
Desk(){ Color="";
Length=0;
Width=0;
Height=0;}
Desk(String C,int L,int W,int H)
{Color=C;
Length=L;
Width=W;
Height=H;}
Desk(){
this("",0,0,0);}

&emsp;
&emsp;

二、 继承

2.1 类继承语法形式

**2.1.1 📒类继承语法形式如下:**   class SubClassName extends SupperClassName extends:继承关键词 SubClassName:新的子类名 SupperClassName:继承的父类名,必须有且只能一个(即每个子类有且只能有一个父类) **2.1.2 📒子类的特性:** (1) 子类拥有其父类的所有属性和方法。但父类中说明为private的属性和方法,子类不可直接访问。 (2) 子类可以对父类的方法覆盖或重载。

&emsp;
&emsp;

2.2 属性的继承、隐藏和添加

注意:属性叫隐藏,方法叫覆盖
2.2.1 📒属性的继承:
• 子类可以继承父类的所有属性(只要该属性没有private修饰)
• 通过属性的继承,子类不需要把父类属性的定义部分重复定义一遍,这样做的好处是减少程序维护的工作量。
例如:ColorPoint类自动具有Point类的坐标a,b属性

&emsp;
2.2.2 📒属性的隐藏:
子类重新定义一个与父类那里继承来的成员变量完全相同的变量,就称作属性的隐藏。

1
2
3
4
5
6
7
 class living
{ public int age; }
class human extends living
{ public int age; //将父类的age隐藏
public static void main(String args[])
{ human h1=new human(); h1.age=20;}
}

&emsp;
2.2.3 📒属性的添加:
在定义子类时,加上的新的属性变量,就可以使子类比父类多一些属性

例如:ColorPoint类比父类Point多了一个属性,颜色(color)

&emsp;
&emsp;

2.3 方法的继承、覆盖、重载和添加

2.3.1 📒方法的继承:
父类的非私有方法也可以被子类自动继承。例如类A具有method()方法,那它的子类B不定义该方法也同样拥有method()。

&emsp;
2.3.2 📒方法的覆盖:

方法的覆盖(方法用覆盖这个词修饰,属性用隐藏这个词来修饰)
覆盖:在子类中定义的方法和父类中的方法的首部是一样的,包括方法名、参数列表、返回类型和异常抛出。但方法体的实现改变了。
1.png

例如:

1
2
3
4
5
6
7
8
class A{
……
int method(int x, int y)
throws Exception{
……
}
……
}
1
2
3
4
5
6
7
8
class B extends A{
……
int method(int x, int y) throws //覆盖父类的方法method(int x, int y)
IOException{
……
}
……
}

&emsp;
覆盖的效果:当一个B类对象调用method方法时它只会使用B类的method版本,而父类A的method版本被覆盖掉了
在覆盖时要注意以下几点:
(1)覆盖的方法的首部必须要和被覆盖的方法的首部完全匹配,才能达到覆盖的效果;
(2)覆盖的方法的返回值类型必须和被覆盖的方法的返回值类型一致;
(3)覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
(4)被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖

&emsp;
2.3.3 📒方法的重载:
含义:方法名相同,但参数列表不同
通过方法的覆盖,能够修改对象的同名方法的具体实现体,相同的方法在各个子类中实现的版本可以不一样.
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; —&ensp;—实现多个类中多态性的基础之一
重载的效果:父类A的method版本不会被覆盖掉,相当于在子类B中有两个method方法,一个来自父类,一个自己加的,它们之间的参数列表不一样.
注意:
(1)在使用重载时只能通过不同的参数表样式。
(2)不能通过访问权限、返回类型、抛出的异常进行重载;(但是同一类里面的成员方法可以这样重载)
(3)被重载的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重载。

特别注意:在继承机制中,不允许在子类中降低成员(包括变量和方法)的访问权限(访问权限大小关系是private < 缺省 < protected < public)。即如果一个方法在父类中是protected的,那么在子类中要重载或覆盖该方法时,就不能把该方法改成是缺省或private,否则会出现编译错误。
&emsp;
习题1:分析下列程序输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{ public void PrintNumber(int a)
{ System.out.println(++a);
}
}
class B extends A
{ public void PrintNumber(int a,int b)
{ System.out.println(a);
}
public static void main(String args[])
{
B b=new B();
b.PrintNumber(100);
}
}
输出结果:101

&emsp;
习题2:分析下列程序输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{ public void PrintNumber(int a)
{ System.out.println(++a);
}
}
class B extends A
{ public void PrintNumber(int a)
{ System.out.println(--a);
}
public static void main(String args[])
{
B b=new B();
b.PrintNumber(100);
}
}
输出结果:99

&emsp;
&emsp;
2.3.4 📒方法的添加:
子类可以新加一些方法,以针对子类实现相应的功能.

&emsp;
&emsp;

2.4 静态成员变量的继承

(1)某类的静态成员为该类及该类的所有子类所共有;
(2)如果子类中新定义的静态成员变量与父类中的某个静态成员变量同名,则这两个静态成员变量相互独立。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(1)
class a
{
public static int b=10; //静态属性
}
class test extends a
{
public static void main(String args[])
{
test.b=20;
System.out.println(a.b);
}
}
输出结果:20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2)
class living
{
public static int age; //静态属性
}
class human extends living
{
public static int age; //静态属性
public static void main(String args[])
{
human.age=20;
System.out.println(living.age);
}
}
输出结果:0

&emsp;
&emsp;

2.5 super的使用

super:代表父类对象,在继承中有重要的作用
📒使用情况:
(1) 子类隐藏了超类中的变量或方法,而在程序中又要使用超类中被隐藏的变量或方法时
&emsp;&emsp;格式:super.变量 ; super.方法([参数表])
(2) 在子类的构造方法中引用超类的构造方法时
&emsp;&emsp;格式: super([参数表])
📒注意: 构造方法不能被继承,但是子类的构造方法能确保它的直接父类
和间接父类的构造方法都被调用,但是注意一点:子类构造方法中调用父类构造方法时,调用语句必须放在子类构造方法中的第一句.

&emsp;
&emsp;

1. 📒使用super访问父类的属性和方法
1.png
&emsp;
&emsp;
2. 📒使用super在子类中调用父类的构造方法
2.png

&emsp;
3. 📒使用super的注意事项
(1)通过super不仅可以访问直接父类中定义的属性和方法,还可以访问间接父类中定义的属性和方法。
(2)由于它指的是父类对象,所以super不能在static环境中使用,包括类变量、类方法和static语句块。
(3)使用super不能访问本类定义的属性和方法。
(4)在构造方法中使用super时,super语句必须放在第一句
(5)在子类的构造方法中,super可以不明确使用,也可以明确使用。

&emsp;
4. 📒明确使用super调用父类构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Point{
int x=0;
Point(int x){
this.x=x;
}


class MapPoint extends Point{
MapPoint(){
super(3); //明确使用
 System.out.println("MapPoint()");
}
public static void main(String[] args){
  new MapPoint();
  }


&emsp;
&emsp;
5. 📒不明确使用super调用父类构造方法
&emsp;当父类重载自己的构造函数时,在子类的构造函数中要明确写出到底是使用父类的哪一个构造函数!!!否则编译器不知道在子类中到底是调用父类的哪一个构造方法!

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point{
int x=0;
Point(int x){
this.x=x;
}


class MapPoint extends Point{
MapPoint(){
 System.out.println("MapPoint()");
  }
public static void main(String[] args){
  new MapPoint();
  }

//编译会出错

答案:因为子类中没有明确使用super,系统会自动在构造方法中加上super(),来调用直接父类的不带参数的构造方法,由于Point类中没有定义不带参数的构造方法,所以编译通不过。以上代码相当于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point{
int x=0;
Point(int x){
this.x=x;
}


class MapPoint extends Point{
MapPoint(){
 super(); //此处不能缺省
 System.out.println("MapPoint()");
  }
public static void main(String[] args){
  new MapPoint();
  }
}

&emsp;
解决方法:
(1)在MapPoint的构造方法中,明确使用super来调用父类已有的构造方法,如super(3);
(2)在Point中加入一个不带参数的构造方法,如Point{}      
(3)去掉Point中全部的构造方法,则编译器会自动加入一个不带参数的构造方法,称为缺省构造方法。

&emsp;
6. 📒构造方法的调用顺序
一个复杂对象的构造方法调用顺序如下:

(1)首先调用父类的构造方法。这个步骤会反复递归,使继承阶层的根源最先被构建,然后是次一层的子类,直至最末一层子类为止;(即先调用最根部的父类,然后依次调用该父类的子类的子类的……..)
(2)根据各个成员的声明顺序,执行成员变量的初始化赋值;
(3)执行该构造方法中的各语句

&emsp;
&emsp;

三、 抽象类与抽象方法abstract

类似于它是一个模板!!!

(1)abstract关键字修饰的类和方法 (2)抽象类不能创建任何对象,抽象类必须产生其子类,由子类创建对象。 (3)抽象类中可以包含抽象方法,也可以不包含抽象方法,但如果类中的某一方法是抽象的,整个类就必须被说 明成抽象的。 (4)抽象方法在子类中必须被实现,否则子类仍是抽象的。 (5)抽象类不是可有可无的。 (6)(类、成员方法、成员属性)访问权限修饰符为abstract。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[例4-17]  抽象类举例
abstract class Shape
{ abstract double area();
abstract void draw(); }
//上述方法对一般图形无法定义其实现
class Rectangle extends Shape {
int width;
int length;
public double area() {
//矩型类实现了Shape类的抽象方法area()
return width*length;
}
public void draw () { … …
//矩型类实现了Shape类的抽象方法draw(),详细可由自己完成}
}

&emsp;
&emsp;

四、 final类和final方法

4.1 final类

如果一个类被final修饰符所修饰和限定,说明这个类不能被继承,即不可能有子类——最终类 例如:*java.lang.System* **📒作用:** final修饰符通常是出于安全的目的而使用的,因为不能继承final类,人们就不能重载或覆盖它的任何方法。 如果允许一个类被继承,其允许被重载或覆盖的方法可能会被改写。保证某个特定的方法在类层次关系上 的某层以后只有一个定义,这对于那些安全性非常关键的类是非常必要的。

&emsp;
&emsp;

4.2 final方法

final修饰符所修饰的方法,是不能被子类所覆盖的方法。 📒**作用:**固定了这个方法所对应的具体操作,可以防止子类对父类关键 方法的错误的重定义,保证了程序的安全性和正确性。 📒**注意:**所有已被private修饰符限定为私有的方法,以及所有包含在final类中的方法,都被默认为是 final的。 因为这些方法不可能被子类所继承,所以不可能被重载,自然都是最终的方法。

&emsp;
&emsp;
&emsp;
&emsp;

&emsp;
&emsp;
&emsp;

序列密码

一、 序列密码分类

1.1.png

1.2.png

1.3.png

1.4.png

&emsp;
&emsp;

1.1 同步序列密码

1.5.png
1.6.png
1.7.png

&emsp;
&emsp;

1.2 自同步序列密码

1.8.png
1.9.png
1.10.png
1.11.png

&emsp;
&emsp;

二、 密钥流与密钥生成器

1.12
1.13
1.14
1.15
1.16
1.17

&ensp;
&ensp;

一、 Java中类的定义、类访问权限

1.1 类的定义格式:

[修饰符] class 类名 [extends父类名] [implements接口名列表]
&emsp;
&emsp;
1

&emsp;

1.2 类修饰符的访问权限:
类修饰符 访问权限
缺省(默认方式) 这种类只能被同一个包中的类访问;
public(公共) 它修饰的类能被所有的类访问;
abstract(抽象) 它修饰的类不能被实例化,它可能包含有未实现的方法。
extends(继承) 该保留字用来表明新创建的类继承哪个类, 被继承的类称为此类的父类。extends后面只能跟一个父类名称, 因为Java中一个类最多能继承一个类(单继承)。
final(最终) 它修饰的类不能被继承,即不能有子类。
implements (实现) 该保留字用来表明这个类实现了哪些接口,接口名可以有多个。

&emsp;

1.3 关于类访问权限的总结:
1. 具有继承关系的子类可以继承父类的一些成员变量,即可以不创建对象就可以直接访问,如果是 同一个包的子类可以继承到public、缺省和protected修饰的变量,如果是不同的包的子类就只能继承 到public 和protected的; 2. 如果是其他类,不管是一个包还是不在一个包,都要创建该类的对象才能引用 3. 如果是main方法,不管是本类还是非本类,要访问实例变量都要创建对象,可以引申到其他所有 的类方法中 4. 私有成员只能在本类中访问,如果在main方法中访问私有成员 必须创建对象

&ensp;
&ensp;

二、 成员变量访问权限

&ensp;
2

&ensp;
&emsp;

2.1 成员变量访问权限总结为:
**(1) 访问修饰符缺省** 访问修饰符缺省时,成员变量只能被同一包(package) 中的所有类访问,所以也称为包(package)变量。 (缺省访问修饰符实际是friendly修饰符,但因为friendly不是Java语言的关键字,所以friendly修饰符 不能显式说明。) **(2) public(公共)** public修饰的成员变量可以被程序中的任何类所访问。由于public成员变量不受限制, 这易使类的对象引起 不希望的修改,建议成员变量尽量不要使用public修饰符。 **(3) protected (受保护)** protected修饰的成员变量可以被本包及有继承关系的类自由访问。 **(4) private (私有)** private修饰的成员变量只能在同一个类中使用。这种方式通常是最为安全的。

&ensp;
&ensp;

三、 static(静态)——静态变量访问权限

(1)**static修饰的成员变量称为****类变量(静态变量)****不用static修饰的成员变量又叫****对象变量(实例变量)。** (2)**区别:**对象变量依附于具体的对象实例,它的值因具体对象实例的不同而不同,而类变量为该类的所有 对象所共享,它的值不因类的对象不同而不同。 (3)可以通过类来访问静态成员变量,也可以通过该类的对象访问静态成员变量。   **形式:类名.成员变量/对象名.成员变量** (4)**反过来,非staic的对象方法可以直接使用static的类变量** (5)类方法不能直接调用类中的对象方法(非static方法)。 (6)定义变量时必须当即赋值。

&emsp;
&emsp;
&emsp;
&emsp;

&ensp;
&ensp;

标识符,注释和分隔符、变量和常量、基本类型
   

一、 标识符、注释和分隔符

📒标识符:是程序员用来标记语言中元素(变量、常量、类、对象等)名称的命名记号。

Java中的标识符遵守先定义后使用的原则。即只有定义了的标识符,才可在语句部分使用。

Java定义标识符的规则:
(1)由字母、 数字、下划线、$组成,不能由数字开头
(2)不能是Java中的保留字(关键字);
(3)大小写敏感,长度无限制。

&emsp;

类型 保留字
类型保留字 boolean, byte, char, short, int, long,float, double, void
语句保留字 if, else, switch, case, default, break, for, while, do,return,continue,try,catch,finally,throw,synchronized
表达式保留字 new, null, this, super
修饰符保留字 abstract, final, public, private, protected, static, transient, volatile
类、方法保留字 class, throws, native, instanceof
扩展类构筑模块保留字 package, import, extends, implements, interface
其它保留字 cast, futuer, generic, inner, oprater, outer, rest,var

&emsp;
&emsp;

1.1 标识符
**举例**: 合法标识符: A , a1, $Systembol, square, ex_sa 不合法标识符: 1a (以数字1开头) break (禁止使用保留字) TWO WORDS (含有空格) .NO (有圆点)

&emsp;

1.2 注释
**Java有三种注释形式:** 1.“//” 单行注释。表示从此向后,直到行尾都是注释。 2.“/\*……\*/“ 块注释。在“/\*”和“\*/”之间都是注释。 3.“/\*\*……\*/”文档注释。所有在“/\*\*”和“\*/”之间的内容可以用来自动形成文档。

&emsp;

1.3 语句
与C++一样,Java中的语句也是最小的执行单位。     各个语句之间以 ; 间隔     X大括号{ }内的一系列语句称为语句块


&emsp;
&emsp;

二、 变量和常量

2.1 变量
📒Java的变量有两种:**局部变量 类成员变量** 变量必须先定义后使用!变量的定义形式如下:       类型 变量名表;

注意:局部变量在使用前必须给定初值,否则,将编译出错,而类成员变量无此要求。

如下出现编译错误,缺少“y=8;”

1
2
3
4
5
6
7
8
9
10
class test{
int x;
void method(){
int y;
System.out.println(x)
System.out.println(y);}
public static void main(String[] args){
test t=new test();
t.method();}
}

&emsp;

2.1.1 变量名
**1. 📒在Java中用标识符来表示变量名**。   1.1 变量名中字母大写和小写是有区别的,如name和Name表示两个不同的变量名;   1.2 变量名标志存放变量值的内存位置,其内存存放的内容是变量的值。 **2. 📒命名方法**: 2.1 程序员应该为变量取有意义的名称,以利于程序的阅读和理解; 2.2 习惯上,变量名以小写字母开头,若一个变量名由超过一个单词所组成,则第一个单词之后的所有单词都以大写字母开头,以便于理解该变量名。   例如,anIntVar

&emsp;

2.1.2 变量的类型
**变量类型用于表述数据的格式和结构。** 📒**Java中的数据类型可分为两大类:** **基本数据类型:**整数类型、实数类型、字符型、布尔型 **构造数据类型:**Java 语言中构造类型用类来描述。


&emsp;
&emsp;

三、 基本数据类型表:变量和常量

1

&emsp;

3.1 整型

2

&emsp;

3.1.1 整型常量

3

&emsp;

3.1.2 整型变量

4

&emsp;

3.2 实型

5

&emsp;

3.2.1实型常量有两种表示法:
**(1)十进制表示法**   -3.5f 0.0f 123.45f +678.9f 要注意,采用十进制表示法时,小数点的两侧都必须有数字, 缺一不可,如+123.和 .56都是不合法的。 **(2)科学表示法:** **<尾数> E <阶码 > **   -1.234567E+12 (-1.2345678901×1012)   尾数 阶码 **注意:**     尾数必须有,但小数部分可无;     阶码必须有,必须是整数。     基数是10
**下列表示都是不正确的。**   E-6  (缺尾数)   3E  (无阶码)   2.E3  (尾数出错)   2E1.2  (阶码非整数)   **📒实型变量的定义** 例如: float aFloatVar; double aDoubleVar;

&emsp;

3.3 布尔型
**📒布尔数据类型用于表达逻辑状态:**   布尔型数据只有两个值:true和false。   **注意: Java中不可将布尔类型看做整型值。true不对应1,false不对应0,不像C++一样。**

&emsp;

3.4 字符型
**📒字符类型用来表示单个字符,采用16位二进制Unicode 编码表示** 字符常量是用两个单引号括起来的一个字符   例如: 'A'; 'a'; '2'; '我'; 也可用unicode代码表示一个字符常量   例如: '\u0041'

&emsp;
&emsp;

四、 Unicode编码

**📒Unicode编码:** 1. Java的字符使用Unicode编码,使用两个字节(16位),所以它几乎包括所有字符,中文、日文、…,它支持世 界上所有语言。 2. 大多数语言使用ASCII码,用8位表示一个字符。ASCII码是Unicode码的一个子集,Unicode表示ASCII 码时,其高位为0,它是其前255个字符。Unicode字符通常用十六进制。例如“\u0000”-“\u00ff”表示 ASCII码集。”\u”表示转义字符,它用来表示其后四个十六进制数字是Unicode代码。

&emsp;
&emsp;
&emsp;
&emsp;

&ensp;

一、User-Agen

注意在HTTP头部参数的首字母大写格式!

🌸1.1 什么是User-Agen
**1.1.1**、 Http Header之User-Agen的User-Agen中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA。它是一个特殊字符串头,是一种向访问网站提供你所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。通过这个标识,用户所访问的网站可以显示不同的排版从而为用户提供更好的体验或者进行信息统计;例如用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的UA来判断的。UA可以进行伪装。浏览器的UA字串的标准格式:浏览器标识 (操作系统标识; 加密等级标识; 浏览器语言)渲染引擎标识版本信息。但各个浏览器有所不同。
**1.1.2**、用较为普通的一点来说,是一种向访问网站提供你所使用的浏览器类型、操作系统及版本、CPU 类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识。UA字符串在每次浏览器 HTTP 请求时发送到服务器!User-Agen也是用户识别的重要部分,通过分析,能知道用户使用的是什么设备、什么浏览器、什么应用等,进而可以分析其购买力、属性、职业等。

&ensp;

🌸1.2 对UA字串的说明

1.2.1
&emsp;出于兼容及推广等目的,很多浏览器的标识相同,因此浏览器标识并不能说明浏览器的真实版本,真实版本信息在 UA 字串尾部可以找到。



1.2.2 操作系统标识
FBlnde.jpg



1.2.3 加密等级标识
&emsp;N: 表示无安全加密
&emsp;I: 表示弱安全加密
&emsp;U: 表示强安全加密



1.2.4 浏览器语言
&emsp;在首选项 > 常规 > 语言中指定的语言



1.2.5 渲染引擎
&emsp;显示浏览器使用的主流渲染引擎有:Gecko、WebKit、KHTML、Presto、Trident、Tasman等,格式为:渲染引擎/版本信息



1.2.6 版本信息
&emsp;显示浏览器的真实版本信息,格式为:浏览器/版本信息
注:
1、在广告定向设定中,浏览器定向和操作系统定向均是针对User-Agent中的信息进行定向。
2、欲了解更多的User-Agent信息,请参考User-Agen 字串史



&ensp;

🌸1.3 User-Agen的作用
通过user-agent不能完全准确的判断是属于那款浏览器。由于UA字符串在每次浏览器HTTP 请求时发送到服务器,所以服务器就可以根据它来做好多事。 比如: 1、统计用户浏览器使用情况。有些浏览器说被多少人使用了,实际上就可以通过判断每个IP的UA来确定这个IP是用什么浏览器访问的,以得到使用量的数据。 2、根据用户使用浏览器的不同,显示不同的排版从而为用户提供更好的体验。有些网站会根据这个来调整打开网站的类型,如是手机的就打开wap,显示非手机的就打开pc常规页面。用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的UA来判断的。 既然知道了UA的作用,那么其实客户端也可以使用UA来做一些神奇的事。 比如:伪装 User-Agen 来回避某些侦测特定浏览器才能读取的网站。 如果使用Firefox浏览器插件User-Agen switcher,用户就可以轻松地在不同UA之间切换,把自己伪装成其他浏览器。这样就可以在PC上预览WAP或移动格式的网页,比如专门为iPhone设计的页面。

&ensp;

二、Cookie——用户追踪之基础技术

2.1 关于Cookie
2.1.1 前言

&emsp;Cookie是如此的重要,以至于我们后面要讲到的回头客定向、访客频次定向、用户定向等等都需要基于此技术才可以实现,并且我们日常工作中所能见到的第三方监测工具如doubleclick、99click、秒针等也都要利用cookie技术,网站分析工具如GA、百度统计、CNZZ等也需要利用Cookie。如果没有Cookie,互联网广告市场将受到巨大打击,尤其对于目前我们谈论的精准广告而言。如果没有Cookie,网站分析也不从做起,遑论优化了。



2.1.2 Cookie是什么

&emsp;Cookie在英文中是小甜品的意思,但在计算机语言中,Cookie指的是当你浏览某网站时,网站存储在你电脑上的一个小文本文件,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。它记录了你的用户ID,密码、浏览过的网页、停留的时间等信息,用于用户身份的辨别。Cookie通常是以user@domain格式命名的,user是你的本地用户名,domain是所访问的网站的域名。



2.1.3 为什么要Cookie

&emsp;因为HTTP协议是无状态的,对于一个浏览器发出的请求,服务器无法区分是不是同一个来源,无法知道上一次用户做了什么。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据,用于维护浏览器和服务器的会话。我们可以想象一个场景,你没有登录京东时在京东上购物,选择了3件商品放入购物车,在结算时,京东为什么还能知道这三件商品是什么?没错,是Cookie!



2.1.4 Cookie的工作原理

&emsp;Cookie利用网页代码中的HTTP头信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。例如:当你在浏览器地址栏中键入了Amazon的URL,浏览器会向Amazon发送一个读取网页的请求,并将结果在显示器上显示。在发送之前,该网页在你的电脑上寻找Amazon网站设置的Cookie文件,如果找到,浏览器会把Cookie文件中的数据连同前面输入的URL一同发送到Amazon服务器。服务器收到Cookie数据,就会在他的数据库中检索你的ID,你的购物记录、个人喜好等信息,并记录下新的内容,增加到数据库和Cookie文件中去。如果没有检测到Cookie或者你的Cookie信息与数据库中的信息不符合,则说明你是第一次浏览该网站,服务器的CGI程序将为你创建新的ID信息,并保存到数据库中。(此例子来源于百度百科——Cookie)



2.1.5 关于Cookie的一些知识点

&emsp;1、Cookie是基于浏览器的,因此当电脑上安装多个浏览器时,服务器会生成多个Cookie。虽然是同一个人,但服务器是识别为多个用户。
&emsp;2、Cookie是基于浏览器的,因此当同一台电脑有多个人使用时,服务器也只会生成一个Cookie。虽然是多个人,但服务器会认为是一个用户。
&emsp;3、Cookie是无法跨设备进行设置的。比如我们在单位和家里分别使用两台电脑,即使我们使用同一种同一版本的浏览器,我们还是生成了两个Cookie,服务器会认为是两个用户。(PS:现在有些浏览器可以同步数据,比如Chrome、Friefox,可以避免这种问题)
请注意:以上所说的Cooke指的全部是Http Cookie。有一种Cookie——Flash Cookie,可以解决多浏览器的问题



&emsp;FlashCookie是由FlashPlayer控制的客户端共享存储技术,鉴于目前Flash技术的普遍性,几乎所有的网站都采用,所以具有同Http Cookie一样的作用。在技术上,通过使用JavaScript与ActionScript可以将Http Cookie和Flash Cookie进行互通。
Flash cookie的优势在于:
1、跨浏览器
不管用户的计算机上安装了多少个浏览器或者浏览器的不同版本,使用Flash Cookie能够使所有的浏览器共用一个Cookie。
2、不易删除
所有的浏览器均提供了清除Http Cookie的快捷方式,但Flash Cookie并没有此种方式,并且其保存位置非常隐蔽,网民难以删除。
3、容量更大
Flash Cookie可以容纳最多100千字节的数据,而一个标准的HTTP Cookie只有4千字节。



2.1.7 Cookie的数量

&emsp;1、大多数浏览器支持最大为 4096 字节的 Cookie。因此最好用 Cookie 来存储用户 ID 之类的标识符,用户的详细信息则通过用户 ID从数据库或其他数据源中读取。
&emsp;2、浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;当存储更多 Cookie时,最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。



2.1.8 Cookie的失效时间

&emsp;1、浏览器的Cookie设置会决定是否保存Cookie数据。如果浏览器不允许Cookie保存,则关掉浏览器后,这些数据就消失。
&emsp;2、如果浏览器允许保存Cookie,那么Cookie的时间由服务器的设置决定。Cookie有一个Expires(有效期)属性,这个属性决定了Cookie的保存时间,服务器可以通过设定Expires字段的数值,来改变Cookie的保存时间。如果不设置该属性,那么Cookie只在浏览网页期间有效,关闭浏览器,这些Cookie自动消失,绝大多数网站属于这种情况。通常情况下,Cookie包含Server、Expires、Name、value这几个字段,其中对服务器有用的只是Name和value字段,Expires等字段的内容仅仅是为了告诉浏览器如何处理这些Cookies。



参考:《【总结】浏览器 User-Agent 大全》《User-Agent详解》.
&ensp;
&ensp;
&ensp;
&ensp;