Java主要入门知识总结

1、Java的发展历史

1991年,Sun公司为了研究与开发面向家电市场的软件产品,专门成立了James Gosling(公认的Java爸爸)领导的Green小组基于C++研发了比于C++具有突出平台独立性、高度的可靠性和安全性的Oak语言。

1992年,Oak与GreenOS的操作系统、用户接口模块、硬件模块一起集成为一种类似于PAD的设备Star Seven,这个设备收到了Sun决策层的好评,并升级为First Person公司。

后来由于商业原因,Star Seven没有融入市场,公司最终因为没有业绩而解体。此时的WWW上传输的是静态信息,不具有交互性和动态性。Sun决策层意识到Oak可以实现交互功能。

于是在1995年James Gosling和Patrick Naughton这两个哥们儿完成了Oak的新版本和第一个Oak程序——Web Runner。Oak从此更名为Java,Web Runner更名为HotJava。

从此Java语言的发展进入快车道
1996年1月,Sun发布JDK 1.0
1997年2月,Sun发布JDK 1.1
1998年12月,Sun发布Java 2平台和JDK 1.2
1999年6月,Sun将Java 2平台分为了三个版本:J2SE、J2EE、J2ME
2004年10月,Sun发布JDK 1.5(JavaSE 5.0),同时将J2SE、J2EE、J2ME分别改名为:JavaSE、JavaEE、JavaME
2006年4月,Sun发布JDK 6
2009年4月20日,Oracle收购Sun
2011年7月,Oracle发布JDK 7
2014年3月,Oracle发布JDK 8
2017年9月,Oracle发布JDK 9
2018年3月,Oracle发布JDK 10
2018年9月,Oracle发布JDK 11
2019年3月,Oracle发布JDK 12
2019年9月,Oracle发布JDK 13
2020年3月,Oracle发布JDK 14

2、Java的特性

在Sun公司的Java白皮书中指出,Java是一种“简单、面向对象、分布式、解释型、强壮、安全、体系结构中立、可移植、高性能、多线程和动态”的编程语言。对这些特性的理解是领会Java语言精髓的关键。

2.1 简单性

Java的语言句法和语义比较单纯,容易学习和使用。另外,Java对C++中容易引起错误的成分进行了相当成功的改造。Java还提供了大量丰富的可重用类库,简化了编码工作量。

2.2 面向对象

Java是一种面向对象编程语言。

Java的对象有模块化和信息隐藏能力;满足面向对象的封装要求;支持满足面向对象的继承性;同时,Java通过抽象类和接口支持面向对象的多态要求,即一个对外接口,多种内部实现。

2.3 分布式特征

Java具有分布式计算的特征。分布式计算中“分布”具有两层含义:1、数据分布:应用系统所操作的数据可以分散存储在不同的网络节点上;2、操作分布:应用系统的计算可由不同的网络节点完成。

数据分布支持:通过Java的URL类,Java程序可以访问网络上的各类信息资源,访问方式完全类似于本地文件系统

操作分布支持:Java通过嵌在WWW页面中的Applet(小应用程序)将计算从服务器分布至客户机。Applet由WWW浏览器在客户端执行,从而避免了网络拥挤,提高了系统效率

2.4 半编译、半解释特征

Java应用程序的执行过程具有半编译半解释的特征。

采用编译器对程序进行编译,编译得到的是一种中性的字节码(后缀为.class),并不是本机代码,编译没有进行彻底,故称为“半编译”。
字节码的执行采取解释执行方式,这种解释执行与传统的解释执行方式的差异是:不是以源码为输入的,而是以编译后得到的字节码为输入,所以称为“半解释”。

正是这种特征提高了Java的可移植性——不仅源码可以移植,编译后的字节码也可以移植,而且字节码的移植有利于源码的保密。

半编译和半解释的过程兼具编译执行的效率优势和解释执行的灵活性。

2.5 强壮性

Java提供自动垃圾收集来进行内存管理,防止程序员在管理内存时容易产生的错误出现。通过集成的面向对象的例外处理机制,在编译时,Java提示出可能出现但未被处理的例外,帮助程序员正确地进行选择以防止系统的崩溃。另外,Java是一种强类型语言,程序编译时要经过严格的类型检查,防止程序运行时出现类型不匹配等问题。

2.6 安全性

在分布式环境中,安全性是一个十分重要的问题。Java在语言和运行环境中引入了多级安全问题措施,其采用的主要安全机制有两种:

(1)内存分配以及布局由Java运行系统规定
Java的内存布局由运行系统决定,内存布局依赖于Java运行系统所在的软硬件平台的特性。其次,程序中内存引用关系不是内存单元指针,而是用符号代表。Java编译器是通过符号指针来引用内存,由Java运行系统在运行时将符号指针解释为实际的内存地址。因此,Java的内存分配和引用模型对于程序员是透明的,它完全由底层的运行系统控制,Java程序无法破坏不属于它的内存地址。

(2)运行系统执行基于数字签名技术的代码认证、字节码验证与代码访问权限控制的安全控制模型

2.7 结构体系中立性

Java语言的设计不是针对某种具体平台结构的。Java为了做到结构中立,除了编译成字节码以外还制定了完整统一的语言文本。而C/C++并不满足这一点。

