Java基础知识整理5

类的继承:
继承通过extends关键字实现:
修饰符 class 类名:extends 父类
{类定义部分}
Java子类不能获得父类的构造器
Java只能有一个直接父类,可以有无限间接个父类
Java.lang.Object是所有类的父类
子类与父类同名方法的现象叫做重写。
重写遵循“两同两小一大”规则,两同即方法名相同,形参列表相同,两小是子类方法返回值类型应比父类的返回值类型更小或相等,一大指的是子类方法的访问权限应比父类更大或相等,覆盖方法和被覆盖方法都是类方法或者实例方法
当子类重写父类方法后,子类将无法访问父类的方法,但还可以在子类方法调用父类中被覆盖的方法。
如果父类方法具有private访问权限,则该方法对其子类是隐藏的,子类无法调用重写该方法,如果在子类中写了一个与父类private相同的方法,依然不是重写,只是在子类重新定义了一个新方法。
父类实例的super的引用:
Super是Java的关键字,他是父类的默认引用。系统会隐式的创建该父类的对象,只要有一个子类对象存在,则一定存在一个与之对应的父类对象,
父类和子类之间也会发生重载,因为子类会获得父类的方法,如果子类定义了一个父类有相同方法名,但参数列表的不同的方法,就会形成子类和父类方法的重载。
如果子类定义了父类的同名的属性,也会发生自类属性覆盖夫类属性的情形,可以通过super来访问父类被覆盖的属性。(C#里是base)
如果被覆盖的是类属性,在子类的方法中则可以通过父类名作为调用者来访问被覆盖的类属性。
如果我们在某个方法中访问名为a的属性,但没有显式指定调用者,系统查找a的循序为:
1.查找该方法中是否含有名为a的局部变量。
2.查找当前类是否包含名为a的属性
3.查找a的直接父类中是否含有a的属性,依次上述到父类,直到Java。Lang,object类如果最终没有找到则出现编译错误。
调用父类的构造器:
使用super调用父类构造器也必须出现在子类执行体的第一行。不管我们是否使用super调用来执行父类父类构造器的初始化代码,子类构造器总会调用父类的构造器一次,子类构造器调用父类构造器分以下几种情况:
1.子类构造体执行体的第一行使用super显示调用父类构造器,系统根据super调用里传入的实参列表调用父类对应的构造器。
2.子类构造器执行体的第一行代码使用this显式调用里传入的实参列表调用本类另一个构造器。执行本类另一个构造器时即会调用父类构造器。
3.子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参的构造器。

多态性:
子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无需任何类型转换,或者被称为向上转型,向上转型由系统自动完成。
当把一个子类对象直接赋给父类引用变量,父类 变量名=new 子类();当运行时调用该变量的方法时,其方法总是表现出子类方法的行为,而不是父类的方法行为,这将出现相同类型的变量,执行同一个方法时呈现出的不同行为特征,这就是多态。
引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时所具有的方法,因此编写Java时,引用变量只能调用声明改变量时所用类里包含的方法。
父类 变量名=new 子类();只能调用父类的方法,而不能调用子类的,但是会表现出子了的行为。
通过引用变量来访问包含实例属性时,系统总是试图访问它编译时类所定义的属性,而不是运行时他所定义的属性
引用变量的强制类型转换:引用变量只能调用它编译时类型的方法,而不去调用它运行时类型的方法,即使使他实际所引用对象确实包含该方法,如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制类型转化成运行时类型,强制类型转换需要借助类型转换符。
类型转换运算符还可以将一个引用类型变量转换成子类类型。
注意:
1.基本类型之间的转换只能在数值类型之间进行,但不能和布尔类型进行转换。
2.引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行强制类型转换,否则编译时会出现错误,如果试图把一个父类实例转换子类类型,则必须这个对象实际上是子类实例才行(即编译时类型为父类,运行时类型是子类类型)否则将在运行时引发异常;
考虑到进行强制类型的转换时可能会出现异常,因此进行类型转换之前应先通过instanceof运算符来判断是否可以转化成功。避免程序出现异常。
当把子类对象赋给父类引用变量时,被称为向上转型,这种转型总是可以成功的,这也从另一个侧面证实了,子类是一种特殊的父类。这种转型只是表明这个引用变量的编译类型是父类,但实际执行方法时,依然表现出子类对象的行为方式。但把一个父类的对象赋给子类引用变量时,就需要进行强制类型转化,而且还可能在运行时产生ClassCastException异常,使用instanceof运算符可以让强制类型转换更安全。
Instanceof运算符的前一个操作数通常是一个引用类型的变量,后一个操作数通常是一个类(也可以是接口,也可以把接口理解成一个特殊的类),它用于判断前面的对象是否是后面的类,或者其子类,实现类的实例,如果是返回true。
在这里插入图片描述
在使用instanceof时,INstance运算符前面的操作数的编译时类型要么与后面的类相同,要么是后面类的父类,否则会引起编译错误。
Instanceof的作用就是在执行强制类型转化之前,首先判断前一个对象是否是后一个类的实例,是否可以转化成功。
继承与组合:
为了保证父类良好的封装性,不会被子类随意改变,遵循一下规则:
1.尽量隐蔽父类的内部数据,尽量把所有的属性都设置成private访问类型,不用让子类直接访问父类的属性。
2.不要让子类可以随意访问,修改父类的方法。父类中那些仅为辅助其他的工具方法,应该使用private访问修饰符,让子类无法访问该方法;如果父类中的方法需要被外部类调用,必须以public修饰,但又不希望子类重写该方法,可以使用final修饰符来修饰该方法。如果希望父类的某个方法被子类重写,但不希望被其他类自由访问,可以使用protected来修饰该方法。
3.尽量不要在父类构造器中调用将要被子类重写的方法。
到底何时从父类派生新的子类?
1.子类需要郑家额外的属性,而不仅仅是属性值的改变。
2.子类需要增加自己独有的行为方式(包括增加新的方法或重写父类方法)。
利用组合实现复用:
对于继承而言,子类可以直接获得父类的public方法,程序使用子类时,将可以直接访问该子类从父类那里继承到的方法;而组合则是把旧类对象作为新类对象的属性嵌入,用以实现新类的方法,而不能看到嵌入对象的方法,因此,通常需要在新类里使用private修饰嵌入旧类对象。
继承是对已有的类进行改造,以此获得一个特殊的版本,就是将一个较为抽象的类改造成能适应于某些特定需求的类。继承要表达的是一种(is a)的关系,而组合表达的是(has -a)的关系。
初始化块(也可以对对象进行初始化操作):
一个类里可以有多个初始化块,相同类型之间的初始化块之间有顺序,前面定义的初始化块先执行,后面定义的初始化块后执行。初始化块只能是static,使用static修饰的初始化块称为静态初始化块,初始化块里的代码可以包含任何可执行的语句。
普通初始化块,声明实例属性的默认值都可认为是对象的初始化代码,他们的执行顺序与源程序中排列顺序相同。
静态初始化块是类相关的,用于整个类进行初始化处理,通常用于对类的属性执行初始化处理。静态初始化块不能对实例属性进行初始化处理。

面向对象深入
Java提供了final关键字来修饰变量,方法和类,系统不允许为final变量重新赋值,子类不允许覆盖父类的final方法,final类不能派生子类。通过使用final关键字,允许Java实现不可变类,不可变类会让系统更加安全。
Abstract和interface两个关键字分别定义抽象类和接口,抽象类和接口都是多个子类中抽象的出来的共同特征。但抽象类主要作为多个类的模板,而接口则定义了多类应该遵守的规则。枚举是一种不能自由创建对象的类,枚举类的对象在定义的时候就已经固定下来了。
基本数据类型的包装类:
Java是面向对象的编程语言,包含了八种基本数据类型,这八个基本数据类型不支持面向对象编程机制,基本数据类型的数据也不具备“对象”的特性:没有属性方法可以被调用。
为了解决8个基本数据类型的变量不能当成Object类型变量的使用问题,Java提供了包装类(Wrapper class)的概念,为八个基本数据类型分别定义了相应的引用数据类型,并称之为基本数据类型的包装类。
在这里插入图片描述
把基本数据类型变量包装成包装类实例是通过对应的包装类的构造器来实现的,除了Character之外,还可以通过传入一个字符串参数来构建包装类对象。
如果希望获得包装类对象中包装的基本变量类型,则可以使用包装类提供的XXXvalue()实例方法。包装类还可以实现基本类型变量和字符床之间的转化,除了character之外的所有包装类都提供一个parseXXX(string s)静态方法,用于将一个特定的字符串转换成基本类型变量,在string类里提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串。
处理对象:
打印对象和toString方法:
重写tostring:
在这里插入图片描述
和equals比较运算符:
Java中判断两个变量是否相等有两种方式:一种是利用
运算符,另一种是利用equals方法。
当使用来判断两个变量是否相等时,如果2个变量是基本变量,而且都是数值型,则只要两个变量的值相等,使用判断就会返回true.
但对于两个引用类型的变量,必须他们指向同一个对象时,==判断才会返回true。==不可比较类型上没有父子关系的两个对象。
String已经重写了Object的equals方法,String的equals方法判断两个字符串相等的标准是:只要两个字符串所包含的字符序列相同,通过equals()比较将返回true;否则将返回false.
在这里插入图片描述
通常而言:
1.自反性:对任意x,x.equals(x)一定返回true
2.对称性:对任意的x和y,如果y.equals(x)返回true,则x.equals(y)也返回true
3.传递性:对任意x,y,z,如果有x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true。
4.一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论怎样调用x.equals(y)多少次,返回的结果都应该保持一致,要么一直是true,要么一直是false。
5.对任何不是Null的x,x.equals(null)一定返回false。
由于instanceof运算符的特殊性,当前面对象是后面类的实例或其子类的实例都将返回true,所以实际上重写equals()方法时判断2个对象是否为同一个类的实例时使用instanceof是有问题的,改为一下代码合适if(obj!=null&&bj.getClass()==person.class),用到了反射。
在这里插入图片描述
类成员:static关键字修饰的成员就是类成员,前面已经介绍的类成员有类的属性,类方法,静态初始化块等三个成分,static关键字不能修饰构造器。Static修饰的类成员属于整个类,不是属于单个实例的。
在Java类里只能包含属性,方法,构造器,初始化块,内部类和枚举类等六种成员,以static修饰的成员就是类成员。类成员属性属于整个类,而不是属于单个对象。类属性即可以通过类来访问,也可以通过类的对象来访问。对象根本不包括对应类的类属性。
通过对象访问类属性只是一种假象,通过对象访问的依然是该类的属性,可以这样理解:当通过对象来访问类属性时,系统会在底层转换为通过该类来访问类属性。
(c#不允许通过对象访问类属性,对象只能访问实例属性;类属性必须通过类来访问)
由于所有对象实际上并不保持类属性,类属性是由该类来保持的,同一个类的所有对象访问类属性时,实际访问的是该类所持有的属性。因此从程序上来看,即可看到同一类的所有实例的类属性共享同一块内存区。
Null对象可以访问它所属类的类成员。如果一个null对象访问实例成员(包括属性和方法),将会引发NullPointerException异常,因为null表明实例根本不存在,既然实例不存在,那么他的属性和方法也不存在。一旦类初始化结束完成,静态初始化块将永远不会获得执行的己会。
对于static关键字而言,有一条规则:类成员(包括方法,初始化块,内部类和枚举类)你能访问实例成员(包括属性,方法,初始化块,内部类和枚举类)。因为类成员是属于类的,;类成员的作用域比实例成员作用域更大,完全可能出现类成员已经初始化完成,但实例成员还没有初始化,如果允许类成员访问实例成员将会引起大量的错误。
单例类:
如果一个类始终只能创建一个对象实例,则这个类被称为单例类。
良好的封装原则:
1.一旦把该类的构造器隐藏起来,则需要提供一个public方法作为类的访问点用于创建该类的对象,且该方法必须是使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。
2.该类必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,也就无法保证只创建一个对象。为此该类需要使用一个属性来保存曾经创建的对象,因为该属性需要被上面的静态方法访问,故该属性必须使用static修饰。
在这里插入图片描述
Final修饰符:可以用于修饰类,变量,方法,final关键字有点类似c#里的sealed关键字,他表示它修饰的类,方法和变量不可变。
Final变量:final修饰变量时,表示该变量一旦获得了初始值之后就不可被改变,final即可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量,形参,一旦获得初始值之后,该final变量就不可被重新赋值。
Final修饰成员变量:
成员变量是随类初始化或对象初始化而初始化的。
类属性:可以在静态初始化块中,声明属性时指定初始值。
实例属性:可以在非静态初始化块,声明该属性的,构造器中指定初始值。
Final修饰的类实例属性,要么在定义该属性时指定初始值,要么在普通初始化块,或构造器中为该属性指定初始值,但需要注意的是,如果普通初始化块已经为某个实例属性指定了初始值,则不能再在构造器中位该实例属性指定初始值。Final修饰的类属性,要么在定该属性时指定初始值,要么在静态初始化块中为该属性指定初始值。实例属性不能在静态初始化块中指定初始值,因为静态初始化块是静态成员,不可访问实例属性-------——非静态成员,类属性不能在普通初始化块中指定初始值,因为类属性在类初始化阶段已经被初始化了。
Final成员变量(包括实例属性和类属性)必须由程序员显式的进行初始化,系统不会对final成员进行隐式的初始化,所以,如果打算在构造器,初始化块中对final成员变量进行隐式的初始化,则不要在初始化之前就访问成员变量的值。
Final修饰局部变量
系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。因此使用final修饰局部变量时即可以在定义时指定默认值,也可以不指定默认值。
如果是final修饰的局部变量在定义时没有指定默认值,则可以在后面的代码中进行对该final变量赋初始值,但只能一次,不能重复赋值;如果已有初始值,则后面不能对该变量赋值。

猜你喜欢

转载自blog.csdn.net/qq_43467548/article/details/94383224