Java面向对象
静态变量
静态变量被类中的所有对象共享。静态方法不能访问类中的实例成员。
如果想让一个类中的所有实例共享数据,就要使用静态变量(static variable),也称类变量(class variable)。静态变量将变量值存储在一个公共的内存地址。因为是公共的,所以如果某一个对象修改了静态变量的值,那么同一个类的所有对象都会受影响。
无需创建类的实例就可以调用静态方法(static method)。
要声明一个静态变量或定义一个静态方法,就需要中声明中加上修饰符static。
1 | static int numberOfObjects; |
Math类中所有的方法都是静态的。main方法也是静态方法。
类中的常量是被该类的所有对象所共享的。因此,常量应该声明为final static
:
1 | final static double PI = 3.1415; |
实例方法可以调用实例方法和静态方法,访问实例数据域或静态数据域。静态方法只能调用静态方法和访问静态数据域。因为静态方法和静态数据域不属于某个特定的对象。
一个常见的设计错误就是将一个本应该声明为静态的方法声明为实例方法。例如
factorial(int n)
应该定义为是静态的,因为它不依赖于任何具体的实例。
可见性修饰符
可见性修饰符可以用于确定一个类以及它的成员的可见性。
可以在类、方法和数据域前使用public修饰符,表示它们可以被任何其他的类访问。如果没有可见性修饰符,则默认类、方法和数据域是可以被同一个包中的任何一个类访问的。这称作包私有(package-private)或包内访问(package- access)。
包可以用来组织类。用下面的语句:
1 package packageName;如果定义时没有包,就表示把它放在默认包中。
Java建议最好把类放入包中,而不要使用默认包。
private修饰符限定方法和数据域只能在它自己的类中被访问。
如果一个类没有被定义为公共类,则它只能在同一个包内被访问。
不可变对象和类
可以定义不可变类来产生不可变对象。不可变对象的内容不能被改变。
通常创建一个对象后,它的内容是允许之后改变的。有时候也需要创建一个一旦创建其内容就不能再改变的对象。我们称这种对象为一个不可变对象(immutable object),而它的类就是不可变类(immutable class)。例如,String类就是不可变的。如果把set方法去掉,也可能变为不可变类。
如果一个类是不可变的,那么它的所有数据域必须都是私有的,而且没有对任何一个数据域提供公共的set方法。但是这只是充分条件。反例如下:
1 | public class Student{ |
getDateCreated方法返回的是一个引用。通过这个引用可以改变getDateCreated的值:
1 | public class Test{ |
变量的作用域
实例变量和静态变量的作用域是整个类,无论变量是在哪里声明的。
一个类的实例变量和静态变量称为类变量(class’s variables)或数据域(data field)。在方法内部定义的变量是局部变量。
类变量只能声明一次,但是在一个方法内不同的非嵌套块中,可以多次声明相同的变量名。
如果一个局部变量和一个类变量具有相同的名字,那么局部变量优先,而同名的类变量被隐藏。
this引用
关键字this引用对象自身。它也可以在构造方法内部用于调用同一个类的其他构造方法。
关键字this是指向调用对象本身的引用名。可以用this关键字引用对象的实例成员。
在引用隐藏数据域(例如set方法将数据域名作为参数名)以及调用一个重载的构造方法的时候,this引用是必须的。
1 | public class Circle{ |
继承和多态
面向对象编程允许从已经存在的类中定义新的类,这称为继承。
继承能够避免冗余并使系统更易于理解和易于维护。
父类和子类
如果类C1扩展自另一个类C2,那么称C1为次类(subclass),C2为超类(superclass)。超类也称父类(parent class)或基类(base class),次类又称子类(child class)、扩展类(extended class)或派生类(derived class)。子类从它的父类中继承可访问的数据域或方法,还可添加新数据域和新方法。
父类中的私有数据域是不能被子类访问的,但是可以用get和set方法。
extends
一个Java类只可能直接继承自一个父类,这种限制称为单一继承(single inheritence)。如果使用extends关键字来定义一个子类,它只允许有一个父类。然而,多重继承是可以直接通过接口来实现的。
super
关键字super指代父类,可以用于调用父类中的普通方法和构造方法。
调用父类的构造方法
构造方法用于构建一个类的实例。不同于属性和普通方法,父类的构造方法不会被子类继承。它们只能使用关键字super从子类的构造方法中调用。
调用父类构造方法的语法是:super 或 super(parameters)
语句super()
调用父类的无参构造方法,而语句super(arguments)
调用与参数匹配的父类的构造方法。它们必须出现在子类构造方法的第一行,这是显示调用父类构造方法的唯一方式。在子类中调用父类构造方法的名字会引起一个语法错误。例如:
1 | public CircleFromSimpleGeometricObject(double radius,String color,boolean filled){ |
原来是:
1 | public CircleFromSimple(double radius,String color, boolean filled){ |
构造方法链(IMPORTANT 354 355)
调用父类的方法
语法是:super.methodName(parameters)
例如:
1 | public void print(){ |
方法重写
要重写一个方法,需要在子类中使用和父类一样的签名以及一样的返回值类型来对该方法进行定义。
子类从父类中继承方法,有时子类需要修改父类中定义的方法的实现,这称作方法重写(method overriding)。
例如GeometricObject类中的toString方法返回表示几何对象的字符串。这个方法可以被重写,返回表示圆的字符串。
1 | public class Son extends Father{ |
Son使用super.toString()访问Father中的toString方法,但是GrandSon不能使用super.super.toString()访问Father中的toString方法。这是一个语法错误。
值得注意的是:
仅当实例方法是可访问时,它才可被覆盖。因为私有方法在它的类本身以外是不能访问的,所以它不能被覆盖。如果子类中定义的方法是父类中私有的,那么这两个方法完全没有关系。
与实例方法一样,静态方法也能被继承。但是静态方法不能被覆盖。如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏。可以使用语法:父类名.静态方法名(SuperClassName.staticMethodName)调用隐藏的静态方法。
重载意味着使用同样的名字但是不同的签名来定义多个方法。
重写意味着在子类中提供一个对方法的新的实现。
- 方法重写发生在通过继承而相关的不同类中;方法重载可以发生在同一个类中,也可以发生在由于继承而相关的不同类中。
- 方法重写具有相同的签名和返回值类型;方法重载具有相同的名字,但是不同的参数列表。
为了避免错误,可以使用一个特殊的Java语法,成为重写标注(override annotation),在子类的方法前面放一个@Override。例如:
1 | public class Son extends Father{ |
该标注表示被标注的方法必须重写父类的一个方法。如果具有该标注的方法没有重写父类的方法,编译器将会报错。
Object类及其toString()方法
Java中的所有类都继承自java.lang.Object
类。
如果在定义一个类时没有指定继承性,那么这个类的父类就被默认为是Object。例如下面两个类的定义是一样的:
1 | public class ClassName{ |
toString()方法的签名是:public String toString()
默认情况下,它返回一个由该对象所属的类名、@符号以及该对象十六进制形式的内存地址组成的字符串。但是通常这个结果没什么用。所以考虑重写这个方法:
1 | public String toString(){ |
1 | public class TestToString{ |
输出是:java.lang.Object@dcf3e99
多态
多态意味着父类的变量可以指向子类对象。
首先定义两个属于:子类型和父类型。一个类实际上定义了一种类型,子类定义的类型称为子类型(subtype),父类定义的称为父类型(supertype)。
继承关系使一个子类继承父类的特征,并且附加一些新特征。
本文作者 : preccrep
原文链接 : https://preccrep.github.io/2021/03/14/Java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!