为了使Java的应用程序不依赖于底层具体的系统,Java语言环境还提供了一个用于访问底层操作系统功能的可扩展类库。当程序运行这些库时,可以确保它能运行在支持Java的各种平台上。

2.8 可移植性

Java是迄今为止对可移植性支持最佳的编程语言,可以实现“一次编程,多处使用”。任何设备只要配备了Java解释器便可以运行Java程序。这种特性源于Java的“半编译,半解释”特征;另一方面是Java体系结构中立,采用标准的独立于硬件平台的数据类型,不会因为机器的不同而有所改变。

2.9 高性能

Java虽然采取字节码解释运行方式,但是字节码和机器码十分接近,使得字节码到机器码的转换十分便捷。Java还提供了即时编译技术,即将要执行的字节码一次编译为机器代码,再全速运行,提高了Java应用的运行速度。

2.10 多线程

Java在语言级嵌入了多线程机制,支持程序的并发处理功能。从程序的角度看,一个线程就是应用程序的一个执行流,一个Java程序可以有多个执行线程。

多线程中最大的问题是线程的同步,在许多新型操作系统中广泛使用临界区保护规则。Java讲这些原理集成到语言中,使这些规则的使用更加方便有效。

如果底层的操作系统支持多线程,Java的线程通常被映射到实际的操作系统线程中,这意味着在多机环境下,用Java写的程序可以并行执行。

2.11 动态特性

Java的动态特性是其面向对象设计的延伸,Java程序的基本组成单位是类,而Java的类又是运行时动态装载的,这使得Java可以动态地维护应用程序以及支持类之间的一致性,而不用像C++那样,当其支持类库升级之后,相应的应用程序都必须重新编译。

3、为什么要学Java

Gosling说过:“语言只是实现目标的工具,而不是目标本身”,换一句话来说,编程语言应当注重于解决特定的问题。个人认为,编程语言没有最好的,只有最合适的。比如说用HTML来开发网页要比C更合适,用C来硬件编程要比HTML更合适。在WWW技术和HTML技术引爆的信息时代,Java可以解决网页GUI和用户交互的问题,所以Java被推上了流行语言排行的前列。这也是为什么Java技术人才短缺和薪资较高的原因。

在未来,可能会出现一个新的技术,引发了一场新的变革,出现了新的主要问题,解决那个主要问题的语言也将会像今天的Java一样被推向语言排行的前列。

除此之外,Java的特性可以解决很多问题。例如,Java的跨平台性可以让Java程序在所有安装了JRE的设备上运行。在未来万物互联的时代,Java势必会成为更加重要的语言。有点像“山不辞土,故能成其高;海不辞水,故能成其深”的感觉。

4、JDK、JRE和JVM

整体关系示意图
图片来源于:https://docs.oracle.com/javase/8/docs/index.html
从官方发表的图片来看,JDK包含了JRE,JRE包含了JVM

5、面向对象程序设计的基本概念

5.1 面向对象程序设计方法概述

5.1.1 面向对象问题求解的基本思想

所有的程序语言都是基于一种抽象机制进行问题求解。汇编语言是对底层机器的抽象,面向过程的高级语言是对汇编语言的抽象。虽然我们有了像C、BASIC这样的高级语言,但是仍然要求程序员以机器世界中的数据结构去想象现实世界中的问题,建立机器世界的问题模型,并将“机器空间”和“问题空间”进行关联和映射。因为机器世界的问题模型与现实世界问题本质上存在的结构有很大差异,因此这两种不同的结构空间的映射往往是很复杂的,程序的编写和维护都存在很大的问题,所以人们专门研究程序方法学来解决这些问题。

早期的LISP和APL选取了现实世界的不同视图建立模型,对特定的问题有效,但在特定问题之外就表现出了很大的局限性。

面向对象方法在基于问题的求解方法上前进了一大步。它向程序员提供了通用的方法和工具来表达现实世界中的各种问题。表示客观存在且可区分的实体 — — “对象”。它的概念是现实世界很普遍的概念。在面向对象方法中,以“对象”的概念作为建立“问题空间”与“机器空间”模型的基本元素 — — 人们基于现实世界中对象以及对象之间的关联建立问题空间的问题模型,在程序中建立对象并通过对象之间的互操作机制建立了机器世界问题模型,使得问题的“问题模型”和计算机的“机器模型”能够具有统一的表达,从而使问题得到解决。

5.1.2 面向对象程序设计方法的内涵

Alan Kay总结提出了Smalltalk的5个基本特征。SmallTalk被认为是第一种成功的面向对象语言,也是Java语言的基础。通过这些特征可以深入理解面向对象程序设计方法的内涵,如下所述:

1、程序中所有事物都是对象。可以将对象想象成一种新类型的变量,它保存着数据,对外提供服务,对自己的数据进行操作;
2、程序是一些列对象的组合。对象之间通过消息传递机制组合起来,相互调用彼此方法,实现程序的复杂功能;
3、每个对象都有自己的存储空间,可以容纳其他对象。利用封装机制,可以以现有对象为基础构造出新的对象。因此,虽然对象的概念很简单,但程序中可以实现任意复杂度的对象;
4、每个对象都有一种类型。每个对象都是某个类的一个实例,其中类是类型的同义词。类最主要的特征是对外接口;
5、同一类型的所有对象都能够接收相同的消息。子类和父类具有“同一区域”。例如类型为Circle的对象与类型为Shape的对象是同类对象,所以Circle对象可以接收Shape对象的消息。这意味着,在程序中可以统一操作Shape类体系(包括Shape及其所有子类),这就是面向对象程序语言中的多态性。

