目录
Object类
Object是所有类的父类,也称超类。
自定义类可以不用显示的写出 extends Object。
在这之中需要了解的常用方法为 equlas toString
equlas在Java对象和类的使用中已经讲过
而toString 是对象的特定输出表现形式,可以对其重写,那么当输出该对象时,就是输出该方法中的信息,如果没有重写,则默认调用父类的toString方法也就是输出对象的hashcode
public class Demo{
@Override
public String toString() {
return getClass().getName() + "类重写了toString方法";
}
public static void main(String[] args) {
System.out.println(new Demo());
}
}
重写前
org.example.blog.Demo@2d554825
重写后
org.example.blog.Demo类重写了toString方法
继承
继承可以直接使用父类的非private成员或者对父类现有的功能进行扩展。例如某些行为或属性不是属于这个父类的,但子类和父类又有关联,那么这个时候就应该使用继承这个特性。
在Java中继承某一个类使用 extends 关键字,并且一个子类只能继承自一个父类。想要实现的话就可以间接继承,但是不推荐这种方式。
class A{
//doAnything...
}
class B extends A{
//doAnything...
}
class C extends B{
//doAnything...
}
权限
当重写父类方法时必须要比父类方法的权限修饰符要高,比如父类如果是protected的话,那子类重写方法的修饰符就必须比protected要高,所以子类就是public权限。如果怕不满足重写条件,可以使用@Override注解来帮你检查。
重写(Override)
重写父类方法时,如果要修改方法的返回值,也只能是父类方法返回值或其子类,下面的代码就是这样,由于ExampleClassOfSon是ExampleClass的子类,所以可以重写而不报错
package org.example.blog;
public class ExampleClass {
private String privateName = "Father";
public String publicName = "PublicFather";
static {
System.out.println("父类的静态代码块"); //父类静态代码块
}
{
System.out.println("父类的普通代码块"); //父类的普通代码块
}
public ExampleClass() { //父类构造方法
fatherPrivateMethod();
}
private void fatherPrivateMethod() {
System.out.println("类被初始化调用了父类的private方法");
}
protected void fatherProtectedMethod() {
System.out.println(getClass().getName() + "父类的protected方法");
}
public ExampleClass getExampleClass() {
return new ExampleClass();
}
public static void main(String[] args) {
ExampleClassOfSon ecos = new ExampleClassOfSon();
}
}
class ExampleClassOfSon extends ExampleClass{
static {
System.out.println("子类的静态代码块"); //子类的静态代码块
}
{
System.out.println("子类的普通代码块"); //子类的普通代码块
}
public ExampleClassOfSon() { //子类的构造方法调用的是本类的私有方法
sonPrivateMethod();
}
private void sonPrivateMethod() {
System.out.println("类被初始化调用了子类的private方法");
}
@Override
public void fatherProtectedMethod() {
System.out.println(getClass().getName() + "重写了父类的protected方法");
}
public void sonPublicMethod() {
System.out.println("SonPublicMethod");
}
@Override
public ExampleClassOfSon getExampleClass() {
// TODO 自动生成的方法存根
return new ExampleClassOfSon();
}
}
初始化顺序
在加载初始化顺序中
静态成员(代码块,字段) > 普通代码块(代码块,字段) > 构造方法,如果要调用有参只能显示的写出super(... , ...)
在上述代码中如果存在继承那么就会先初始化父类,所以运行结果如下:
父类的静态代码块
子类的静态代码块
父类的普通代码块
类被初始化调用了父类的private方法
子类的普通代码块
类被初始化调用了子类的private方法
对象的类型转换
向上转型
比如葡萄是水果的一种,那么葡萄也可以看作为是水果对象。
下面代码主方法中第一行就是在向上转型,因为葡萄是水果的一种,很显然是在将葡萄抽象化,让水果类的变量指针指向葡萄对象,在水果类中的方法来进行判断,这里并没有写死是因为这个时候只要是水果的子类都可以作为方法的参数,但是方法的具体实现写死了,可以根据具体写的类来一一判断(体现了多态机制)
package org.example.blog;
public class Fruits {
public static boolean isSweet(Fruits frutis) {
return (frutis instanceof Grape) ? true : false;
}
public static void main(String[] args) {
Fruits frutis = new Grape();
Fruits frutis1 = new Fruits();
System.out.println(Fruits.isSweet(frutis));
System.out.println(Fruits.isSweet(frutis1));
}
}
class Grape extends Fruits{
}
运行结果:
true
false
向下转型
当我们将具体化的事物或类抽象,总是能成功,因为具体化的类包含抽象类的大多数属性或行为,但如果将抽象化的事务具体化就会出大问题了。
具体化的包含范围或者实现都是很细致的,没有像抽象化的那么含糊。我们可以说葡萄是水果,但不能说水果就是葡萄,所以这个时候向下转型的时候就会出问题
public class Fruits {
public static void main(String[] args) {
Fruits f = new Grape();
Grape g = f; //error
Grape g = (Grape) f; //right
Grape grape = new Fruits(); //error
}
}
class Grape extends Fruits{
}
上述代码第一个向下转换的错误是没有显示的写出要转换的类型所以报错,因为这个 f 对象也有可能是其他的水果,但是这里的g对象类型是葡萄类。
第二个错误是不能将抽象具体化,这里与第一行代码不同的是第一行虽然是Fruits类,但是它指的却是Grape的对象,所以这里会报类转换异常。这些都是需要注意的点
instanceof关键字
语法
myInstance instanceof exampleClass
判断某个对象引用是否是 exampleClass的实例对象,返回值为布尔值
在向下转型的时候可以先用instanceof 关键字来判断一下,避免出现类转换异常
方法的重载
同一个类中,一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
只要返回值不同,其它都相同不算是重载。
多态
利用多态的机制可以使程序具有较好的扩展性,还可以对类作出通用的处理
抽象类和接口
抽象类
前面讲的继承和多态性质来举例。在继承的这个关系中,越是往上面的就越是抽象,比如葡萄继承水果,而水果又可以继承植物。多态中我们又只需要来实例化子类,所以在Java中抽象类不可以实例化出对象,就像植物不能具体出来,而它的子类可以。
定义抽象类的关键字是 abstract
被这个关键字修饰的类是抽象类、修饰的方法为抽象方法
抽象类中可以没有抽象方法,但类中一但有了抽象方法后这个类必须定义为抽象类
抽象类的抽象方法可以不用写出具体的实现,可以看作所有继承这个父类的共有方法(有这个行为但其实现却不一样,比如猫是喵喵喵叫,而狗是汪汪汪的叫,只需要定义一个抽象方法call()即可)。所以行为是子类根据自己独有的行为特征来分别实现的。
继承抽象类需要实现它的抽象方法。
package org.example.demo;
public abstract class Animal {
abstract void call();
void sleep(){
System.out.println("睡觉了");
}
}
class Cat extends Animal{
@Override
void call() {
System.out.println("喵喵喵");
}
}
class Dog extends Animal{
@Override
void call() {
System.out.println("汪汪汪");
}
}
接口
在抽象类的这个问题中可能会发现一点问题,如果有一些动物的类可能不需要这个call()方法呢,但是要继承这个抽象类就不得不实现这个方法,如果不需要这个方法又要一个新的普通A类来继承抽象类,实现了过后再用一个B类来继承A这样就不用硬实现抽象类中的抽象方法,因为A类要继承就不得不实现。但就为了一个抽象方法去创建一个过渡类,势必会造成代码的冗余。
接口其实可以看做抽象类的延伸,可以将它视为抽象类,我们可以使用接口来对其进行扩展。
优化前
优化后
接口的定义关键字为 interface
语法:
interface 接口名{
}
package org.example.demo;
public interface CallTest {
void call();
}
在接口中的成员(方法和字段)都是public 且不允许定义为private 和protected
字段默认都是是 static 和final 的
在Java8以前接口中的方法没有方法体,但在Java8之后接口可以有默认的方法实现。
但要加上 default 这个关键字
package org.example.demo;
public interface CallTest {
void call();
default void test(){
System.out.println("callTest");
}
}
接口与继承
Java不允许多继承,但是可以实现多个接口,继承接口后就必须对抽象方法进行实现
实现接口语法:
class 类名 implements 接口1,接口2,...... ,接口n
接口实现另外一个接口:
interface 接口名 extends 接口
interface interfaceTest{
}
interface interfaceTest2 extends interfaceTest{
}