Java高级类特性(二)
一、static关键字
static关键字用来声明成员属于类,而不是属于类的对象。
1). static (类)变量
类变量可以被类的所有对象共享,以便与不共享的成员变量区分开来。
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
注意:static成员变量的初始化顺序按照定义的顺序进行初始化。
2). static (类)方法
静态方法可以通过类名直接调用该方法,而不用通过对象调用。
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
注意:虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
例如:
class PersonCount {
private int personID; private static int num = 0; public PersonCount() {
num++;
personID = num;
} public static String getPersonDes() {
return "this is a policeman";
}
} class TestPersonCount {
public static void main(String[] args) {
// 直接用类名来访问该静态方法,而不需要该类的对象
String s = PersonCount.getPersonDes();
System.out.println(s);
}
}
3)static代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。下面看个例子:
class Person{
private Date birthDate; public Person(Date birthDate) {
this.birthDate = birthDate;
} boolean isBornBoomer() {
Date startDate = Date.valueOf("1946");
Date endDate = Date.valueOf("1964");
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
isBornBoomer是用来这个人是否是1946-1964年出生的,而每次isBornBoomer被调用的时候,都会生成startDate和birthDate两个对象,造成了空间浪费,如果改成这样效率会更好:
class Person{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1946");
endDate = Date.valueOf("1964");
} public Person(Date birthDate) {
this.birthDate = birthDate;
} boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。静态方法不能被覆盖成非静态。同样,非静态方法也不能被覆盖成静态方法。
"独身"设计模式
独身设计模式,也就是说一个类只产生一个对象。那么怎么才能做到这一点呢??我们知道构造器是用来构造对象的。首先要对构造器入手。既然只产生一个对象,那么我们就干脆先一刀砍断,把构造器的访问权限定义成私有,不能在类的外面再构造该类的对象。也就是说只能在类的里面调用该类的构造器来产生对象。那么在该类里应该定义一个静态的属性,并初始化该类的一个对象。(原因是静态的东西只执行一次,也就是说该属性只初始化一次。那么每次得到的应该是同一个实例)
class TestSC {
public static void main(String[] args) {
SingleClass sc1 = SingleClass.sc;
SingleClass sc2 = SingleClass.sc;
sc1.test();
sc2.test();
}
} class SingleClass {
int i = 0;
static SingleClass sc = new SingleClass();
private SingleClass() {
} public void test() {
System.out.println("hello " + (++i));
}
}
运行的结果为:
hello 1
hello 2
说明是同一个实例。
在类的设计的时候,我们也应该遵守封装的要求。把属性定义成私有的。再定义一个共有的方法来去到该属性的值。
改后的代码:
class TestSC {
public static void main(String[] args) {
SingleClass sc1 = SingleClass.getSingleClass();
SingleClass sc2 = SingleClass.getSingleClass();
sc1.test();
sc2.test();
}
} class SingleClass {
int i = 0; private static SingleClass sc = new SingleClass(); private SingleClass() {
} /*
* 因为在类的外面不能来构造给类的实例了, 所有该方法定义成静态的,通过类名直接可以调用。
*/
public static SingleClass getSingleClass() {
return sc;
} public void test() {
System.out.println("hello " + (++i));
}
}
二、final关键字
1 、final类
Java编程语言允许关键字final修饰类。如果这样做了,类便不能被继承。比如,类Java.lang.String就是一个final类。这样做是出于安全原因,因为它保证,如果方法有字符串的引用,它肯定就是类String的字符串,而不是某个其它类的字符串。
2 、final方法
方法也可以被标记为final。被标记为final的方法不能被覆盖。这是由于安全原因。如果方法具有不能被改变的实现,而且对于对象的一致状态是关键的,那么就要使方法成为final。
被声明为final的方法有时被用于优化。编译器能产生直接对方法调用的代码,而不是通常的涉及运行时查找的虚拟方法调用。
被标记为static或private的方法被自动地final。
3 、final变量
如果变量被标记为final,其结果是使它成为常数。想改变final变量的值会导致一个编译错误。下面是一个正确定义final变量的例子:
public final int PI = 3.14;
三、内部类
内部类,有时叫做嵌套类。内部类允许一个类定义被放到另一个类定义里。内部类是一个有用的特征,因为它们允许将逻辑上同属性的类组合到一起,并在另一个类中控制一个类的可视性。内部类可以访问外部类的属性和方法。你可以把内部类看作"方法"一样,在使用的时候调用执行。你也可以把内部类看作"属性"一样,在构造内部类对象的时候,也会在堆里为内部类的属性分配存储空间。所以内部类也有类似像修饰属性,方法那样的修饰符,比如:public,private,static 等等。当一个类没有用static 关键字修饰的时候,这个内部类就叫做成员类,类似属性,方法,作为类的成员。
内部类有如下属性:
1 内部类只在定义他们的代码段内可见。
2 内部类可以被定义在方法中。如果方法中的变量被标记为final,那么,就可以被内部类中的方法访问。
3 内部类可以使用所嵌套类的类和实例变量以及所嵌套的块中的本地变量。
4 内部类可以被定义为abstract.
5 只有内部类可以被声明为private或protected,以便防护它们不受来自外部类的访问。
6 一个内部类可以作为一个接口,由另一个内部类实现。
7 被声明为static的内部类自动地成为顶层类。这些内部类失去了在本地范围和其它内部类中使用数据或变量的能力。
8 内部类不能声明任何static成员;只有顶层类可以声明static成员。因此,一个需求static成员的内部类必须使用来自顶层类的成员。
四、成员内部类
例1:
public class OC1 {
private int size; public class IC {
public void addSize() {
size++;
}
} public void testTheIC() {
IC i = OC1.new IC();
i.addSize();
}
}
内部对象拥有一个外部对象的引用:
例2:
这个例子阐述了如何在其它类(外部类的外部)实例化内部类:
class OC2 {
private int size; public class IC {
public void addSize() {
size++;
}
}
} public class TestIC // Test Inner Class
{
public static void main(String[] args) {
OC2 outer = new OC2(); // 因为是成员内部类,所以必须用外部类的对象来构造内部类的对象,类似调用方法一样。
OC2.IC inner = outer.new IC();
inner.addSize();
}
}
内部类要在外部类实例的上下文中实例化:
例3:
this的一个作用是调用本类的其它构造器,另外一个作用就是做隐含参数的调用,代表当前的实例。完整的写法应该是 该类的类名.this 如下例:
本例阐述如何区分同名变量:
public class OC3 {
private int size; public class IC // Inner Class
{
private int size; public void addSize(int size) {
// 方法里的临时变量,当方法执行完自动消失
size++;
this.size++;
// 代表本类的当前对象,全称是IC.this.size++;
OC3.this.size++;
}
}
}
四、静态内部类(也叫顶层类)
1) 内部类的最简单形式;
2) 不能和外部类同名;
3) 被编译成一个独立的类文件;
4) 只能访问外部类的静态方法、静态实例变量(包括私有类型);
class OC4 {
private static int size; // 声明一个内部类 叫 "IC"
public static class SIC // Static Inner Class
{
public void addSize() {
// 访问外部类的属性
size++;
}
} } public class TestSIC // Test Static Inner Class
{
public static void main(String[] args) {
// 因为内部类是静态内部类,所以直接可以构造内部类的一个对象,与调用静态方法类似
OC4.SIC inner = new OC4.SIC();
inner.addSize();
}
}
五、方法内部定义内部类(又称局部内部类)
1) 定义在方法里;
2) 最少的一种内部类;
3) 和局部变量类似, 不能被定义为public,protected,private和static;
4) 只能访问final型局部变量。
class OC5 {
// 内部类访问,应该定义成final
public Object makeObject(final int i) {
class MIC // Methord Inner Class
{
int k = i; public String toString() {
return ("属性k :" + k);
}
}
return new MIC();
} public static void main(String[] args) {
OC5 oc = new OC5();
Object o = oc.makeObject(5);
System.out.println(o);
}
}
注意:在方法中定义的内部类的方法,不能访问外方法的运行时变量空间(line 10),可以访问外方法的非运行时变量空间(line 11)。
六、匿名内部类
1) 没有类名;
2) 没有class关键字;
3) 没有继承和声明;
4) 没有构造函数;
有时候定义一个类,并不需要提供名字。所以叫匿名类。
class OC6 {
// 多态,传递的参数应该是实现该接口的任何类产生的对象
public void testFly(Fly f) {
f.fly();
} public static void main(String[] args) { OC6 oc = new OC6();
// 画下划线的代码就是构造了实现Fly接口的某个类的对象,类名并不需要知道,只
// 知道该对象具有接口的功能就行。
oc.testFly(new Fly() {
public void fly() {
System.out.println("fly higher and higher");
}
});
}
} interface Fly {
public void fly();
}
匿名类具有广泛性。不只是对接口才有,对抽象类或者具体的类都适用。例如:
class OC7 {
public static void main(String[] args) {
System.out.println(new Person() {
public String toString() {
return "this is a person";
}
});
}
}
class Person {
}
注意:如果是接口或者抽象类的话,在匿名类里面必须实现接口或者抽象类里所有的抽象方法。如果是具体的类的话就没必要了,需要的话可以覆盖方法,不需要时可以不写任何代码。看下例:
class OC7 // Outer Class 7
{
public static void main(String[] args) {
// 这里其实是Person类里的一个子类,只不过该子类并没有扩展功能
System.out.println(new Person() {
});
}
} class Person {
public String toString() {
return "this is a person";
}
}
Java高级类特性(二)的更多相关文章
- Java高级类特性(一)
一.继承性 1)继承的使用:权限修饰符 class A extends B{}:2)子类:A 父类(基类 SuperClass):B3)子类继承父类后,父类中声明的属性.方法,子类都可以获取到明确:当 ...
- Java SE学习笔记 --->高级类特性 ---> toString() 方法
概述: toString() 方法在面向对象当中十分常见,使用频率很高,和equals() 方法一样,也是Object类中定义的方法. jdk中 源码: java.lang.Object类中ToStr ...
- Java【第八篇】面向对象之高级类特性
static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ...
- Java高级篇(二)——网络通信
网络编程是每个开发人员工具相中的核心部分,我们在学习了诸多Java的知识后,也将步入几个大的方向,Java网络编程就是其中之一. 如今强调网络的程序不比涉及网络的更多.除了经典的应用程序,如电子邮件. ...
- Java常用类(二)String类详解
前言 在我们开发中经常会用到很多的常用的工具类,这里做一个总结.他们有很多的方法都是我们经常要用到的.所以我们一定要把它好好的掌握起来! 一.String简介 1.1.String(字符串常量)概述 ...
- Java高级规范之二
二十一.提交java代码前应该检查是否有没用的语句,如:System.out.println(); jsp页面上面是否有alert调试信息 不规范示例:暂无 规范实例:暂无 解析:因为如果保留了有可能 ...
- 高级类特性----接口(intertface)
接 口 有时必须从几个类中派生出一个子类,继承它们所有的属性和方法.但是,Java不支持多重继承.有了接口,就可以得到多重继承的效果. 接口(interface)是抽象方法和常量值的定义的集合. 从本 ...
- 高级类特性----final关键字
final 关键字 在Java中声明类.属性和方法时,可使用关键字final来修饰. final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次. final标记的类不能被继承.提高安全性,提 ...
- 高级类特性----static关键字
static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用. 我们 ...
随机推荐
- HTML学习总结(作业五)
1:HTML简介 超文本标记语言,标准通用标记语言下的一个应用.是 网页制作必备的编程语言“超文本”就是指页面内可以包含图片.链接,甚至音乐.程序等非文字元素.超文本标记语言的结构包括“头”部分(英语 ...
- AI for VS ,美团创新之处分析
微软在2017中发布了VS Tools for AI,旨在提升用户对于深度学习的需求体验.AI组件可以让我们迅速构建和训练深度学习的Project,其功能主要有开发,调试和部署深度学习和人工智能的解决 ...
- PLSQL基础知识-图片
什么是PL/SQL?
- 使用Python对Twitter进行数据挖掘(Mining Twitter Data with Python)
目录 1.Collecting data 1.1 Register Your App 1.2 Accessing the Data 1.3 Streaming 2.Text Pre-processin ...
- pb数据导出
pb数据导出(一) 1.在窗口新建用户事件 ue_export 2.事件调用函数 gf_dw_to_excel(THIS.dw_dict) 3.写函数 :boolean lb_setborde ...
- Oracle OMF管理数据文件
1.什么是OMF? Oracle managed file的缩写,简单的理解,就是oracle自己管理自己的文件,可以是dbf,redolog 等等,具体可以参考官方文档Adiministrator中 ...
- Android-Java-饿汉式单例模式(内存图)
描述Single对象: package android.java.oop14; public class Single { // 默认构造方法 私有化 不让外界调用 private Single() ...
- .Wait()与.GetAwaiter()之间有什么区别
两者都是同步等待操作的结果差异主要在于处理异常.使用Wait,异常堆栈跟踪不会改变并表示异常时的实际堆栈,因此如果您有一段代码在线程池线程上运行,那么您将拥有类似的堆栈 ThreadPoolThrea ...
- Android 和 iOS 实现录屏推流的方案整理
一.录屏推流实现的步骤 1. 采集数据 主要是采集屏幕获得视频数据,采集麦克风获得音频数据,如果可以实现的话,我们还可以采集一些应用内置的音频数据. 2. 数据格式转换 主要是将获取到的视频和音频转换 ...
- JavaScript实现页面显示倒计时功能
下面是用JS中的日期函数和定时器完成的一个倒计时的例子,效果如图: 代码如下: <!DOCTYPE html> <html> <head> <title> ...