5.2 对象和类

5.2.1 对象的含义与结构

现实生活中存在很多对象,比如小猫、小狗、人、自行车、汽车等,它们可以看做成对象。现实生活中的对象具有两个特征:状态和行为;面向对象程序设计以现实生活中的对象为模型创建,也具备两个特征:状态和行为,状态保存在变量中,行为保存在方法中。因此,可以用编程语言中的“对象”来抽象表示现实生活中的“对象”。

对象的方法一方面将内部变量包裹、保护起来,另一方面还是对象与外部环境和其他对象交互、通信的接口。

5.2.2 对象之间的互操作

单个对象能够实现的功能很有限。只有对象之间相互作用才能让程序实现更高级、更复杂的功能。当对象A让对象B精确地完成一个动作,需要将一些细节信息作为消息参数发送给对象B。所以一个消息由三部分构成:消息所属对象、消息名称和消息所需参数。

5.2.3 类的概念

同一类型的对象具有共同的特征和行为。比如说,捷安特、美利达和永久都属于自行车类,但他们本身是自行车类的对象,而对象和对象之间可能也会有差异。类是模板,它规定了对象所具备的特性和行为,对象可以有属于自己的特性和行为。

5.3 封装

对象的方法包裹着对象的内核(或说状态,属性),使对象的内核能够对程序中的其他对象隐藏。使用对象的方法将对象的变量与实现保护起来,就称为封装。

封装可以使得对象的代码能够形成独立的整体并保证对象数据的一致性并易于维护。

5.4 继承

5.4.1 继承的概念

对象之间主要存在三种关系:包含、关联和继承。汽车与汽车中的发动机属于包含关系;汽车与汽车制造厂商属于关联关系,它们可以通过零部件关联起来;山地车是自行车的一种,它们是继承关系。

继承是这三种关系中最为重要的一种关系。A继承于B,A为子类,B为父类。子类继承父类的状态和方法,子类继承并具有父类的接口使得子类对象可以作为父类对象使用。子类和父类不同之处在于:1、子类可以扩充父类的功能;2、可以修改父类的功能(Override重写)。

5.4.2 继承关系的特点

Java只支持单继承,子类 + extends + 父类,实现子类对父类的单继承。单继承的优点是可以避免多个直接父类之间可能产生的冲突,使代码更可靠。

而现实生活中多重继承是普遍存在的,Java提供了接口(interface)机制,允许一个类实现多个接口。既避免了多重继承的复杂性,又达到了多重继承的效果。Java通过接口实现多重继承。

在继承关系中,子类可以继承父类的属性和方法,子类中只需要声明自己特有的属性和方法。但子类不能继承父类所有的变量和方法,下列是子类所不能继承的:
(1)带private修饰符的属性和方法
(2)构造方法

super关键字指向该关键字所在类的父类,用来引用父类中的成员变量或方法。通过super.someMethod([paramlist])将调用父类中的someMethod()方法。

5.4.3 子类对象的创建与实例化过程

Java中对象的初始化是很结构化的,目的是保证程序运行的安全性。在有继承关系的类的体系中,一个子类对象的创建和初始化都要经过以下3步:
(1)分配对象所需要的全部内存空间
(2)按继承关系,自顶向下显式初始化
(3)按继承关系,自顶向下调用构造方法
当前类的各级父类直到类体系的根类(Object类),都要执行上述第(2)步和第(3)步
Java的安全模型要求对象在初始化时,必须先将从父类继承的部分进行完全的初始化。因此Java在执行子类构造方法之前通常要调用父类的一个构造方法。一般在子类构造方法的第一行通过super([paramlist])调用父类的某个构造方法,如果不使用super关键字指定,则Java将调用父类默认的构造方法(不带参数的构造方法)。如果在父类中没有无参数的构造方法,则将产生错误。

5.4.4 方法重写

在类层次结构中,当子类的成员变量与父类的成员变量同名时,子类的成员变量会隐藏父类的成员变量;当子类的方法与父类的方法具有相同的方法名、参数列表、返回值类型时,子类的方法重写了父类的方法,在父类定义的方法就被隐藏。隐藏的含义是,通过子类对象调用子类中与父类同名的变量和方法时,操作的就是这些变量和方法在子类中的定义。子类成员变量的隐藏和方法的重写可以把父类的状态和行为改变为自身的状态和行为。

方法重写遵循的规则
(1)子类重写方法必须与父类中对应的方法具有相同的返回值、方法名和参数列表
(2)子类中重写方法的访问权限不能缩小
(3)子类中重写方法不能抛出新的异常

5.5 多态

多态,简而言之是“对外一个接口,内部多种实现”。多态分为两种:1、编译时多态;2、运行时多态。编译时多态通过重载(Overloading)实现,即一个类中相同的方法名可用来定义多种不同的方法。下面主要讲解运行时多态。

5.5.1 方法重载

