什么是继承
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
继承的语法、关键字extends
修饰符 类名 extends 被继承的类{
//类定义部分
}
//父类
class Subclass{}
//子类
class Subclass extends ParentClass{
}
-
继承(inheritance)通过extends关键字来实现,修饰符如果是public,则在整个项目中可见;若无public修饰符,则该类只在当前包可见;不可以使用private和protected修饰类
-
继承是面向对象的三大特征之一,是Java中实现代码重用的重要手段之一。Java中只支持单继承,即每个类只能有一个直接父类。
-
所有的Java类都直接或间接地继承了java.lang.Object类。Object类是所有类的祖先。在定义一个类时,如果没有使用extends关键字,那么这个类直接继承Object类。
在Java中,子类可以从父类中继承到哪些?
-
继承public和protected修饰的属性和方法,无论子类和父类是否在同一个包里。
-
继承默认权限修饰符修饰的属性和方法,但子类和父类必须同在一个包里。
-
无法继承private修饰的属性和方法。
-
无法继承父类的构造方法
super
Java中的super关键字是一个引用变量,用于引用父类对象。
super的三种用法:
1、普通的直接引用
与this类似,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。
2、子类中的成员变量或方法与父类中的成员变量或方法同名。
3、引用构造函数
super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)
class Person
{
void message()
{
System.out.println("This is person class");
}
}
class Student extends Person
{
void message()
{
System.out.println("This is student class");
}
// Note that display() is only in Student class
void display()
{
// will invoke or call current class message() method
message();
// will invoke or call parent class message() method
super.message();
}
}
class Test
{
public static void main(String args[])
{
Student s = new Student();
s.display();
}
}
输出:
This is student class
This is person class
在上面的例子中,我们已经看到,如果我们只调用方法message(),那么当前的类message()被调用,但是使用super关键字时,超类的message()也可以被调用。
注意事项:
1.调用super()必须是类构造函数中的第一条语句。
2.如果构造函数没有明确调用超类构造函数,那么Java编译器会自动向超类的无参构造函数插入一个调用。如果超类没有没有参数的构造函数,你会得到一个编译时错误。对象 确实有这样的构造函数,所以如果Object是唯一的超类,那就没有问题了。
3.如果子类构造函数调用其超类的构造函数,无论是显式还是隐式调用,您都可能认为调用了整个构造函数链,并返回到Object的构造函数。事实上,情况就是如此。它被称为构造函数链。
重写和继承关系中的构造方法
子类重写父类方法
- 先创建Dog.java父类
public class Dog {
public void run()
{
System.out.println("跑");
}
public void jump()
{
System.out.println("跳");
}
public void sit()
{
System.out.println("坐");
}
- 再创建Cat.java子类
/*
Cat 子类继承Dog父类,并对父类的run()方法进行重写
*/
public class Cat extends Dog{
public void run()
{
super.run(); //使用super关键字调用父类方法
System.out.println("吃饭");
System.out.println("睡觉");
}
- 在Main.java中调用
public class Main {
public static void main(String[] args)
{
Dog dog = new Dog(); //创建父类对象
dog.run();
dog.jump();
dog.sit();
Cat cat = new Cat (); //创建子类对象
cat.run();
}
}
输出:
跑跳坐
跑吃饭睡觉
注意事项:
-
重写方法和被重写方法必须具有相同的方法名。
-
重写方法和被重写方法必须具有相同的参数列表
-
重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类
-
重写方法不能缩小被重写方法的访问权限
Java单例模式的五种实现方式
1、饿汉式(线程安全,调用效率高,但是不能延时加载):
public class ImageLoader{
private static ImageLoader instance = new ImageLoader;
private ImageLoader(){}
public static ImageLoader getInstance(){
return instance;
}
}
一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式。但是问题也比较明显。单例在还没有使用到的时候,初始化就已经完成了。也就是说,如果程序从头到位都没用使用这个单例的话,单例的对象还是会创建。这就造成了不必要的资源浪费。所以不推荐这种实现方式。
2、懒汉式(线程安全,调用效率不高,但是能延时加载):
public class SingletonDemo2 {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
private static SingletonDemo2 instance;
//构造器私有化
private SingletonDemo2(){}
//方法同步,调用效率低
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance=new SingletonDemo2();
}
return instance;
}
}
3.Double CheckLock实现单例:DCL也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题,不建议使用):
public class SingletonDemo5 {
2 private volatile static SingletonDemo5 SingletonDemo5;
3
4 private SingletonDemo5() {
5 }
6
7 public static SingletonDemo5 newInstance() {
8 if (SingletonDemo5 == null) {
9 synchronized (SingletonDemo5.class) {
10 if (SingletonDemo5 == null) {
11 SingletonDemo5 = new SingletonDemo5();
12 }
13 }
14 }
15 return SingletonDemo5;
16 }
17 }
4、静态内部类实现模式(线程安全,调用效率高,可以延时加载)
public class SingletonDemo3 {
private static class SingletonClassInstance{
private static final SingletonDemo3 instance=new SingletonDemo3();
}
private SingletonDemo3(){}
public static SingletonDemo3 getInstance(){
return SingletonClassInstance.instance;
}
}
5、枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)
public enum SingletonDemo4 {
//枚举元素本身就是单例
INSTANCE;
//添加自己需要的操作
public void singletonOperation(){
}
}
如何选用:
-单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉。
-单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式。