一、Java语言发展简史
Java 语言源于 1991 年 4 月, Sun 公司 James Gosling 博士 领导的绿色计划 (Green Project) 开始启动,此计划最 初的目标是开发一种能够在各种消费性电子产品( 如机顶盒、冰箱、收音机等 ) 上运行的程序架构。这个就是 Java 的 前身: Oak ( 得名与 Java 创始人 James Gosling 办公室外的一棵橡树 ) ,但由于这些智能化家电的市场需求没有预期的高,Sun 公司放弃了该项计划。随着 1995 年代互联网的发展 , Sun 公司看见 Oak 在互联网上应用的前景,于是改 造了 Oak ,于 1995 年 5 月以 Java 的名称正式发布,并提出 "Write once, Run anywhere" 的口号 。
二、Java语言特性
1、简单性
Java语法是C++语法的一个"纯净版本",相当于对C++做了一个减法。这里没有头文件、指针运算(甚至指针语法)、结构、联合、操作符重载、虚基类等等。不仅如此,Java开发环境远远超出大多数其他编程语言的开发环境。
2、面向对象
什么是面向对象?
在Java的世界里,一切皆对象。比如:人,电脑,熊猫,竹子等都是对象。所谓面向对象,就是依靠对象之间的交互来完成事情,,比如:人用电脑上网,熊猫吃竹子...
3、分布式(微服务)
Java有丰富的例程库,用于处理像 HTTP 和 FTP 之类的 TCP/IP 协议。 Java 应用程序能够通过 URL 打开和访问网络上的对象,其便捷程度就好像访问本地文件一样。
4、健壮性
Java 与 C++ 最大的不同在于 Java采用的指针模型可以消除重写内存和损坏数据的可能性。不仅如此, Java 编译器能够检测许多在其他语言中仅在运行时才能够检测出来的问题。
5、安全性
Java 适用于网络 / 分布式环境。为了达到这个目标,在安全性方面投入了大量的精力。使用 Java 可以构建防病毒、防篡改的系统
6、体系结构中立
编译器生成一个体系结构中立的目标文件格式,按照该规范生成的文件,只要有 Java 运行时系统,这些编译 后的代码就可以在许多处理器上运行。Java 编译器通过生成与特定计算机体系结构无关的字节码指令来实现 这一特性 。精心设计的字节码不仅可以很容易的在任何机器上解释执行,而且还可以动态地翻译成本地机器 代码。这就是为什么可以: "Wrice once , Run anywhere"。而且其他语言编写的程序,在编译后如果能够严格按照字节码文件的规范生成.class 文件,也可以在 JVM 上运行。
7、可移植性
例如, Java 中的 int 永远是 32 位的整数,而在 C/C++ 中, int 可能是 16 位整数、 32 位整数,也可能是编译器 提供商指定的其他大小。在Java 中,数据类型具有固定的大小,这消除了代码移植时令人头疼的主要问题。
8、解释性
Java 为了实现与平台无关,自己维护了一套基于栈架构的指令集, Java 源代码经过编译之后,字节码文件中的 指令就是按照自己的指令集来组织的,但是在具体硬件环境中运行时,系统并不能识别,因为Java 程序在执行时,Java 解释器会逐条的将字节码文件中的指令翻译成 CPU 的指令集。
9、高性能
近年来 JVM 也在不断的优化,比如: JIT( 即时编译 器) ,热点代码探测,让 Java 程序的执行效率大幅提高,在有些场合不亚于 C/C++ 。
10、多线程
Java 在当时很超前。它是第一个支持并发程序设计的主流语言。多线程可以带来更好的交互响应和实时行为。并发程序设计绝非易事,但是Java 在这方面表现出色,可以很好的管理这个工作。
11、动态性
Java 与 C/C++ 相比更加具有动态性。它能够适应不断发展的环境。库中可以自由的添加新方法和实例变量,而 对客户端没有任何影响。在Java 中找出运行时类型信息十分简单。
因此: Java 不仅仅是一门编程语言,也是一个由一些列计算机软件和规范组成的技术体系 。
三、初认Java
3.1 Java结构
Java 程序的结构由如下三个部分组成:
- 源文件(扩展名为*.java):源文件带有类的定义。类用来表示程序的一个组件,小程序或许只会有一个 类。类的内容必须包含在花括号里面。
- 类:类中带有一个或多个方法。方法必须在类的内部声明。
- 方法:在方法的花括号中编写方法应该执行的语句。
总结一下:类存在于源文件里面;方法存在于类中;语句存在于方法中。
注意:在一个源文件中只能有一个 public 修饰的类,而且源文件名字必须与 public 修饰的类名字相同 。
3.2 运行java程序
Java 是一门半编译型、半解释型语言。先通过 javac 编译程序把源文件进行编译,编译后生成的 .class 文件是由字节 码组成的平台无关、面向JVM 的文件。最后启动 java 虚拟机来运行 .class 文件,此时 JVM 会将字节码转换成平台能够理解的形式来运行。
注: JDK、JRE、JVM之间的关系?
- JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。
- JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程序运行的所需环境。
- JVM:Java虚拟机,运行Java代码
3.3 注释
Java 中的注释主要分为以下三种
- 单行注释:// 注释内容(用的最多)
- 多行注释:/* 注释内容*/
- 文档注释: /** 文档注释 */(常见于方法和类之上描述方法和类的作用),可以被javadoc工具解析,生成一套以网页文件形式体现的程序说明文档
注意:
- 多行注释不能嵌套使用
- 不论是单行还是多行注释,都不参与编译,即编译之后生成的.class文件中不包含注释信息。
3.4 标识符
在程序中由用户给类名、方法名或 者变量所取的名字 。
【 规则 】
标识符中可以包含:字母、数字以及 下划线和 $ 符号等等。
注意:标识符不能以数字开头,也不能是关键字,且严格区分大小写
推荐的写法**:**
- 类名:每个单词的首字母大写(大驼峰)
- 方法名:首字母小写,后面每个单词的首字母大写(小驼峰)
- 变量名:与方法名规则相同
3.5 关键字
关键字是由 Java 语言提前定义好的,有特殊含义的标识符,或者保留字 。
|------------------|---------------|----------------|-----------------|-------------|
| class | extends | implements | interface | inport |
| package | break | case | continue | default |
| do | if | else | for | return |
| switch | while | false | short | null |
| boolean | byte | char | try | int |
| long | float | double | abstract | catch |
| throw | throws | finally | public | final |
| native | private | protected | instance of | static |
| synchronized | transient | volatile | true | new |
| super | void | assert | enum | goto |
| const | | | | |
注:在Java中关键字都是小写的。
3.6 数据类型与变量
3.6.1 字面常量
常量即程序运行期间,固定不变的量称为常量
字面常量的分类:
- 字符串常量:由""括起来的,比如"12345"、"hello"、"你好"。
- 整形常量:程序中直接写的数字(注意没有小数点),比如:100、1000
- 浮点数常量:程序中直接写的小数,比如:3.14、0.34
- 字符常量:由 单引号 括起来的当个字符,比如:'A'、'1'
- 布尔常量:只有两种true和false
- 空常量:null
3.6.2 数据类型
在 Java 中数据类型主要分为两类: 基本数据类型 和 引用数据类型 。
基本数据类型有四类八种:
- 四类:整型、浮点型、字符型以及布尔型
- 八种:
注意:
- 不论是在16位系统还是32位系统,int都占用4个字节,long都占8个字节
- 整形和浮点型都是带有符号的
- 整型默认为int型,浮点型默认为double
- 字符串属于引用类型
3.6.3 类型转换
Java 作为一个强类型编程语言 , 当不同类型之间的变量相互赋值的时候 , 会有较严格的校验 .
在 Java 中,当参与运算数据类型不一致时,就会进行类型转换。 Java 中类型转换主要分为两类:自动类型转换 ( 隐式) 和 强制类型转换 ( 显式 ) 。
(1)自动类型转换(隐式)
自动类型转换即: 代码不需要经过任何处理,在代码编译时,编译器会自动进行处理 。特点: 数据范围小的转为数 据范围大的时会自动进行 。
java
int a = 100;
long b = 10L;
//a和b都是整形,a的范围小,b的范围大,当将a赋值给b时,编译器会自动将a提升为long类型,然后赋值
b = a;
//编译报错,long的范围比int范围大,会有数据丢失,不安全
a = b;
(2)强制类型转换(显式)
强制类型转换:当进行操作时,代码需要经过一定的格式处理,不能自动完成。特点:数据范围大的到数据范围小 的。
java
int a = 10;
long b = 100L;
//int--->long,数据范围由小到大,隐式转换
b = a;
//long--->int,数据范围由大到小,需要强转,否则编译失败
a = (int)b;
注意:
- 不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型
- 如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢失
- 将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查
- 强制类型转换不一定能成功,不相干的类型不能互相转换
3.6.4 类型提升
不同类型的数据之间相互运算时,数据类型小的会被提升到数据类型大的。
(1)int与long之间:int会被提升为long
java
int a = 10;
long b = 20;
int c = a + b; // 编译出错: a + b==>int + long--> long + long 赋值给int时会丢失数据
long d = a + b; // 编译成功:a + b==>int + long--->long + long 赋值给long
(2) byte与byte的运算
java
byte a = 10;
byte b = 20;
byte c = a + b;
System.out.println(c);
// 编译报错,不兼容的类型: 从int转换到byte可能会有损失
//以下是正确的写法
byte c = (byte)(a + b);
结论 : byte 和 byte 都是相同类型 , 但是出现编译报错 . 原因是 , 虽然 a 和 b 都是 byte, 但是计算 a + b 会先将 a 和 b 都提升成 int, 再进行计算 , 得到的结果也是 int, 这是赋给 c, 就会出现上述错误 .
由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据 . 为了硬件上实现方便 , 诸如 byte 和 short 这种低于 4 个字节的类型 , 会先提升成 int, 再参与计算
【类型提升小结 :】
- 不同类型的数据混合运算, 范围小的会提升成范围大的.
- 对于 short, byte 这种比 4 个字节小的类型, 会先提升成 4 个字节的 int , 再运算.
3.7 字符串类型
在Java中使用String类定义字符串类型
在有些情况下,需要将字符串和整形数字之间进行转换:
(1) int 转成 String
java
int num = 10;
//方法1
String str1 = num + "";
//方法2
String str2 = String.valueOf(num);
(2) String 转成 int
java
String str = "100";
int num = Integer.parseInt(str);
3.7 运算符
3.7.1 算术运算符
(1) 基本四则运算符:加减乘除模(+-*/%)
java
int a = 20;
int b = 10;
System.out.println(a + b); // 30
System.out.println(a - b); // 10
System.out.println(a * b); // 200
System.out.println(a / b); // 2
System.out.println(a % b); // 0 --->模运算相当于数学中除法的余数
注意:
-
都是二元运算符,使用时必须要有左右两个操作数
-
int / int 结果还是int类型,而且会向下取整
-
做除法和取模时,右操作数不能为0
-
% 不仅可以对整形取模,也可以对double类型取模,但是没有意义,一般都是对整形取模的
java
System.out.println(11.5 % 2.0);
// 运行结果
1.5
- 两侧操作数类型不一致时,向类型大的提升
java
// +的左侧是int,右侧是double,在加之前int被提升为double.故:输出1.2
System.out.println(1+0.2);
3.7.2 增量运算符
该种类型运算符操作完成后,会将操纵的结果赋值给左操作数。
java
int a = 1;
a += 2; // 相当于 a = a + 2
System.out.println(a); // 输出3
注意:只有变量才能使用该运算符,常量不能使用。
3.7.3 自增**/自减运算符 ++ --**
++ 是给变量的值 +1 , -- 是给变量的值 -1 。
java
int a = 1;
a++; // 后置++ 表示给a的值加1,此时a的值为2
System.out.println(a++); // 注意:后置++是先使用变量原来值,表示式结束时给变量+1,因此输出2
System.out.println(a); // 输出3
++a; // 前置++ 表示给a的值加1
System.out.println(++a); // 注意:前置++是先给变量+1,然后使用变量中的值,因此输出5
System.out.println(a); // 输出5
// --操作符给操作-1,与++含义类似
注意:
- 如果单独使用,【前置++】和【后置++】没有任何区别
- 如果混合使用,【前置++】先+1,然后使用变量+1之后的值,【后置++】先使用变量原来的值,表达式 结束时给变量+1
只有变量才能使用自增 / 自减运算符,常量不能使用,因为常量不允许被修改
3.7.4 关系运算符
关系运算符主要有六个 : == != < > <= >= ,其计算结果是 true 或者 false 。
java
int a = 1;
int b = 2;
// 注意:在Java中 = 表示赋值,要与数学中的含义区分
// 在Java中 == 表示相等
System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a < b); // true
System.out.println(a > b); // false
System.out.println(a <= b); // true
System.out.println(a >= b); // false
注意:当需要多次判断时,不能连着写,比如: 3 < a < 5 , Java 程序与数学中是有区别的
3.7.5 逻辑运算符
逻辑运算符主要有三个 : && || ! ,运算结果都是 boolean 类型。
(1)逻辑与 &&
语法规则:表达式 1 && 表达式 2 ,左右表达式必须是 boolean 类型的结果。
两个表达式都为真,结果才是真,只要有一个是假,结果就是假
(2)逻辑或 ||
语法规则:表达式 1 || 表达式 2 ,左右表达式必须是 boolean 类型的结果。
注意:左右表达式至少有一个位真,则结果为真
(3)逻辑非 !
语法规则: ! 表达式
真变假,假变真。
(4)短路求值
&& 和 || 遵守短路求值的规则
java
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
注意 :
- 对于 && , 如果左侧表达式值为 false, 则表达式结果一定是 false, 无需计算右侧表达式.
- 对于 ||, 如果左侧表达式值为 true, 则表达式结果一定是 true, 无需计算右侧表达式.
- & 和 | 如果表达式结果为 boolean 时, 也表示逻辑运算. 但与 && || 相比, 它们不支持短路求值.
java
System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常
3.7.6 位运算符
位运算符主要有四个 : & |
~ ^ ,除 ~ 是一元运算符外,其余都是二元运算符。
位操作表示 按二进制位运算 . 计算机中都是使用二进制来表示数据的 (01 构成的序列 ), 按位运算就是在按照二进制位 的每一位依次进行计算
(1)按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0.
(2)按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1.
注意**:**当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表 示逻辑运算.
(3)按位取反 ~:如果该位为 0 则转为 1, 如果该位为 1 则转为 0
(4)按位异或 ^**:**如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1.
注意:如果两个数相同,则异或的结果为0
3.7.7 条件运算符
条件运算符只有一个 :
表达式 1 ? 表达式 2 : 表达式 3
当 表达式 1 的值为 true 时 , 整个表达式的值为 表达式 2 的值 ;
当 表达式 1 的值为 false 时 , 整个表达式的值为 表达式 3 的值 .
也是 Java 中唯一的一个 三目运算符 , 是条件判断语句的简化写法 .
java
// 求两个整数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;
注意:
-
- 表达式2和表达式3的结果要是同类型的,除非能发生类型隐式类型转换
-
- 表达式不能单独存在,其产生的结果必须要被使用
3.7.8 运算符的优先级
在一条表达式中,各个运算符可以混合起来进行运算,但是运算符的优先级不同。
注意:运算符之间是有 优先级 的 . 具体的规则我们 不必记忆 . 在可能存在歧义的代码中加上括号即可
3.8 逻辑控制
3.8.1 顺序结构
顺序结构 比较简单,按照代码书写的顺序一行一行执行。
3.8.2 分支结构
(1)if语句
java
if(布尔表达式1){
// 语句1
}else if(布尔表达式2){
// 语句2
}else{
// 语句3
}
表达式 1 成立,执行语句 1 ,否则表达式 2 成立,执行语句 2 ,否则执行语句3
(2)switch 语句
java
switch(表达式){
case 常量值1:{
语句1;
[break;]
}
case 常量值2:{
语句2;
[break;]
}
...
default:{
内容都不满足时执行语句;
[break;]
}
}
执行流程:
- 先计算表达式的值
- 和case依次比较,一旦有响应的匹配就执行该项下的语句,直到遇到break时结束
- 当表达式的值没有与所列项匹配时,执行default
【 注意事项 】
- 多个case后的常量值不可以重复
- switch的括号内只能是以下类型的表达式:
基本类型: byte 、 char 、 short 、 int ,注意不能是 long 类型
引用类型: String 常量串、枚举类型
3.8.3 循环结构
(1)while循环
java
while(循环条件){
循环语句;
}
循环条件为 true, 则执行循环语句 ; 否则结束循环 .
(2)break
break 的功能是让循环提前结束 .
(3)continue
continue 的功能是跳过这次循环 , 立即进入下次循环 .
执行到 continue 语句的时候 , 就会立刻进入下次循环 ( 判定循环条件 ), 从而不会执行到下方的打印语句
(4)for语句
java
for(表达式①;布尔表达式②;表达式③){
表达式④;
}
表达式 1: 用于初始化循环变量初始值设置,在循环最开始时执行,且只执行一次
表达式 2: 循环条件,满则循环继续,否则循环结束
表达式 3: 循环变量更新方式
(5) do while 循环
java
do{
循环语句;
}while(循环条件);
先执行循环语句 , 再判定循环条件,循环条件成立则继续执行,否则循环结束。
【注意事项】
- do while 循环最后的分号不要忘记
- 一般 do while 很少用到, 更推荐使用 for 和 while.
四、Java进阶
4.1方法的使用
4.1.1方法的定义
java
// 方法定义
修饰符 返回值类型 方法名称([参数类型 形参 ...]){
方法体代码;
[return 返回值];
}
【 注意事项 】
-
在java当中,没有方法声明一说
-
返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成 void
-
方法名字:采用小驼峰命名
-
参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
-
方法体:方法内部要执行的语句
-
在java当中,方法必须写在类当中
-
在java当中,方法不能嵌套定义
-
定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
-
一个方法可以被多次调用.
4.1.2 实参和形参的关系
注意: 在 Java 中,实参的值永远都是拷贝到形参中,形参和实参本质是两个实体
java
public class TestMethod {
public static void main(String[] args) {
int a = 10;
int b = 20;
swap(a, b);
System.out.println("main: a = " + a + " b = " + b);
}
public static void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
System.out.println("swap: x = " + x + " y = " + y);
}
}
// 运行结果
swap: x = 20 y = 10
main: a = 10 b = 20
可以看到,在 swap 函数交换之后,形参 x 和 y 的值发生了改变,但是 main 方法中 a 和 b 还是交换之前 的值,即没有交换成功。
实参a 和 b 是 main 方法中的两个变量,其空间在 main 方法的栈 ( 一块特殊的内存空间 ) 中,而形参 x 和 y 是 swap 方法中 的两个变量,x 和 y 的空间在 swap 方法运行时的栈中,因此:实参 a 和 b 与 形参 x 和 y 是两个没有任何关联性的变量, 在 swap 方法调用时,只是将实参 a 和 b 中的值拷贝了一份传递给了形参 x 和 y ,因此对形参 x 和 y 操作不会对实参 a 和 b产生任何影响。
注意:对于 基础类型 来说 , 形参相当于实参的拷贝 . 即 传值调用
【 解决办法 】 : 传引用类型参数 ( 例如数组来解决这个问题)
4.1.3 方法重载
在 Java 中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了 。
java
public class TestMethod {
public static void main(String[] args) {
add(1, 2); // 调用add(int, int)
add(1.5, 2.5); // 调用add(double, double)
add(1.5, 2.5, 3.5); // 调用add(double, double, double)
}
public static int add(int x, int y) {
return x + y;
}
public static double add(double x, double y) {
return x + y;
}
public static double add(double x, double y, double z) {
return x + y + z;
}
}
注意:
- 方法名必须相同
- 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
- 与返回值类型是否相同无关
- 编译器在编译代码时,会对实参类型进行推演,根据推演的结果来确定调用哪个方法
4.1.4 方法签名
方法签名即:经过编译器编译修改过之后方法最终的名字。具体方式: 方法全路径名 + 参数列表 + 返回值类型,构成方法完整的名字。因此在同一个作用域中不能定义两个相同名称的标识符
4.1.5 递归
递归的必要条件:
- 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同
- 递归出口
4.2 数组的定义与使用
4.2.1 什么是数组
数组:可以看成是 相同类型元素的一个集合 。在内存中是一段连续的空间。
4.2.2 数组的创建
java
T[] 数组名 = new T[N];
- T:表示数组中存放元素的类型
- T[]:表示数组的类型
- N:表示数组的长度
4.2.3 数组的初始化
数组的初始化主要分为 动态初始化以及静态初始化 。
- 动态初始化:在创建数组时,直接指定数组中元素的个数
java
int[] array = new int[5];
- 静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
语法格式: T[] 数组名称 = {data1, data2, data3, ..., datan};
java
int[] array = new int[]{0,1,2,3,4,5,6,7,8,9};
【注意事项】
- 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
- 静态初始化时, {}中数据类型必须与[]前数据类型一致。
- 静态初始化可以简写,省去后面的new T[]。
java
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原
int[] array1 = {0,1,2,3,4,5,6,7,8,9};
- 静态和动态初始化也可以分为两步,但是省略格式不可以
java
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
// 注意省略格式不可以拆分, 否则编译失败
// int[] array3;
// array3 = {1, 2, 3};
- 如果没有对数组进行初始化,数组中元素有其默认值
- 如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值,比如:
|---------|--------|
| 类型 | 默认值 |
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0 |
| float | 0.0f |
| double | 0.0 |
| char | /u0000 |
| boolean | false |
- 如果数组中存储元素类型为引用类型,默认值为null
4.2.4 数组的使用
数组在内存中是一段连续的空间,空间的编号都是从 0 开始的,依次递增,该编号称为数组的下标,数组可以通过 下标访问其任意位置的元素 。
【 注意事项 】
- 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
- 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
- 数组是引用类型
注:JVM对所使用的内存按照功能的不同进行了划分:
- 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
- 虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含 有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一 些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
- 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
- 堆**(Heap)**: JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
- 方法区**(Method Area)**: 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域 。
4.2.5 基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。
java
public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
在上述代码中, a 、 b 、 arr ,都是函数内部的变量,因此其空间都在 main 方法对应的栈帧中分配。a、 b 是内置类型的变量,因此其空间中保存的就是给该变量初始化的值.array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该
地址,引用变量便可以去操作对象 。有点类似 C 语言中的指针,但是 Java 中引用要比指针的操作更简单。
注意:
- null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
- null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操 作. 一旦尝试读写, 就会抛出 NullPointerException.
注意 : Java 中并没有约定 null 和 0 号地址的内存有任何关联 .
4.3 类和对象
4.3.1 什么是面向对象
Java 是一门纯面向对象的语言 (Object Oriented Program ,继承 OOP) ,在面向对象的世界里,一切皆为对象。 面 向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情 。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。
注意:面向过程和面向对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
4.3.2 类的定义格式
在 java 中定义类时需要用到 class 关键字 ,具体语法如下
java
// 创建类
class ClassName{
field; // 字段(属性) 或者 成员变量
method; // 行为 或者 成员方法
}
class 为 定义类的关键字, ClassName 为类的名字, {} 中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类
具有哪些功能,称为类的成员方法。
4.3.3 类的实例化
定义了一个类,就相当于在计算机中定义了一种新的类型 ,与 int , double 类似,只不过 int 和 double 是 java 语言自
带的内置类型,而类是用户自定义了一个新的类型。
用类类型创建对象的过程,称为类的实例化 ,在 java 中采用 new 关键字,配合类名来实例化对象。
注意:
-
类只是 一个 模型 一样的东西,用来对一个实体进行描述,限定了类有哪些成员 .
-
类是一种自定义的类型 ,可以用来定义变量 .
-
一个类可以实例化出多个对象, 实例化出的对象占用实际的物理空间,存储类成员变量
-
做个比方: 类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图 ,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
4.3.4 this引用
this 引用指向当前对象 ( 成员方法运行时调用该成员方法的对象 ) ,在成员方法中所有成员变量的操作,都是通过该 引用去访问 。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
注意: -
this 的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
-
this 只能在 " 成员方法 " 中使用
-
在 " 成员方法 " 中, this 只能引用当前对象,不能再引用其他对象
-
this 是 " 成员方法 " 第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法
对象的引用传递给该成员方法, this 负责来接收。
4.3.5 构造方法
构造方法 ( 也称为构造器 ) 是一个特殊的成员方法, 名字必须与类名相同,在创建对象时,由编译器自动调用,并且
在整个对象的生命周期内只调用一次 。
注意: -
名字必须与类名相同
-
没有返回值类型,设置为 void 也不行
-
创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次 ( 相当于人的出生,每个人只能出生一次 )
-
构造方法可以重载 ( 用户根据自己的需求提供不同参数的构造方法 )
-
如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
-
构造方法中,可以通过 this 调用其他构造方法来简化代码
-
一旦用户定义,编译器则不再生成。
-
构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
4.3.6 默认初始化
局部变量在使用时必须要初始化,而成员变量可以不用。
4.3.7 封装
面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
(1)访问限定符
Java 中主要通过类和访问权限来实现封装: 类可以将数据以及封装数据的方法结合在一起 ,更符合人类对事物的认
知,而 访问权限用来控制方法或者字段能否直接在类外使用 。 Java 中提供了四种访问限定符:
1、访问控制权限控制符包括:
public 表示公开的,在任何位置都可以访问
protected 同包,子类
缺省 同包
private 表示私有的,只能在本类中访问
2、访问控制修饰符可以修饰类、变量、方法......
3、当某个数据只希望子类使用,使用protected进行修饰。
4、修饰符的范围:
private < 缺省 < protected < public