有时候需要在同一个类中定义几个功能相同但是参数不同的方法。比如说输出函数println() — — 将参数以文本形式输出显示,由于参数的类型不同,所以方法会有不同的处理。输出int、float、String类型的参数,需要定义printInt() printFloat() printString()方法。这种定义方式不仅显得枯燥还要求程序员熟悉不同方法名称,出错率增加。

为此Java提供了重载(Overloading)机制,允许一个类的定义中,多个方法使用相同的方法名,不同的参数列表。程序员只需要记住一个方法名称println(),就可以打印输出各种数据类型的方法,减轻了程序员的负担。

public void println(int i){...}
public void println(float f){...}
public void println(String s){...}

方法重载属于程序语言多态性的一种,属于Java的编译时多态。由编译器在编译时刻确定具体调用哪个被重载的方法。

Java规定重载的方法必须遵循以下原则以便编译器能够区分这些方法:
(1)方法的参数列表必须不同,包括参数的类型或个数,以区分不同的方法体;
(2)方法名称必须相同;方法的返回类型、修饰符可以相同也可以不同。

5.5.2 运行时多态

向上造型技术是面向对象程序实现多态的关键技术之一,要理解运行时多态需要掌握向上造型的概念。运行时多态的基础是继承。子类对象可以当做父类对象看待,这种把子类当做父类看待的处理过程叫做向上造型。在图中的例子中,Circle、Square和Triangle都是Shape的子类,它们都可以通过向上造型使其类型对外呈现出Shape类型。
向上造型图解
因为子类可以当做父类来使用,所以整个类的体系中所有类采取一致的接口(即父类/基础类的接口)进行访问。当程序运行时,会根据运行时刻基础类对象的具体类型(子类类型)调用该子类对象中相应的接口实现。从而实现了“对外一个接口,内部多种实现”的特点,因此称之为“多态”。

子类通常包含比父类更多的变量和方法,所以子类可以认为是父类的超集,所以上溯造型是安全的。相反,下塑造型即一般的强制类型转换不一定安全,需要进行类型检查。

5.5.3 运行时多态的实现机制

运行时多态的实现机制是动态联编技术,也叫做晚联编或运行期联编。将一个方法调用和一个方法体连接到一起,就称为联编。若在程序运行之前执行联编操作,则称为“早联编”;在运行时刻执行联编就称为“晚联编”。

在晚联编中,联编操作是在程序运行时刻根据对象的具体类型执行的。实现晚联编的语言,必须提供一些机制在程序运行期间判断对象的类型,并进一步调用适当的方法。也就是说,在晚联编中编译器此时不知道对象的类型,但运行时刻的方法调用机制能够自己确定并找到正确的方法体。

在Java中,除了定义为final的方法,其余所有方法的联编都采用晚联编技术。

5.5.4 对象类型的强制转换

对象类型的强制转换也称为向下造型,是将父类类型的对象变量强制(显式)地转换为子类类型。Java中允许上溯造型的存在,但是通过该变量只能访问父类中定义的变量和方法,子类中特有的部分被隐藏不能访问。只有将父类类型变量强制转换为具体的子类类型,才能通过该变量访问子类的特有成员。

强制类型转换的格式一般是:

(SomeClass)aObjectVariable

在进行对象类型的强制转换时,为了保证转换能够成功,一般先使用instanceof对对象进行测试,当测试结果为true时再进行转换

public void doSomething(Employee e){
	if(e instanceof Manager){
		Manager m = (Manager)e//处理一个Manager对象
	}else if(e instanceof Secretary){
		Secretary s = (Secretary)e//处理一个Secretary对象
	}else{
		...//处理其他类型的Employee对象
	}
}

Java语言在执行强制类型转换时需要遵循以下规则:
(1)对象变量转换目标类型,一定是当前对象类型的子类。这个规则由编译器检查
(2)在运行时刻也要进行对象类型的检查。

6、Java语言基础

6.1 数据类型和变量

6.1.1 Java的基本数据类型

Java一共定义了4类共8种基本数据类型:

逻辑型:boolean
boolean类型数据占据1bit空间,有两种取值:true & false,默认初始值为false

文本型:char
char是一个16bit的Unicode字符,支持各类文字的字符

整型:byte,short,int和long

类型长度取值范围
byte8bit-2^7 - 2^7 ( -128 - 127 )
short16bit-2^15 - 2^15-1(-32768 - 32767)
int32bit-2^31 - 2^31-1(-2147483648 - 2147483647)
long64bit-2^63 - 2^63-1(-9223372036854775808 - 9223372036854775807)

P.S. Java中所有整数类型都是有符号的

浮点型:double和float

类型长度取值范围
float32bit1.4e-45 ~ 3.4028235e+38
double64bit4.9e-324 ~ 1.7976931348623157e+308

6.1.2 复合数据类型

Java内置了8种基本数据类型,但在很多应用开发中,使用这几种类型远远不够。比如说要处理日期需要定义3个int类型的变量:day、month、year,但是这三个变量之间又有约束,则需要编码,但是编码会导致程序的复杂度,增加错误的概率。

Java语言允许用户定义新的数据类型,所以上述问题就得到了很好的解决。一般将用户定义的新的类型成为复合数据类型。Java是一种面向对象的语言,基于面向对象的概念,以类和接口的形式定义新类型。

