JAVA
一、绪论
主要参考
-
书籍《JAVA学习笔记》
-
《JAVA核心技术卷1》
1.问题汇总
Q:JAVA和C++有什么区别
A:
解答1:
(18条消息) c++与java的应用区别_蓝黑墨水的博客-CSDN博客_c++和java区别
解答2:
Java和C/C++到底有什么关系?它们有什么区别? (biancheng.net)
发展历程:C -> C++ -> Java 。Java 是由 C++发展而来的,保留了 C++ 的大部分内容,其编程方式类似于 C++。但 Java 的句法更清晰、规模更小、更易学。Sun 公司曾对多种程序设计语言进行分析研究,取其精华去其糟粕,最终推出了 Java。Java 从根本上解决了 C++ 的固有缺陷,形成了新一代面向对象的程序设计语言。
解答3:
C/C++和Java确实不太一样。C语言和C++,尤其是C++,语言密度更细,机制多,性能虽然高,但是就语言本身来说,包狱太重。所以也就称之为“造轮子”的语言,但是也正是因为他性能好,密度细,所以什么都能做。而Java本身是服务于互联网软件
开发(后端开发、客户端开发)的语言,他有一个明显的生态圈的概念,所以应用领域非常清晰。因为Java是纯应用层的,所以相对而言学习C++更加困难一些,对于程序员能力的要求要更高一些。C/C++的主场在系统底层
;(PS:现在JAVA也很少用来做浏览器客户端的交互了,取代applet(使用JAVA编写的应用程序)的是JS和Flash等脚本语言;当然不管是C++还是JAVA都别用来写前端界面!!!)
解答4:(基于作者学过的一些语言对比)
后端逻辑使用JAVA或者Node.js(JS本身是做前端逻辑的,但是node.js是可以写后端的,但应该没多少人会这么疯狂)做都可以,C/C++更适合做底层系统相关的东西,python因为简单轻量有很多现成完善的库可以直接调用(自己写小工具也很方便)
解答5:
(19条消息) c面向对象 java_c++面向对象和java面向对象的区别?_weixin_39584571的博客-CSDN博客
Q:为什么要学JAVA?
A:因为我想知道有关软件开发和桌面应用等知识点,我学JAVA并不是需要会用这门语言,而是需要知道这门语言和我学习中会接触到的大量知识盲区的关系,所以学习过程中不要特别在于语法条件之类的,着重看重JAVA特性和用途;
Q:为什么JAVA的项目名和什么类型这么麻烦?一旦不对应似乎整个程序都会崩溃????
A:因为JAVA是一门非常严谨的语言,任何规则都必须遵守(有句话很有意思“C++更加灵活,JAVA更加严谨”),正是因为其语法的严谨特性,所以还有一句话是“C++动不动就崩溃,JAVA想写崩溃都难”;
2.JAVA概述
按应用范围,Java 可分为 3 个体系,即 Java SE、Java EE 和 Java ME
- JAVA SE:Java SE(Java Platform Standard Edition,
Java 平台标准版
)以前称为 J2SE,它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java EE 提供基础,如 Java 语言基础、JDBC 操作、I/O 操作、网络通信以及多线程等技术,适合开发桌面级应用如QQ、微信
; - JAVA EE:Java EE(Java Platform Enterprise Edition,
Java 平台企业版
)以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端 Java 应用程序。Java EE 是在 Java SE 基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(Service Oriented Architecture,SOA)和 Web 2.0 应用程序,适合Web应用程序开发
; - Java ME(Java Platform Micro Edition,
Java 平台微型版
)以前称为 J2ME,也叫 K-JAVA。 Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、丰富的内置网络协议以及对可以动态下载的联网和离线应用程序。基于 Java ME 规范的应用程序 只需编写一次就可以用于许多设备,而且可以利用每个设备的本机功能,适合开发移动端的应用程序
;
JAVA主要有以下特性:
- 简单性:JAVA是C++的“纯净版本”
- 面向对象
- 网络技能:JAVA可以让socket通信等变得非常简单
- 健壮性:JAVA采用的指针模型可以消除重写内存和损坏数据的可能性
- 安全性:JAVA的网络环境绝对安全
- 体系结构中立:JAVA编译器生成与操作系统无关的字节码实现
- 可移植性:JAVA的基本数据类型大小是固定的,不依赖具体实现
- 解释型
- 高性能
- 多线程:JAVA实现多线程非常简单
- 动态性:C#与JAVA在这方面类似,可以自由在库中增加方法和实例变量
3.核心机制
3.1 JAVA虚拟机
JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。对于不同的平台,有不同的虚拟机。只有某平台提供了对应的java虚拟机,java程序才可在此平台运行。
Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”;
3.2 垃圾回收机制
C/C++中需要程序员手动释放无用内存;
Java 语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空间的分配情况。并在JVM空闲时,检查并释放那些可被释放的存储空间。垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预;
尽管JAVA拥有自动垃圾回收机制,但是有时候我们写的程序可能存在问题仍将导致自动回收机制无法识别垃圾进而造成内存泄漏(某些对象申请内存没有被释放,一直占用)和内存溢出(程序所需的内存大于系统所能提供的最大内存);
4.JAVA环境
JDK(Java Development Kit Java开发工具包)JDK是提供给Java开发人员使用的,其中包含了java的开发工具
,也包括了JRE
。所以安装了JDK,就不用在单独安装JRE了。
-
开发工具包括编译工具(javac.exe)打包工具(jar.exe)等(
开发工具用于编写代码
); -
JRE(Java Runtime Environment Java 运行环境)包括
Java虚拟机(JVM Java Virtual Machine)
和Java 程序所需的核心类库
等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可(JRE用于运行Java程序
);
(非常幸运的是在之前安装爬虫appinum的时候安装Andriod SDK时顺便就安装了JDK环境,所以我们省略了安装JDK的步骤,教程参考(18条消息) JDK下载与安装教程_墨笙弘一的博客-CSDN博客_jdk安装教程)
PS:按照网上的JDK安装教程并没有配置安装JRM所以可能目录环境和B站老师讲的不太一样,先将就用着,期间有什么问题做记录写下来记录即可
-
报错解决:(18条消息) 关于解决 错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException 的方法_是我,Zack的博客-CSDN博客
-
Eclipse汉化:(19条消息) eclipse汉化教程(官方汉化包,傻瓜式操作,附带中英文快捷切换方式)_Zeromes的博客-CSDN博客_eclipse汉化
5.JAVA标准格式
我们这里给出一个基本的JAVA程序的模板,并简单解释下相关的参数,在之后还会详细介绍
public class HelloJava {
public static void main(String[] args){
System.out.println(""!我的第一个 Java程序!");
}
}
- JAVA程序中可以有多个类,但只能有一个public类,且主文件名必须与这个公开类的的名称相同;
- main()是JAVA程序的入口,程序的执行从入口开始,main()一定是public成员这样它才可以在执行环境中被调用,同时main()必须是个static成员这样它才能在不产生对象的情况下被执行;
- 括号中的String[] args可以在执行程序时取得使用者指定的命令行参数,尽管现在用不着但仍然要书写;
- 每一条语句的结束要用分号;
- println()输出的字符串后自动换行,print()输出字符串后程序并不会自动换行;
JAVA中main()方法是应用程序的入口方法,main()方法与其他方法存在很大差别:
- 访问控制权限是公有的(public)。
- main() 方法是静态的。如果要在 main() 方法中调用本类中的其他方法,则该方法也必须是静态的,否则需要先创建本类的实例对象,然后再通过对象调用成员方法。
- main() 方法没有返回值,只能使用 void。
- main() 方法具有一个字符串数组参数,用来接收执行 Java 程序的命令行参数。命令行参数作为字符串,按照顺序依次对应字符串数组中的元素。
- 字符串中数组的名字(代码中的 args)可以任意设置,但是根据习惯,这个字符串数组的名字一般和 Java 规范范例中 main() 参数名保持一致,命名为 args,而方法中的其他内容都是固定不变的。
- main() 方法定义必须是“public static void main(String[] 字符串数组参数名)”,即main()方法的格式必须固定不变。
- 一个类只能有一个 main() 方法,这是一个常用于对类进行单元测试(对软件中的最小可测试单元进行检查和验证)的技巧。
二、JAVA基础
1.JAVA注释
- 单行注释
//注释文字
- 多行注释
/*注释文字*/
- 文档注释(JAVA特有)
/**
@author 指定 java 程序的作者 **
@version 指定源文件的版本 **
*/
文档注释的内容可以被JDK提供的工具javadoc解析(包、公有类和接口、公有的和受保护的方法、公有的和受保护的域)生成一套以网页文件形式体现该程序的说明文档
javadoc -d 文件夹名 -author -version 文件名
文档注释一般用在类、方法和变量上面,用来描述其作用。注释后,鼠标放在类和变量上面会自动显示出我们注释的内容
关于JAVA文档注释的更多消息可以参考Javadoc(文档注释)详解 (biancheng.net)也可以参考《JAVA技术卷1》P137、Java注释:类、方法和字段注释 (biancheng.net)
2.JAVA运行流程
Java程序的运行过程(执行流程)分析 (biancheng.net)
JAVA中的所有东西都会属于某个类,我们写的源文件经过编译后会得到类文件(.class,存储的字节码),被VM执行的实际是类 – 执行程序就表示命令JVM加载HelloJava这个类并开始执行它的main(),直到main的所有程序代码结束为止;
3.JAVA常量
常量是指在程序的整个运行过程中值保持不变的量。
注意:常量和常量值是不同的概念,常量值是常量的具体和直观的表现形式,常量是形式化的表现。通常在程序中既可以直接使用常量值,也可以使用常量
3.1 常量值
3.1 常量值
常量值又称字面常量
,通过数据直接表示,可分为:
- 整型常量值:整型(int)常量默认在内存中占 32 位,是具有整数类型的值,当运算过程中所需值超过 32 位长度时,可以把它表示为长整型(long)数值。长整型类型则要在数字后面加 L 或 1, 如 697L,表示一个长整型数,它在内存中占 64 位
- 实型常量值:Java 实型常量默认在内存中占 64 位,是具有双精度型(double)的值。如果考虑到需要节省运行时的系统资源,而运算时的数据值取值范围并不大且运算精度要求不太高的情况,可以把它表示为单精度型(float)的数值,单精度型数值一般要在该常数后面加 F 或 f,如 69.7f,表示一个 float 型实数,它在内存中占 32 位
- 布尔型常量:false true
- 字符型/字符串常量:Java 字符串常量值中的单引号和双引号不可混用,单引号表示字符型常量,双引号表示字符串常量;
3.2 常量
JAVA中的常量名一般使用大写
- 声明常量
final dataType variableName = value
final 是定义常量的关键字(实际上JAVA也有const关键字但没使用),dataType 指明常量的数据类型,variableName 是常量的名称,value 是初始值
常量分为:
- 静态全局常量
public static final double PI = 3.14;
在 final 之前 public static 修饰,public static 修饰的常量作用域是全局的,不需要创建对象就可以访问它
- 成员常量
- 局部常量
3.3 final修饰符
final 应用于类、方法和变量时意义是不同的,但本质是一样的,都表示不可改变;
使用 final 关键字声明类、变量和方法需要注意以下几点:
- final 用在变量的前面表示变量的值不可以改变(不是不可以赋值,而是不可以改变!!!),此时该变量可以被称为
常量
;- 当使用 final 修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变;
- 对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变(使用 final 修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容);
- final 用在方法的前面表示方法
不可以被重写
(子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写,又称为方法覆盖,重写不等于重载
); - final 用在类的前面表示该类不能有子类,即该类
不可以被继承
;
4.JAVA变量
JAVA中的每一个变量都属于一种类型,声明变量时一定要声明变量所属类型;
4.1 变量
- 声明变量
DataType identifier;//每个声明以分号结束(因为声明是一条完整的语句)
DataType identifier=value;//声明变量的同时初始化变量
- DataType:变量类型,如 int、string、 char 和 double 等;
- identifier:标识符,也叫变量名称;
- value:声明变量时的值;
C/C++中int i=10是定义一个变量,extern int i是声明一个变量,但是JAVA中并不区分变量的声明和定义;
4.2 变量作用域
变量根据作用域的不同可以分为:
- 成员变量:定义在方法体和语句块之外,不属于任何一个方法,作用域是整个类(我们在后面也叫它域)
- 全局变量(实例变量)
- 静态变量(类变量)
- 局部变量:在方法或者方法代码块中定义的变量,其作用域是其所在的代码块
- 方法参数变量(形参):在整个方法内有效
- 方法局部变量(方法内定义的变量):从定义这个变量开始到方法结束这一段时间内有效
- 代码块局部变量(代码块内定义的变量):从定义这个变量开始到代码块结束这一段时间内有效(比如for循环中的循环计数器i)
成员变量和局部变量的主要不同点就在于,必须明确的初始化局部变量才能使用,而成员变量假如没有初始化则自动初始化为默认值(0、false、null)
JAVA面向对象的概念中并不存在全局变量的概念;
5.JAVA数据类型
数据类型是指编译器存储在变量中的数值应当具有适当的数据类型,JAVA是一种强数据类型的语言,必须为每一个变量声明一种类型,JAVA数据类型分为基本数据类型和引用数据类型
5.1 基本数据类型
- JAVA没有任何无符号类型;
- 浮点类型用于表示有小数部分的数值,千万不要将浮点类型用在金融计算中(这将引发骚乱);
- ‘A’是编码为65对应的字符常量,”A”是包含A的字符串,建议不要在程序中使用char类型(JAVA的Unicode字符集比较特殊);
- JAVA中的布尔型和整型不能相互转换,C++中数值或指针可以代替布尔值;
5.2 引用数据类型
引用数据类型建立在基本数据类型的基础上,包括数组、类和接口。引用数据类型是由用户自定义,用来限制其他数据的类型(这一点其实非常类似C++的复合数据类型)。另外,Java 语言中不支持C++中的指针类型、结构类型、联合类型(类类型、枚举类型)。
6.JAVA流程控制
程序设计主要有三种流程结构:顺序结构、选择结构和循环结构,系统默认是自上而下以顺序结构执行;
6.1 JAVA语句
Java 中,语句是最小的组成单位,每个语句必须使用分号作为结束符
按照语句的组成部分,可将JAVA中的语句分为如下三类
6.1.1 空语句
;
空语句就是一个分号,在程序中什么也不做,主要用于做空循环体;
6.1.2 表达式语句
pi = 3.1415926;
output(pi); // 将pi的值传递到output()函数中作为参数
sum = (a+b)/2;
printf("%f",sum); // 将sum的值传递到printf()函数输出
temp = x*y*z-y+(20-x); // 将表达式的值保存到temp变量中
一般表达式语句应该能完成一个操作,如修改变量的值或者作为函数参数等
6.1.3 复合语句
{
statement-list // 语句列表
}
复合语句(代码块/语句块)是很多语句的组合,将一个代码块/语句块看作一个语句;
6.2 选择结构
选择结构(也叫分支结构)解决了顺序结构不能判断的缺点,可以根据一个条件判断执行哪些语句块;
JAVA支持两种选择语句(使用选择语句实现选择结构):if 语句和 switch 语句,这些语句允许只有在程序运行时才能知道其状态的情况下,控制程序的执行过程
- if 语句使用布尔表达式或布尔值作为分支条件来进行分支控制;
- switch 语句则用于对多个整型值进行匹配,从而实现分支控制;
6.3 循环结构
循环语句能够使程序代码重复执行,适用于需要重复一段代码直到满足特定条件为止的情况;
JAVA中采用的循环语句与C语言中的循环语句相似,主要有 while、do-while 和 for、for-each 循环语句,for-each 循环是 for 循环的变形,它是专门为集合遍历而设计的;
6.3.1 for-each语句
- 格式
for(类型 变量名:集合) {
语句块;
}
- 举例
// 声明并初始化数组
int[] numbers = { 43, 32, 53, 54, 75, 7, 10 };
System.out.println("----for----");
// for语句
for (int i = 0; i < numbers.length; i++) {
System.out.println("Count is:" + numbers[i]);
}
// 声明并初始化int数组
int[] numbers = { 43, 32, 53, 54, 75, 7, 10 };
System.out.println("----for each----");
// for-each语句
for (int item : numbers) {
System.out.println("Count is:" + item);
}
foreach 循环和普通循环不同的是,它无须循环条件,无须循环迭代语句,这些部分都由系统来完成,foreach 循环自动迭代数组的每个元素,当每个元素都被迭代一次后,foreach 循环自动结束;
6.3.2 退出循环
可以使用return结束循环(本质上是终止函数的执行或退出类的方法并将控制权返回给方法的拥有者),JAVA中提供break专门用于强制退出循环;
我们下面介绍break的特殊用途 —— 退出深层循环,要知道在C/C++中要退出多层循环是没有简单方法的,JAVA提供了一种带标签的break语句,实现goto的功能,可以明确指定从何处重新开始执行
- 格式
break label;
label 是标识代码块的标签。当执行这种形式的 break 语句时,控制权被传递出指定的代码块。用标签(label)可以指定一个代码块
,标签可以是任何合法有效的 Java 标识符,后跟一个冒号。加上标签的代码块可以作为 break 语句的对象,使程序在加标签的块的结尾继续执行;
- 被加标签的代码块必须包围 break 语句,但是它不需要直接包围 break 的块。也就是说,可以使用一个加标签的 break 语句来退出一系列的嵌套块,但是不能使用 break 语句将控制权传递到不包含 break 语句的代码块;
- 标签语句必须和循环匹配使用,使用时书写在对应的循环语句的上面,标签语句以冒号结束。如果需要中断标签语句对应的循环,可以采用 break 后面跟标签名的方式;
- 举例
public class GotoDemo {
public static void main(String[] args) {
label: for (int i = 0; i < 10; i++) {
for (int j = 0; j < 8; j++) {
System.out.println(j);
if (j % 2 != 0) {
break label;
}
}
}
}
}
7.JAVA数组
数组(array)是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等;
数组是一种引用数据类型
7.1 一维数组
- 声明一维数组语法格式
//声明一个引用该数组的变量,并指明整个变量可以引用的数组类型
type[] arrayName; // 数据类型[] 数组名,这种方式是推荐的,type[]是一种新的引用类型;
//或者
type arrayName[]; // 数据类型 数组名[],尽管C++是这种声明方式,但之后的语言都逐渐摒弃了这种方法;
声明数组时不要规定数组的长度,会导致错误
- 实例
int[] score; // 存储学生的成绩,类型为整型
double[] price; // 存储商品的价格,类型为浮点型
String[] name; // 存储商品名称,类型为字符串型
声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储, Java 中可以使用 new 关键字来给数组分配空间;
- 分配空间语法格式
arrayName = new type[size]; // 数组名 = new 数据类型[数组长度];
//当然也可以将分配空间和声明数组的时机放在一起
- 举例
score = new int[10];
price = new double[30];
name = new String[20];
注意:一旦声明了数组的大小,就不能再修改。这里的数组长度也是必需的,不能少;
8.JAVA集合类
(JAVA的集合类真的是一个非常非常天坑的知识点,光看知识点真的没法理解)
数组长度不可变、数组无法保存具有映射关系的数据,为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),JAVA提供了集合类;
数组元素可以是基本类型的值也可以是对象,而集合类中只能保存对象;
8.1 List集合
-
List 是一个
有序、可重复的集合
,集合中每个元素都有其对应的顺序索引; -
List 集合
允许使用重复元素
,可以通过索引来访问指定位置的集合元素; -
List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推;
8.1.1 ArrayList类
ArrayList 类实现了可变数组的大小
,存储在内的数据称为元素;
它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好;
使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问
,不过,向 ArrayList 中插入与删除元素的速度相对较慢
;
8.1.2 LinkedList类
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素;
需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢(这里的随机访问是指检索集合中特定索引位置的元素);
8.2 set集合
-
Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合;
-
Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素;
8.2.1 HashSet类
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法
来存储集合中的元素。因此具有很好的存取和查找性能。
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化;
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步;
- 集合元素值可以是 null;
8.3 Map集合
Map 是一种键-值对(key-value)集合
,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据;
-
Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据;
-
Map 的 key 不允许重复,value 可以重复;
-
Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value;
Map 接口主要有两个实现类:HashMap 类
和 TreeMap 类
。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序;
三、JAVA进阶
1.JAVA类和对象
首先,C++中没有这种将基本数据类型设计为包装类的说法,为什么?因为C++的特性并不仅限于面向对象,所以无论是基本数据类型还是复合数据类型(数组、类类型、结构)有各自的特点和作用;
但是,JAVA不一样,它规定了“一切皆为对象”,它是完全面向对象的语言,也就是说无论是基本数据类型还是其他的类型,所有的操作都要求用对象的形式进行描述(比如.运算符访问属性),然而基本数据类型本身不符合这个规定,所以就出现了包装类这个说法(将在后面简单介绍这一性质,这个性质算不上JAVA的重点);
- 类:构造对象的模板或蓝图;
- 对象:通过类构造(也称为创建)的实例;
- 实例域:类中的数据称为实例域,每个特定的实例对象都有一组特定的实例域值,这些值的集合就是对象的
状态
; - 方法:类中操纵数据的过程;
- 对实例域做出修改的方法称为更改器方法;
- 仅访问实例域而不进行修改的方法称为访问器方法;
- 封装:绝对不能让类中的方法直接地访问其他类的实例域(实例域设置为Private),程序只能通过对象的方法与对象数据进行交互进而改变对象的状态,
JAVA封装的基本单位是类
;
类之间的关系有:
- 依赖(uses-a):一个类的对象操作另一个类的对象,应当减少依赖类的存在(让类之间的耦合程度最小);
- 聚合(has-a):一个类的对象包含另一个类的对象;
- 继承(is-a):子类继承父类的成员(方法和域)(C++支持多继承,但是多继承往往会带来很多冲突,所以JAVA只支持单继承);
1.1 对象和对象变量
JAVA中使用构造器来构造一个对象,我们假设使用JAVA中的Date类
- 构造一个Date类的对象
new Date();//构造器的名字和类名相同,在构造器前面加上new操作符
- 构造一个Date类的对象变量
Date deadline;
一个对象变量并没有实际包含一个对象(所以在没有初始化对象变量的时候不能对对象变量使用成员运算符等),对象变量仅仅引用一个对象;
可以将JAVA的对象变量看作是C++的对象指针
Date birthday;//JAVA
Date * birthday;//C++
除了显式创建对象以外还可以隐式创建对象,无论釆用哪种方式创建对象,Java 虚拟机在创建一个对象时都包含以下步骤:
- 给对象分配内存。
- 将对象的实例变量自动初始化为其变量类型的默认值。
- 初始化对象,给实例变量赋予正确的初始值。
1.2 用户自定义类
要创建一个完整的程序,应该将若干类组合在一起,其中只有一个类有main方法;
-
假设某个程序包含两个类EmployeeTest和Employee,其中EmployeeTest类带有public修饰符且包含了main方法,则该程序的源文件名应该是EmployeeTest.java,当编译器编译这段代码的时候将在目录下创建两个文件,EmployeeTest.class和Employee.class,要运行这段程序需要将程序中包含main方法的类名提供给字节码解释器javac;
-
假如习惯将每一个类存在一个单独的源文件中,如EmployeeTest在EmployeeTest.java,Employee在Employee.java,则有两种编译程序的方法使用通用符占位或直接只编译公有类文件;
java Employee*.java
java EmployeeTest.java
1.2.1 定义类
在 Java 中定义一个类,需要使用 class 关键字、一个自定义的类名和一对表示程序体的大括号
[public][abstract|final]class<class_name>[extends<class_name>][implements<interface_name>] {
...
}
中括号“[]”中的部分表示可以省略,竖线“|”表示“或关系”,“|”两边的关键字不能同时出现
public
:表示“共有”的意思。如果使用 public 修饰,则可以被其他类和程序访问。每个 Java 程序的主类都必须是 public 类,作为公共工具供其他类和程序使用的类应定义为 public 类。abstract
:如果类被 abstract 修饰,则该类为抽象类,抽象类不能被实例化,但抽象类中可以有抽象方法(使用 abstract 修饰的方法)和具体方法(没有使用 abstract 修饰的方法)。继承该抽象类的所有子类都必须实现该抽象类中的所有抽象方法(除非子类也是抽象类)。final
:如果类被 final 修饰,则不允许被继承。class
:声明类的关键字。class_name
:类的名称。extends
:表示继承其他类。implements
:表示实现某些接口。
1.2.2 构造器
这里所说的构造器本质上就是C++中的构造函数
public Employee(String n, double s, int year, int month, int day)
{
name=n;
salary=s;
GregorianCalendar calendar = new Gregoriancalendar(year, month -1, day);
hireDay =calendar. getTime();
}
构造器与类同名,构造器的作用是将实例域初始化为所希望的状态;
构造器与其他方法不同的地方在于构造器总是随着new运算符的执行被调用,不能对一个已经存在的对象调用其构造器来重置实例域;
JAVA因为有自动回收机制所以不支持析构器(为什么这里又说有析构方法呢?Java析构方法 (biancheng.net)解答参考java中怎么没有析构函数?_百度知道 (baidu.com));
仅当类没有提供任何构造器的时候系统才会自动提供一个默认构造器,此时构造对象可以不提供任何构造参数,否则要想使用这种形式只能自己额外提供一个重载构造函数;
构造方法不能被 static、final、synchronized、abstract 和 native(类似于 abstract)修饰
1.2.3 成员变量
声明成员变量格式
public class Test {
[public|protected|private][static][final]<type><variable_name>
}
- public、protected、private:用于表示成员变量的访问权限。
- static:表示该成员变量为类变量,也称为静态变量。
- final:表示将该成员变量声明为常量,其值无法更改。
- type:表示变量的类型。
- variable_name:表示变量名称。
举例
public class Student {
public String name; // 姓名
final int sex = 0; // 性别:0表示女孩,1表示男孩
private int age; // 年龄
}
1.2.4 成员方法
一个完整的方法通常包括方法名称、方法主体、方法参数和方法返回值类型
声明成员方法的格式
public class Test {
[public|private|protected][static]<void|return_type><method_name>([paramList]) {
// 方法体
}
}
1.3 访问修饰符
JAVA提供了多个作用域修饰符,这些修饰符有类修饰符、变量修饰符和方法修饰符,详细信息可参考Java访问控制修饰符详解(public、 private、protected 和 friendly) (biancheng.net);
类的访问控制符只能是null或者public,方法和属性的访问控制符有 4 个,分别是 public、 private、protected 和 friendly,其中 friendly 是一种没有定义专门的访问控制符的默认情况;
-
在实现一个类的时候,公开数据是十分危险的,所以应该将所有的数据域都设置为私有private;
-
对于某些辅助方法,不应当充当共有接口的一部分(因为它们往往需要一个特定的调用次序),这样的方法最好设置为private;
-
实际应用中最好少用protected的域,protected的方法更加常见,因为JAVA的protected对所有子类和同一个包中的所有类都可见,所以安全性相较于C++会差一点;
1.4 静态域和静态方法
静态成员不依赖于类的特定实例,被类的所有实例共享,就是说 static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,JAVA 虚拟机就可以根据类名找到它们
调用静态成员的语法形式如下:
类名.静态成员
- static 修饰的成员变量和方法,从属于类。
- 普通变量和方法从属于对象。
- 静态方法不能调用非静态成员,编译会报错。
1.4.1 静态域
如果将类的实例域定义为static(每个类中只会有一个这样的域),那么这个类的每一个对象将共享这个静态域,即使没有任何一个类对象,静态域也存在(因为它属于类而不属于任何独立的对象);
实际上JAVA中的静态域一般都被称为类域;
1.4.2 静态常量
静态变量使用较少而静态常量(不属于静态域的字符)使用较多;
可以直接通过类名.常量名
的形式获取这个常量;
1.4.3 静态方法
使用静态方法的情况:
- 该方法不需要访问对象状态,其所需参数都是通过显式参数提供;
- 该方法只需要访问类的静态域;
静态方法是一种不能向对象实施操作的方法,可以认为静态方法是没有this隐式参数的方法;
因为静态方法不能操作对象故静态方法也就不能访问实例域,但是静态方法可以访问自身类中的静态域;
同样可以通过类名来调用这个方法(当然使用对象名来调用静态方法也没有问题,但本质上静态方法的结果和对象没有任何关系,这容易造成混淆);
1.5 包
JAVA使用包将类组织起来,标准的JAVA类库分布在多个包中;
标准的JAVA包都有一个层次结构,JAVA包名是绝对唯一的;
使用包的其中一个原因是为了确保类名的唯一性(这个包实际上非常类似于C++的名称空间),假如两个程序员都建立了Employee类,只要将这两个类放在不同的包中就不会产生冲突;
1.5.1 类的导入(类导出包)
一个类可以访问所属包中的所有类以及其他包中的公有类public;
访问其他包中的公有类主要有两种方法:
- 在每个类名之前添加完整的包名
java.util.Date today = new java.util.Date()
- 使用import语句
import java.util.*;//导入java.util包中的所有类
import java.util.Date;//导入包中一个特定的类
可以使用import语句导入一个特定的类或者整个包,import语句需要位于源文件的顶部、package语句后面
1.5.2 静态导入
import语句不仅可以导入类,还可以导入静态方法和静态域,静态导入使用相对较少,主要用于以下情况:
- 算术函数:对Math类使用静态导入则可以更加自然的使用算术函数;
- 笨重的常量:如果需要使用大量带有冗长名字的常量则最好使用静态导入;
1.5.3 包导入类
若需要将一个类放入包中,则必须将这个包的名字放在源文件的开头定义类的代码之前
package java.util
public class Employee
{
...
...
}
如果没有在源文件中防止package语句则这个源文件的类就被放置在一个默认的包中(默认包是一个没有名字的包);
我们需要将包中的文件存放在与完整包名匹配的子目录中,com.horstman.corejava包中的所有源文件都应该被放在对应子目录下,这样编译器也会相应的将类文件放在同样的目录结构中;
1.5.4 包作用域
前面介绍的访问修饰符public标记的部分可以被任意的类使用,private标记的部分只能被定义它们的类使用,如果没有指定public和private则这个部分可以被同一个包中的所有方法访问;
1.6 方法参数
程序设计语言中将参数传递给方法/函数有两种方式:
- 值调用:表示方法接收的是调用者提供的值的拷贝;
- 引用调用:表示方法接收的是调用者提供的变量地址;
JAVA程序只能采用值调用(无论参数是值还是对象),因此JAVA的方法不能修改传递给它的任何一个参数;
1.7 this关键字
this 关键字可用于任何实例方法内指向当前对象,也可指向对其调用当前方法的对象,或者在需要当前类型对象引用时使用
1.7.1 this.属性名
大部分时候,普通方法访问其他方法、成员变量时无须使用 this 前缀,但如果方法里有个局部变量和成员变量同名,但程序又需要在该方法里访问这个被覆盖的成员变量,则必须使用 this 前缀;
public class Teacher {
private String name; // 教师名称
private double salary; // 工资
private int age; // 年龄
// 创建构造方法,为上面的3个属性赋初始值
public Teacher(String name,double salary,int age) {
this.name = name; // 设置教师名称
this.salary = salary; // 设置教师工资
this.age = age; // 设置教师年龄
}
}
当一个类的属性(成员变量)名与访问该属性的方法参数名相同时,则需要使用 this 关键字来访问类中的属性,以区分类的属性和方法中的参数
1.7.2 this.方法名
this 关键字最大的作用就是让类中一个方法,访问该类里的另一个方法或实例变量(关于实例变量上面已经介绍过this.属性名);
public class Dog {
// 定义一个jump()方法
public void jump() {
System.out.println("正在执行jump方法");
}
// 定义一个run()方法,run()方法需要借助jump()方法
public void run() {
// 使用this引用调用run()方法的对象
this.jump();
System.out.println("正在执行run方法");
}
}
对于 static 修饰的方法而言,可以使用类来直接调用该方法,如果在 static 修饰的方法中使用 this 关键字,则这个关键字就无法指向合适的对象。所以,static 修饰的方法中不能使用 this 引用。并且 Java 语法规定,静态成员不能直接访问非静态成员;
2.JAVA继承
面向对象除了类和对象以外,另一个基本概念是继承,利用继承可以基于一个已经存在的类来构造一个新类;
继承已经存在的类就是复用这些类的方法和域
,再次基础上可以添加一些新的方法和域;
2.1 类的继承
先展示一下由继承Employee类来定义Manager类的格式,extends关键字表示继承
class Manager extends Employee
{
//添加方法和域
}
- JAVA中的所有继承都是公有继承public,没有C++中的私有继承private和保护继承protected(但是是存在private和protected访问修饰符的);
- 已经存在的类称为
超类、基类或父类
,如上面的Employee;派生出的新类称为子类、派生类或孩子类
; - 在通过
拓展
超类定义子类的时候,只需要指出子类和超类的不同之处即可,因此设计类的时候应该将通用的方法放在超类中,将具有特殊用途的方法放在子类中; - 子类可以通过重新定义超类中的方法来
覆盖
超类中原有方法,However,子类绝对不能删除继承的任何域或方法; - 当子类需要调用超类的同名方法时可以使用super关键字
super.getSalary();
方法重载:同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同;
方法重写:也称为覆盖,子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能;
2.2 继承层次
由一个超类派生出的所有类的集合被称为继承层次
,在继承层次中从某个特定的类到其祖先的路径被称为继承链
;
2.3 阻止继承
当需要阻止利用某个类来自定义子类,可以在定义该类的时候加上final标识符表明该类是final类
final class Executive extends Manager{
//阻止人们自定义Executive类的子类
}
类中的方法也可以被final修饰,这样子类就不能覆盖这个方法(final类的所有方法自动称为final方法)
class Employee
{
public final String getName()
{
return name;
}
}
当然域也可以被声明为final,final域在构造对象之后就不允许改变它们的值了;
将类或方法声明为final的原因是为了确保它们不会在子类中改变语义
2.4 抽象类
从继承结构来看,位于上层的类更加通用、抽象,而位于上层的类具备的一些通用方法,在本层可能暂时无法实现(因为可能需要的某些参数暂时还没有),此时可以使用abstract关键字,这样就完全无需在上层实现该方法(C++中利用纯虚函数实现抽象类);
包含一个或多个抽象方法的类本身必须被声明为抽象的,除了抽象方法外,抽象类还可以包含具体的数据和方法;
abstract class Person
{
public abstract String getDescription();
public String getName()
{
return name;
}
private String name;
}
抽象方法充当着占位的角色,它们的具体实现在子类中,扩展抽象类可以有两种选择:
- 一种是在子类中定义部分抽象方法或不定义抽象方法,这样就必须将子类也标记为抽象类;
- 另一种是定义全部的抽象方法,这样一来,子类就不是抽象的了;
实际上抽象类就算不包含任何的抽象方法也可以被标记为抽象类,抽象类不能被实例化
也就是不能创建这个类的对象;
2.5 Object超类
尽管Object类是所有JAVA类的祖先(C++中没有根类),但是在类声明时没必要这样写
class Employee extends Object
在实际使用中,如果没有明确指出超类,则Object类默认成为该类的超类;
- 可以使用Object类的对象变量引用任何类型的对象
Object obj = new Employee();
- JAVA中除了基本数据类型不是对象(数值、字符、布尔值),其他类型都拓展于Object类(包括数组类型)
Object类中有一些重要的方法,比如equals方法检测两个对象是否相等、toString方法返回表示对象值的字符串,这些Object提供的服务需要掌握:
3.包装类
参考自:Java内置的包装类 (biancheng.net)
有时,需要将int这样的基本类型转换为对象,所有的基本类型都有一个与之对应的类。例如,Integer类对应基本类型int。通常,这些类称为包装器(wrapper)、包装类、包装器类
。这些对象包装器类拥有很鲜明的名字:Integer,Long,Float,Double,Short,Byte,Character,Void和Boolean(前6个类派生于公共的超类Number)。
- 对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值;
- 对象包装器类还是final,因此不能定义它们的子类;
3.1 装箱和拆箱
- 基本数据类型转换为包装类的过程称为装箱(自动打包);
Integer n = 3;
#编译器自动插入一条拆对象包指令
n++;#接着进行int自增计算
#最后将结果打包进对象包Integer内
- 包装类变为基本数据类型的过程称为拆箱(自动拆包);
#将一个Integer对象赋值给一个int值时,会自动拆包
int n = list.get(i);
#上述语句被编译器自动翻译为
int n = list.get(i).intValue();
打包和拆包是编译器认可的,而不是虚拟机;编译器在进行打包和拆包的时候会插入必要的方法调用,接着生成类的字节码
要实现手动装箱和拆箱需要借助包装类的构造方法装箱,通过包装类的intValue方法拆箱
int m = 500;
Integer obj = new Integer(m); // 手动装箱
int n = obj.intValue(); // 手动拆箱
3.2 Integer类
Integer 类在对象中包装了一个基本类型 int 的值,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法;
Integer 类中的构造方法有以下两个(手动装箱):
- Integer(int value):构造一个新分配的 Integer 对象,它表示指定的 int 值;
- Integer(String s):构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值;
Integer integer1 = new Integer(100); // 以 int 型变量作为参数创建 Integer 对象
Integer integer2 = new Integer("100"); // 以 String 型变量作为参数创建 Integer 对象
可以借助Integer中的方法将字符串(应当只包含数值类型的字符)和int类型的数值相互转换
String str = "456";
int num = Integer.parseInt(str); // 将字符串转换为int类型的数值
int i = 789;
String s = Integer.toString(i); // 将int类型的数值转换为字符串
Integer类包含以下四个常量
int max_value = Integer.MAX_VALUE; // 获取 int 类型可取的最大值
int min_value = Integer.MIN_VALUE; // 获取 int 类型可取的最小值
int size = Integer.SIZE; // 获取 int 类型的二进制位
Class c = Integer.TYPE; // 获取基本类型 int 的 Class 实例
3.3 Float类
Float 类在对象中包装了一个基本类型 float 的值,该类提供了多个方法,能在 float 类型与 String 类型之间互相转换,同时还提供了处理 float 类型时比较常用的常量和方法;
Float 类中的构造方法有以下 3 个。
- Float(double value):构造一个新分配的 Float 对象,它表示转换为 float 类型的参数;
- Float(float value):构造一个新分配的 Float 对象,它表示基本的 float 参数;
- Float(String s):构造一个新分配的 Float 对象,它表示 String 参数所指示的 float 值;
Float float1 = new Float(3.14145); // 以 double 类型的变量作为参数创建 Float 对象
Float float2 = new Float(6.5); // 以 float 类型的变量作为参数创建 Float 对象
Float float3 = new Float("3.1415"); // 以 String 类型的变量作为参数创建 Float 对象
可以借助Integer中的方法将字符串(应当只包含数值类型的字符)和float类型的数值相互转换
String str = "456.7";
float num = Float.parseFloat(str); // 将字符串转换为 float 类型的数值
float f = 123.4f;
String s = Float.toString(f); // 将 float 类型的数值转换为字符串
Float类包含很多常量,具体可以查阅资料
float max_value = Float.MAX_VALUE; // 获取 float 类型可取的最大值
float min_value = Float.MIN_VALUE; // 获取 float 类型可取的最小值
float min_normal = Float.MIN_NORMAL; // 获取 float 类型可取的最小标准值
float size = Float.SIZE; // 获取 float 类型的二进制位
3.4 Number超类
上面说过有六个基本的包装类派生于公共的Number超类,Number同时也是一个抽象类,Number类属于java.lang包;
因为抽象类不能被实例化,只能实例化它的具体的子类(Double、Float…),下面简单展示一下Number类的使用
Number num = new Double(12.5);#定义一个抽象类的对象变量,只能引用非抽象子类的对象
System.out.println("返回 double 类型的值:" + num.doubleValue());#返回 double 类型的值:12.5
System.out.println("返回 int 类型的值:" + num.intValue());#返回 int 类型的值:12
System.out.println("返回 float 类型的值:" + num.floatValue());#返回 float 类型的值:12.5
4.System类
System 类位于 java.lang 包,代表当前JAVA程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类;
System 类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法;
4.1 成员变量
System类有3个静态
成员变量:
- printstream out:标准输出流,对应于显示器输出或者由主机环境或用户指定的另一个输出目标
#编写一行输出数据的典型方式
System.out.println(data);
#println 方法是属于流类 PrintStream 的方法,而不是 System 中的方法
- inputstream in:标准输入流,对应于键盘输入或者由主机环境或用户指定的另一个输入源
- printstream err:标准错误输出流:语法与 System.out 类似,不需要提供参数就可输出错误信息,也可以用来输出用户指定的其他信息,包括变量的值
4.2 成员方法
System 类中提供了一些系统级的操作方法,常用的方法有 arraycopy()、currentTimeMillis()、exit()、gc() 和 getProperty(),具体使用方法可以查阅资料;
5.反射
-
编译期:指把源码交给编译器编译成计算机可以执行的文件的过程(Java 中也就是把 Java 代码编成 class 文件的过程);
-
运行期:把编译后的文件交给计算机执行,直到程序运行结束(所谓运行期就把在磁盘中的代码放到内存中执行起);
JAVA反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性(简单来说,反射机制指的是程序在运行时能够获取自身的信息);
Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect
包:
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
JAVA中反射机制用的比较少,这里就不再赘述,感兴趣可以自己查阅资料;