继承(extends)
继承是面向对象程序设计中另一个重要的特征,继承即由一个类从另一个通过extends关键字继承,被继承的类称之为父类(也叫超类),继承的类称之为子类(扩展类);继承可以使得类的扩展性提高,减少重复代码的编写。
[<修饰符>] class 子类 extends 父类 {
}
实例:
父类Father:
public class Father {
public String name;
public int age;
public void coding() {
System.out.println(name + "超级会写代码");
}
public void love(){
System.out.println("情场高手!!!!");
}
}
子类Son:
public class Son extends Father{
//通过代码表现:动物 猫 老鼠之间的关系
public static void main(String[] args) {
Son s = new Son();
s.name = "刘德华";
s.coding();
s.love();
}
}
继承优点
- 子类可以直接通过对象调用父类中的非私有元素
- 子类还可以在父类的基础上扩展新的功能
继承注意事项
- 子类只能继承父类的属性和方法,不能继承构造器
- 子类对象构造之前会先执行父类的构造器(如果存在多重继承,则从最顶层父类构造器开始执行)
- 一个父类可以存在多个子类,但是一个子类只能存在一个父类
- Java不允许多继承,但是支持多重继承
- 被protected修饰的元素可以在子类中被访问(即便父类和子类不在同一个包)
重写(override)与重载(overload)
Java中允许方法的重写和重载,重写和重载是多态的一种实现策略:
- 重载(overload):一个类中存在多个同名的方法
- 重写(override):一个子类对父类的方法覆盖
重写
当父类中的方法实现满足不了子类需求时,此时子类中可以编写一个与父类方法一模一样的方法,对父类的方法进行覆盖,该过程称之为方法的重写(Override).
案例:
Animal类
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//动物咆哮(不同的动物发出的叫声一样,因此无法一概而论)
public void bark() {
System.out.println(name + "哇哇哇哇啊啊...");
}
}
Dog类
public class Dog extends Animal{
//对bark方法重写
public void bark() {
System.out.println(getName() + "汪汪汪。。。");
}
}
Cat类
public class Cat extends Animal{
//对bark方法重写
public void bark() {
System.out.println(getName() + "喵喵喵。。。");
}
}
重载
重载指的是在同一个类中,或者存在继承关系的多个类中,存在同名的方法,这些方法的名称一模一样,但是方法的参数个数 ,类型,顺序任意有一项不一致,具备以上特点的方法统一称之为重载方法。
重载的条件:
- 方法名称必须一致
- 参数的个数、顺序和类型任意有一项不一致
- 重载与返回值无关
public class Driver {
public void driven(Car c) {
}
public void driven(Bus b) {
}
public void driven(Bike b) {
}
public void driven(Plane p) {
}
}
关于方法的重载和重写的区别?
- 重写必须存在继承关系,由子类重写父类的方法,重载无要求
- 重写的方法名称,参数类型必须和父类一致;重载必须要保证方法的参数列表任意有一项不一致
- 重载与返回值无关
装箱拆箱
在JDK1.5之后,引入了装箱和拆箱机制,用于实现基本类型数据和对应的封装类型之间的自动转换。
- 装箱(boxing):将基本类型的数据值包装为对应的引用类型对象
- 拆箱(unboxing):将封装类型中的基本数据值拆成为基本数据类型
//装箱(boxing)
Integer i = 10;
//拆箱(unboxing)
int j = new Integer(10);
设计模式之-单例模式(Singleton)
设计模式即在软件开发历史中,经过不断迭代和演变形成的一套针对于某些特定问题,所提供的的专门的解决方案;在java中常见的设计模式包含23种,比如:
- 单例模式
- 模板方法
- 简单工厂
- 适配器模式
- 观察者模式
- 代理模式
- 装饰模式
- …
单例模式概述
单例模式也称之为单态模式,单子模式等;指的是在程序运行的过程中始终只存在一个对象(实例)。单例模式的表现形式又分为两种:
- 懒汉式(需要的时候才创建:以时间换空间的概念)
- 饿汉式(类加载即创建:以空间换时间的概念)
应用场景
单例模式的使用场景十分广泛,比如:数据库连接池,字符串常量池,线程池等资源池,比如日历类,Runtime类等常见与系统环境交互相关的类。
懒汉式
懒汉式即需要实例对象时才创建:
/**
* 单例模式 - 懒汉式(需要时才创建)
* @author mrchai
*/
public class Singleton {
//实例变量(成员变量,全局变量)
//类变量
private static Singleton instance; //15db9742
//构造器私有化,不允许外界随意访问(不允许随便创建对象)
private Singleton() {}
public static Singleton newInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式
饿汉式即类加载时就创建实例对象
/**
* 单例模式-饿汉式(类加载即创建对象)
* @author mrchai
*
*/
public class Singleton2 {
private static Singleton2 instance = new Singleton2();
private Singleton2() {
public static Singleton2 newInstance() {
return instance;
}
}
抽象类(abstract class)
java中凡是普通类都具备实例化对象的能力,因为一个类具备了实例化对象的一些必要信息,比如属性,比如行为;但是有些时候,当一个类中信息不足以描述一个具体对象时,此时该类就应该考虑定义为抽象类。
java中的抽象类中所包含的信息,不足以描述一个具体对象,抽象类的内部成分由以下信息构成:
- 属性
- 构造器
- 行为
- 已实现
- 未实现(抽象方法)
抽象类基本案例
特点
- 抽象类必须使用abstract修饰
- 抽象类存在构造器,但是无法实例化(可以使用匿名内部类创建子类对象)
- 抽象类中通常会包含抽象方法
- 抽象类的存在一般是需要由子类继承,子类一旦继承了抽象类,则子类必须实现父类中的抽象方法(除非子类也是抽象类)
- 抽象类允许继承其他抽象类,但是也只能单继承,可以通过多重继承的方式实现多继承
- abstract只能修饰类和方法
案例:
/**
* 抽象类
* @author mrchai
*/
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//抽象方法
public abstract void eat();
public abstract void sleep();
}
}
Dog类:
public class Dog extends Animal{
//实现方法
public void eat() {
System.out.println("吃骨头");
}
//实现方法
public void sleep() {
System.out.println("睡狗屋");
}
}
接口(interface)
接口是一种比抽象类更纯粹的抽象;因为内部只能够存在常量以及未实现的方法(JDK8以前),接口通常用于定义一些未实现方法的集合,但是不对法方法具体实现,具体的实现通常是由实现类完成,接口具备以下特征:
- 接口不存在构造器,因此无法实例化
- 接口允许继承接口,可以同时继承多个接口
- 一个类可以实现多个接口,但是必须要同时实现所有接口的方法(除非抽象类)
- 抽象类也实现接口
- JDK8中对接口有其他新增特性(默认方法,静态方法,函数式接口)
语法
/**
* 接口即标准
* USB接口:只是一种标准的定义,不包含具体实现
* 一流的公司定义标准 做架构
* 二流的公司卖服务 写框架
* 三流的公司卖产品 写CRUD
*
* 定义一个接口
* @author mrchai
*/
public interface Flyable {
//默认编译器会将以下代码完善成: public static final String msg = "hello";
String msg = "hello";
//编译器会将以下代码自动完善:public abstract void fly()
void fly();
}
抽象类和接口区别
- 抽象类是一种类结构,接口是一种行为准则
- 抽象类中包含类的所有特征,同时包含抽象方法;接口只能有常量和未实现的方法
- 抽象类由子类通过extends进行扩展;接口由实现类通过implements扩展
- 子类只能继承一个抽象类;一个类可以实现多个接口
- 抽象类能且只能继承一个父类;接口可以继承多个接口,接口不能继承任何类
- 抽象类是对类(名词)的一种抽象;接口是对行为(动词,形容词)的抽象,接口是一种特殊的抽象类
多态详解(polymorphism)
多态的体现包含两种层面:
- 方法层面(重写和重载)
- 属性层面
- 父类引用指向子类对象
- 子类引用指向父类对象(原本就是子类)
多态也称之为动态绑定:在运行期间动态为引用变量绑定具体的对象数据
内部类
通常一个java文件中只能包含一个public class,但是可以在一个java类的内部中嵌套其他的java类,这种类称之为内部类,也叫嵌套类,或者类属类,比如:
public class People {
public void m(){
}
public static void main(String[] args) {
}
//内部类
class Body{
}
}
//不是内部类,和People是平级的关系
//class Head{
//}
以上就是一个典型的内部类,生成的字节码文件如下:
People$Body.class
People.class
java中的内部类包含以下几种定义方式:
- 成员内部类
- 局部内部类
- 静态内部类
- 匿名内部类
内部类的好处
- 提供了比方法更好的封装性
- 提高类多继承的可能性
成员内部类
成员内部类即在类结构中直接定义的与属性,方法,构造器同一级别的类。
public class People extends JFrame{
public void m1() {
System.out.println("外部类方法");
// new Body().m3();
}
//内部类
private class Body extends ArrayList{
String name;
public void m2() {
System.out.println("内部类方法");
}
public void m3() {
}
}
}
成员内部类的使用场景:在JDK中有很多类使用到了成员内部类,例如: ArrayList
成员内部类注意事项:
成员内部类类似成员方法,允许被任意的访问修饰符修饰(default,public,private,protected)
局部内部类
所谓局部内部类即在一个类的方法或者其他成员的(构造器,游离块)语句块中定义,不太常见:
public class User {
int n;
public void login() {
//jdk1.8之后无需显式定义final(编译器自动添加)
final int i = 10;
class Validate{
public void t() {
//成员变量在局部内部类中允许修改
n = 100;
//变量i在局部内部类只能使用不能修改
System.out.println("======="+i);
}
}
Validate v = new Validate();
v.t();
}
public void reg() {
//编译错误:找不到类
// Validate v = new Validate();
// v.t();
}
public static void main(String[] args) {
User u = new User();
u.login();
}
}
** 局部内部类注意事项:**
- 不能使用任何的访问修饰符(除了default外)修饰,原理参考局部变量的定义
- 局部内部类中不能对方法中的局部变量进行修改,如果局部内部类中需要使用为外部方法的局部变量时,该变量必须定义为final(java8无需显式定义final,编译器默认添加)
- 使用final修饰局部变量的作用在于延长局部变量的生命周期,避免因为方法的结束而被清理(可能局部内部类在使用该变量)
静态内部类
静态内部类即使用static修饰的成员内部类;静态内部类与外部类的对象无关,一般用于数据缓存,比如Integer中的静态内部类:IntegerCache
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
自定义静态内部类:
public class StaticClass {
public void m() {
System.out.println("外部类方法");
}
public static class MyCache{
static String msg = "天干物燥,小心火烛";
public static void t() {
System.out.println("静态内部类的方法");
}
public void m2() {
System.out.println("静态内部类的普通方法");
}
}
}
静态内部类的对象创建与方法调用:
//创建静态内部类的对象
StaticClass.MyCache cache = new StaticClass.MyCache();
//调用静态内部类的静态方法
StaticClass.MyCache.t();
//调用静态内部类的成员方法
cache.m2();
匿名内部类
匿名内部类即没有名字的内部类,通常用于事件监听机制,回调机制中
public abstract class Animal {
public abstract void eat();
}
实例:
public static void main(String[] args) {
//匿名内部类
Animal a = new Animal(){
int i = 10;
public void eat() {
i = 20;
System.out.println("吃狗肉"+i);
m();
}
//由于匿名内部类没有名称
//则内部定义的方法外界无法访问
//通常方法使用private修饰
private void m() {
System.out.println("----");
}
};
a.eat();
}
接口的匿名内部类使用
接口的声明:
public interface Flyable {
void fly();
}
接口匿名内部类使用:
Flyable f = new Flyable() {
public void fly() {
System.out.println("fly。。。。");
}
};
f.fly();