//日期复合数据类型的定义
class MyDate{
	int day;
	int month;
	int year;
}

//声明变量
MyDate a,b;

//调用变量
a.day = 3;
a.month = 12;
a.year = 2001;

6.1.3 变量和作用域

变量有基本类型和引用类型,按照作用域来分有局部变量、类成员变量、方法参数和异常处理参数

(1)局部变量
在方法内部或由一对{ }表示的代码块内定义的变量叫做局部变量。作用域是所在的方法或者代码块。

(2)类成员变量
在方法外声明且属于一个类定义体的变量成为类成员变量。作用域是整个类。具体有两种类型:1、用static关键字声明的类变量,该变量在类加载时创建并且只要所属的类存在,该变量就一直存在。2、声明中没有static关键字的变量称之为实例变量。实例变量在调用类的构造方法(new XXX())创建实例对象时创建,并且只要有引用指向变量所属的对象,该变量就将存在。

(3)方法参数
方法参数定义了方法调用时传递的参数,其作用域就是所在的方法。

(4)异常处理器(catch语句块)参数
异常处理器参数是catch语句块的入口参数,这种参数的作用域是catch语句后由{ }表示的语句块。

变量的初始化
类成员变量系统会自动初始化;局部变量必须在使用前手动赋初值初始化,否则会产生编译错误。

6.2 数组

数组的基本性质
数组中的元素是同一种类型(可以是基本类型也可以是引用类型),其长度在创建时确定,并且在创建后不变。

数组的声明
在Java中数组是作为类来处理的。在Java中类类型变量的声明并不创建该类的对象,而是给该数组变量分配了一个可用来引用该数组的引用空间。

//在数组的声明中不指定数组的长度
//格式一
char s[];
Point p[]; //Point是一个类

//格式二
char[] s;
Point[] p;

//格式三
char[] s,m,n;

数组的创建与初始化

//三个步骤三条语句
char[] s;//1、声明
s = new char[20];//2、创建
s[0] = 'a';//3、初始化
s[1] = 'b';

//三个步骤一条语句
String names[] = {"Jack","Wang","Lee"}; 

foreach
JDK5.0以后,为了便于对数组和集合中的元素进行迭代处理,Java引入了一种增强的for循环形式 — — foreach
语法为:for(类型 标识符:可迭代类型的表达式) 语句;

数组的复制
要进行数组数据的复制,需要使用Java语言在java.lang.System类中提供的数组复制方法 — — arraycopy(Object source,int srcIndex,Object dest,int destIndex,int length)。

7、Java的面向对象特征

7.1 Java语言的OOP特征

Java语言支持面向对象程序语言的三个关键特征:1、封装;2、继承;3、多态

7.2 类的定义

7.2.1 类的基本结构

一个类的定义分为类的声明和类体。

类的声明声明了类的名字以及类的其他属性。其完整格式如下:

[public] [abstract] [final] class ClassName [extends SuperClassName] [implements InterfaceNameList]{...}

修饰符public、abstract、final说明了类的属性;extends关键字表示类继承了以SuperClassName为名的父类;implements关键字表示类实现了InterfaceNameList中列出的各个接口

类体提供了这个类对象在生命周期中的所有代码:变量、构造方法和行为方法。

//Java类定义的基本语法是
<modifiers> class <class_name>{
	[<attribute_declarations>]
	[<constructor_declarations>]
	[<methods_declarations>]
}

//Java类定义案例
public class Bicycle{
	//变量声明
	public int cadence;
	public int gear;
	public int speed;
	//构造方法声明
	public Bicycle(int startCadence,int startSpeed,int startGear){
		gear = startGear;
		cadence = startCadence;
		speed = startSpeed;
	}
	//方法声明
	public void setCadence(int newValue){
		cadence = newValue;
	}
	public void setGear(int newValue){
		gear = newValue;
	}
	public void applyBrake(int decrement){
		speed -= decrement;
	}
	public void speedUp(int increment){
		speed += increment;
	}
}

7.2.2 成员变量

一个变量的声明出现在类体中但不属于任何一个方法,则这个变量为类的成员变量。其声明格式如下:

[public | protected | private] [static] [final] [transient] [volatile] type varibleName; 

其中修饰符public、protected或private说明了该成员变量的访问权限;static属性限制该成员变量为类变量,没有用static的变量为实例变量;final关键字限定的变量是常量,在程序中不能修改它的值,而且变量名一般由下划线分开的大写词来表示,如CONSTANT;transient声明一个暂时性变量:在默认情况下,类中所有变量都是对象永久状态的一部分,当对象呗保存到外存时,这些变量必须同时被保存,而用transient限定的变量指示JVM该变量不属于对象的永久状态,从而不能被永久储存;volatile修饰的变量在被多个并发线程共享时,系统将采取更优化的控制方法提高线程并发执行的效率。

7.2.3 成员方法

成员方法的定义由两部分组成:方法声明和方法体。

方法声明的完整格式如下:

[<accessLevel>] [static] [final | abstract] [native] [synchronized] <return_type> <name> ([<argument_list>]) [throws<exception_list>]{<block>}

accessLevel可以使用public、protected或private限定方法的访问权限;static限定它为类方法,实例方法不需要static限定;abstract表明方法是抽象方法;final表明方法不能被重写;native表明方法是由其他语言实现;synchronized用来控制多个并发线程对共享数据的访问;throws exception_list列出该方法将要抛出的例外。

方法体是对方法的实现,它包括局部变量的声明以及所有合法的Java语句。如果局部变量的名称与类成员变量相同(类成员变量被局部变量隐藏),需要在类成员变量前加上this将该变量显现出来。

7.2.4 this

this是Java使用的一个有特定意义的引用,它指向当前对象本身。

public class MyDate{
	//成员变量
	private int day,month,year;
	//构造方法
	public MyDate(int day,int month,int year){
		this.day = day;//this. 使用对象中的成员变量
		this.month = month;
		this.year = year;
	}
	//成员方法
	public String tommorrow(){
		this.day = this.day+1;
		return this.day + "/" + this.month + "/" + this.year;
	}
	public static void main(String[] args){
		MyDate d = new MyDate(3,12,2001);
		System.out.println(d.tommorrow);
	}
}
//结果为:3/12/2001

7.2.5 构造方法

**作用:**构造方法用来进行该类对象的初始化

构造方法定义格式如下:

[public | protected | private] <class_name> (<argument_list>){
	[<statements>]
}

构造方法的规定
(1)构造方法名称必须与类名一样
(2)构造方法不能有返回值
(3)用户不能直接调用构造方法,必须通过关键字new自动调用它

默认的构造方法
在类的定义中可以不定义构造方法,而其他类仍然可以通过调用new XXX() 来实例化XXX类的对象。这是因为Java在编译时给没有定义构造方法的类自动加入了一个特殊的构造方法,这个方法不带有参数而且方法体为空,称为默认的构造方法。其初始化的默认值 — — 数值型为0;boolean为false;char为‘\0’;对象为 null。

注意
如果在类中定义了构造方法,则默认的构造方法将不被加入到类的定义中。此时如果在程序中使用默认的构造方法将会出现编译错误,为避免这种错误,如果类中定义了构造方法,通常也将加入不带参数的构造方法。

7.2.6 访问控制

同一个类同一个包子类全局
private
default
protected
public

7.3 内部类

什么是内部类:内部类是一个类的声明里声明的类,也称为嵌套类,从JDK1.1开始支持。内部类和包容它的类可以形成有机的整体

//类B在类A中定义,B称为内部类,A称为B的包容类或外包类
class A{
	...
	class B{
		...
	}
}

7.3.1 内部类的使用

内部类做为外包类的一个成员使用,可以访问外包类的所有成员,包括带有static的静态成员变量和方法,以及private的私有成员。在JVM对内部类的实现中,在内部类对象中保存了一个对其外包类对象的引用,所以内部类可以通过该引用找到外包类的对象,进而访问外包类的成员。

public class Outer{
	private int size;
	/**定义内部类Inner*/
	public class Inner{
		public void doStuff(){
			size++; //在内部类中的方法doStuff()将外包类成员变量size递增
		}
	}

	Inner i = new Inner(); //成员变量i指向Inner类的对象
	
	public void increaseSize(){
		i.doStuff(); //调用内部类的Inner方法
	}
	
	public static void main(String[] args){
		Outer o = new Outer(); 
		for(int i=0;i<4;i++){
			o.increaseSize();
			System.out.println("The value of size : " + o.size);
		}
	}
	/**
	 * 运行结果:
	 * The value of size : 1
	 * The value of size : 2
	 * The value of size : 3
	 * The value of size : 4
	 */
}

内部类Inner的doStuff()方法访问其外包类Outer的同名变量size时,采用的格式为:外包类名.this.同名变量名

在外包类的语句块中定义内部类
内部类可以在一个方法体的语句块中定义。这时内部类可以访问语句块中的局部变量,但只限于在该语句块运行期内,当该方法运行结束后,内部类对象将不能访问所在语句块中的局部变量。但因为final变量在方法运行结束后仍然存在,所以内部类对带有final的局部变量的访问不受上述限制。

class Outer{
	private int size = 5;
	
	/** 方法makeInner(),返回一内部类对象*/
	public Object makeInner(final int finalLocalVar){
		int LocalVar = 6;
		class Inner{
			public String toString(){
				return ("#<Inner size = "+ size + 
					//"LocalVar=" + LocalVar + //此处对LocalVar的非法访问
				"finalLocalVar = " + finalLocalVar + ">");
			}
		}
		return new Inner();
	}
	public static void main(String[] args){
		Outer outer = new Outer();
		Object obj = outer.makeInner(40);
		System.out.println("The object is " + obj.toString());
	}
}
//结果为:The object is #<Inner size = 5 finalLocalVar = 40>

在外包类以外的其他类中访问内部类
在Java中,内部类的访问权限与普通类不同,可以定义为public、protected、default或者private,而普通类只能定义public或default两种。对于可以在外包类之外访问的内部类,引用内部类名时必须使用完整的标识:外包类名.内部类名,并且在创建内部类对象时,必须与外部类的对象相关。例如,假设类B是类A的内部类,则在其他类中要以下列各式访问类B:

A a = new A();
A.B b = new B();
class Outer{
	private int size;
	
	/** 定义内部类Inner*/
	class Inner{
		void doStuff(){
			size++;
			System.out.println("The size value of the Outer class:" + size);
		}
	}
}

public class TestInner{
	public static void main(String[] args){
		Outer out = new Outer();
		Outer.Inner in = out.new Inner();
		in.doStuff();
	} 
}
//结果为:The size value of the Outer class:1

内部类的特征
(1)内部类的类名只能用于定义它的类或语句块之内,在外部引用它时必须给出带有外部类名的完整名称,并且内部类的名字不许与外包类的名字相同
(2)内部类可以访问外包类的静态或实例成员变量
(3)内部类可以在成员方法中定义,该成员方法的局部变量及参数必须是default的才能被内部使用
(4)内部类可以是抽象类或接口。如果是接口,则可以由其他内部类实现
(5)内部类可以使用public、protected、default或private等4种访问权限控制
(6)内部类可以被声明为static(普通类不可以),这样的内部类变成顶层类,相当于把它放在外面,不再是嵌套的内部类,并且它的对象中将不包含指向外部类对象的指针,所以不能再引用外包类对象
(7)只有顶层类可以声明static成员。如果内部类需要定义static成员,则该内部类必须声明为static,否则,一般内部类的成员不能被声明为static

7.4 对象的生命周期

7.4.1 对象的创建

当一个对象完成了所有操作之后,将被垃圾收集器收集,它所占有的资源将被回收并由系统提供给其他对象使用。对象的生命周期包括了对象的创建、使用和回收三个阶段

对象创建的步骤
(1)声明对象变量:以SomeClass objectVar的形式声明保存该对象引用的变量,将来可以对该变量进行操作。对象的声明没有创建对象,系统只是为该变量分配了一个引用空间
(2)对象的实例化:通过使用new运算符进行对象的实例化:new Someclass(),对象的实例化过程是:为对象分配空间,执行new运算符后构造方法完成对象的初始化,并返回该对象的引用。

创建与初始化对象的过程
对象的创建和初始化,即执行new Someclass()过程如下:
(1)为对象分配内存空间,对成员变量进行初始化。
(2)执行显式初始化,即执行声明时带有的简单赋值语句
(3)执行构造方法,进行对象的初始化

7.4.2 对象的清除

在Java中,可以创建所需要的许多对象,而不必关心对象的删除。Java运行系统会在确定某个对象不再被使用时自动将其删除。这个过程被称为垃圾收集。

垃圾收集器收集的对象是被确认不存在任何引用的对象。一个变量中保存的引用通常在该变量的作用域内有效,在其作用域之外变量及其包含的引用将不复存在。另外也可以显式地删除一个对象的引用,方法是将该引用型变量的值赋为null,只有将对象中的所有引用都删除,垃圾回收器才能回收这个对象。

垃圾收集器会周期性地释放不再被引用的对象所占有的内存,自动执行回收。

一个对象在被收集之前,垃圾收集器将调用对象的 finalize() 方法,以使对象自己能够做最后的处理,释放占有的资源,这个过程称为对象的最终化。只有进行最终化处理的对象才意味着被废弃。程序员一般不需要实现 finalize() 方法。

8、Java高级特性

8.1 static关键字

static关键字可以用来修饰类的成员变量、成员方法和内部类,使得这些类成员的创建和使用与类相关而与类的具体实例不相关,因此以static修饰的关键字或方法又被称为类变量和类方法。

8.1.1 静态变量

在成员变量声明时使用static,则该变量就称为静态变量或者类变量。静态变量只在系统加载其所在类时分配空间并初始化,并且在创建该类的实例时将不再分配空间,所有的实例将共享类的静态变量。因此静态变量可以用来在实例之间进行通信或跟踪该类实例的数目。

8.1.2 静态方法

在类的成员方法中带有static关键字,则该方法就称为类方法静态方法。静态方法要通过类名而不是通过实例对象访问。
在静态方法的编写和使用时应该注意下列问题:
(1)静态方法的调用不是通过实例对象进行的,所以静态方法中没有this指针,不能访问所属类的非静态变量和方法,只能访问方法体内定义的局部变量、自己的参数和静态变量。
(2)子类不能重写父类的静态方法,但在子类中可以声明与父类静态方法相同的方法,从而将父类的静态方法隐藏。另外子类不能把父类的非静态重写为静态的。
(3)main()方法是一个静态方法。因为它是程序的入口点,这可以使JVM不创建实例对象就可以运行该方法。因此如果要在main()方法中访问所在类的成员变量或方法,就必须首先创建相应的实例对象。

8.1.3 静态初始化块

在一个类中,不属于任何方法体并且以static关键字修饰的语句块,称为静态语句块。因为静态语句块常用来进行类变量的初始化,所以也称为静态初始化程序块。其定义格式如下:

static{
	...
}

静态语句块在加载该类时执行且只执行一次。如果一个类中定义了多个静态语句块,则这些语句块将按在类中出现次序运行。

8.2 final关键字

(1)在类的声明中使用final:Java允许在类的声明中使用final关键字,被定义成final的类不能再派生子类

(2)在成员方法声明中使用final:对于类中的成员变量也可以定义为final。被定义为final的方法不能被重写。当方法的实现不能被改变或者方法对于保证对象状态的一致性很关键时,应该把方法定义为final。

定义为final的方法可以使运行时的效率优化。对于final的方法,编译器可以产生直接调用方法的代码,从而阻止运行时刻对方法调用的动态联编。实际上,如果方法被定义为static或private,编译器也将会对他们进行上述优化

(3)在成员变量的声明中使用final:如果类的成员变量被定义为final,则变量一经赋值就不能改变,所以可以通过声明final变量并同时赋初值来定义常量,并且变量名一般大写。如:

final int NUMBER = 100;

如果类的final变量在声明时没有赋初值,则在所属类的每个构造方法中都必须对该变量赋值。如果未赋初值的final变量是局部变量,则可以在所属方法体的任何位置对其赋值,但只能赋值一次。

8.3 抽象类

8.3.1 什么是抽象类

Java允许在类中只声明方法而不提供方法的实现,这种只有声明而没有方法体的方法称为抽象方法,而包含一个或多个抽象方法的类称为抽象类。抽象类和抽象方法必须在声明中加上abstract关键字。抽象类也可以有构造方法、普通的成员变量或方法,也可以派生抽象类的子类。

抽象类不能有实例对象,如果抽象类的子类实现了抽象方法,则可以创建该子类的实例对象,否则该子类也是抽象类,也不能创建实例。一般抽象类构造方法的访问权限为protected而不是public,从而保证构造方法能够由子类调用而不被其他无关的类调用。

8.3.2 抽象类的作用

程序中定义抽象类的目的是为一类对象建立抽象的模型,在同类对象所对应的类体系中,抽象类往往在顶层。定义抽象类和抽象方法可以向用户和编译器明确表明该类的作用和用法,使类体系设计更加清晰;抽象类也为类的体系提供通用的接口,使其能够支持多态。

8.4 接口

8.4.1 什么是接口

Java中接口使抽象类的概念更深入一层。接口中声明了方法,但不定义方法体,因此接口只是定义了一组对外的公共接口。与类相比,接口只规定了一个类的基本形式,不涉及任何实现细节。实现一个接口的类将具有接口规定的行为。可以认为一个接口的整体就是一个行为的协议。实现一个接口的类将具有接口规定的行为,并且外界可以通过这些接口与它通信。

8.4.2 接口的定义

接口的定义包括接口声明和接口体两部分。

接口声明

[public] interface InterfaceName [extends listofSuperInterface]{
	...
}

extends子句与类声明中的extends子句基本相同,不同的是一个接口可以有多个接口,用逗号隔开,而一个类只能有一个父类。子接口继承父接口中所有的常量和变量。

接口体
接口体中包含常量定义和方法定义两个部分
在接口中定义的常量默认具有public、final、static属性。常量定义的具体格式为:

type NAME = value;

在接口中声明的方法默认具有public和abstract属性。方法定义的格式为:

returnType methodName([paramlist]);

接口只进行方法的声明而不提供方法的实现,所以方法定义没有方法体,而且以“;”结尾。另外,如果在子接口中定义了和父接口同名的常量和方法,则父接口中的常量被隐藏,方法被重写。

8.4.3 接口的实现与使用

类的声明中用implements子句来表示一个类实现了某个接口,在类体中可以使用接口定义的常量,而且必须实现接口中定义的所有方法。一个类可以实现多个接口,在implements子句中用逗号分隔。在类中实现接口及接口实现时,方法的声明必须与接口中所定义的完全一致。

在程序中,接口可以像类一样作为数据类型来使用,并且可以支持多态。此时任何实现接口的类都可以认为是该接口的“子类”,因此声明为某接口类型的变量,可以指向该接口“子类”的实例,通过这些变量可以访问接口中规定的方法。

8.4.4 多重继承

Java中规定一个类只能继承一个类,但可以实现多个接口,Java利用接口实现多继承。由于接口中根本没有实现细节,所以在进行父类和多个接口的合并时,只可能有一个类具有实现细节,保证了Java的简单性与代码的安全可靠。

8.4.5 通过继承扩展接口

接口定义后,可能在某些情况下,需要对接口进行扩展,如增加新的功能,这时候就可以通过创建子接口来增加新的方法如:

interface Shape{
	void draw();
	void erase();
	double area();
}

interface ShapePerimeter extends Shape{
	double perimeter();
}

这样使用Shape接口的用户也可以选择采用新的接口ShapePerimeter,也可以保持原来对Shape接口的实现。在接口的定义中使用继承,可以方便地为一个接口添加新的方法;也可以通过接口继承将几个接口合并为一个接口,即在子接口声明中的extends关键字后引用多个基础接口,这些接口间通过“,”分隔。

8.4.6 接口与抽象类

区别:
(1)接口中所以的方法都是抽象的,而抽象类可以定义带有方法体的不同方法
(2)一个类可以实现多个接口,但只能继承一个抽象父类
(3)接口与实现它的类不构成类的继承体系,即接口不是类体系的一部分。因此,不相关的类也可以实现相同的接口。而抽象类是属于一个类的继承体系,并且一般位于类体系的顶层。

使用接口的主要优势在于:一是类通过实现多个接口可以实现多重继承 — — 能使子类对象上溯造型为多个基础类(接口)类型。另一个优势是能够抽象出不相关类之间的相似性,而没有强行形成类的继承关系。使用接口可以同时获得抽象类和接口的优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值