1.概述
◆多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可
◆多个类可以成为子类,单独这个类称为父类或者超类
◆子类可以直接访问父类中的非私有的属性和行为
◆通过关键字extends让类与类之间产生继承关系
class SubDemo extends Demo()
◆继承的出现提高了代码的复用性
◆继承的出现让类与类之间产生了关系,提供了多态的前提
2.特点
◆Java只支持单继承,不支持多继承
·一个类只能有一个父类,不可以有多个父类
·class SubDemo extends Demo{} //OK
·class SubDemo extends Demo1,Demo2…{}//error
◆Java支持多层继承(继承体系)
·class A{}
·class B extends A{}
·class C extends B{}
附注:因为多继承容易带来安全隐患:当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
示例1:
class A{
void demo1(){}
}
class B extends A{
void demo2(){}
}
class C extends B{
void demo3(){}
}
问:如何使用一个继承体系中的功能呢?
答:想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能,通过了解共性功能,就可以知道该体系的基本功能。
问:确认这个体系已经可以使用了,那么在具体调用的时候,为什么要创建最子类的对象?
答:1.因为有可能父类不能创建对象;2.创建子类对象可以使用更多的功能,包括基本的也包括特有的功能。
总而言之:查阅父类功能,创建子类对象使用功能。
◆定义继承需要注意:
·不要仅为了获取其他类中某个功能而失去继承
·类与类之间要有所属(“is a”)关系,xx1是xx2的一种
示例:
class Person{
String name;
int age;
}
class Student extends Person{
void study(){
System.out.println("name:"+name+"---age:"+age+"...goodstudy");
}
}
class Worker extends Person{
void work(){
System.out.println("name:"+name+"---age:"+age+"...goodwork");
}
}
class ExtendsDemo{
public static void main(String[] args){
Student s=new Student2();
s.name="zhangsan";
s.age=14;
s.study();
Worker w=new Worker();
w.name="lisi";
w.age=30;
w.work();
}
}
运行结果:
name:zhangsan---age:14...good study
name:lisi---age:30...good work
3.super关键字
◆super和this的用法相同
◆this代表本类引用
◆super代表父类引用
◆当父类出现同名成员是,可以用super进行区分
◆子类要调用父类构造函数时,可以使用super语句
示例1:(this回顾)
class Fu {
private int num = 4;
public void setNum(int num){
this.num=num;
}
public int getNum(){
return this.num;
}
}
class Zi extends Fu{
int num = 5;
void show(){
System.out.println(num);
}
}
class ExtendsDemo2{
public static void main(String[] args) {
Fu f=new Fu();
System.out.println(f.getNum());
Zi z = new Zi();
z.show();
System.out.println(z.num+"...."+z.num);
}
}
运行结果:
4
5
5....5
示例2:(调用父类的成员)
class Person{
String name;
int age;
public void eat(){
System.out.println("eat...");
}
public void walk(){
System.out.println("walk...");
}
public void sleep(){
System.out.println("sleep...");
}
}
class Student extends Person{
public void dayLife(){
super.eat();//调用父类的成员
this.study();//调用本类的成员
super.sleep();//调用父类的成员
}
public void study(){
System.out.println("study...");
}
}
public class ExtendsDemo {
publicstatic void main(String[] args) {
Student stu=new Student();
stu.dayLife();
}
}
运行结果:
eat...
study...
sleep...
示例3:(调用父类的构造函数)
class Person{
String name;
int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public void eat(){
System.out.println("eat...");
}
}
class Student extends Person{
//调用父类的构造函数
Student(Stringname, int age) {
super(name,age);
}
public String toString(){
return "name:"+name+"----age:"+age;
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Student stu=new Student("zhangsan",18);
System.out.println(stu.toString());
stu.eat();
}
}
运行结果:
name:zhangsan----age:18
eat...
补充:
子父类出现后,类成员的特点:
类中成员:变量、函数、构造函数。
4.函数覆盖
◆子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写
◆父类中的私有方法不可以被覆盖
◆在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取
◆覆盖注意事项
·覆盖时,子类方法权限一定要大于等于父类方法权限
·静态只能覆盖静态
◆覆盖的应用
当子类需要覆盖父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
示例:
class Fu {
void show() {
System.out.println("fushow");
}
void speak() {
System.out.println("fu----vb");
}
}
class Zi extends Fu {
void speak() {
System.out.println("zi----java");
}
void show() {
System.out.println("zishow");
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Fuf=new Fu();
f.speak();
f.show();
Ziz = new Zi();
z.speak();
z.show();
}
}
运行结果:
fu----vb
fu show
zi----java
zi show
附注:
重载:只看同名函数的参数列表。
覆盖(重写):子父类方法要一模一样。
5.子类的实例化过程
◆子类中所有的构造函数默认都会访问父类中空参数的构造函数
◆因为子类每一个构造函数的第一行都有一条默认的隐式语句super()
◆子类会具备父类中的数据,所以要先明确父类是如何对子类这些数据初始化的
◆当父类中没有空参数的构造函数时,子类的构造函数必须手动通过super语句指定要访问父类的构造函数
◆子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。
示例:
class Fu{
int num;
Fu(){
num=60;
System.out.println("furun");
}
Fu(int x){
System.out.println("fu..."+x);
}
}
class Zi extends Fu{
Zi(){
super(4);
System.out.println("zirun");
}
Zi(intx){
this();
System.out.println("zi..."+x);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Ziz = new Zi();
System.out.println(z.num);
}
}
运行结果:
fu...4
zi run
0
总结:
子父类中的构造函数:
在对子类对象进行初始化时,父类的构造函数也会运行,因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
问:为什么子类一定要访问父类中的构造函数?
答:因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
6.final关键字
◆final可以修饰类、方法、变量
◆final修饰的类不可以被继承(为了避免被继承,被子类覆盖功能)
◆final修饰的方法不可以被覆盖
◆final修饰的变量是一个常量,只能赋值一次。既可以修饰成员变量,也可以修饰局部变量。当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。
◆内部类只能访问被final修饰的局部变量
示例1:(不能覆盖)
class FinalMethodTest{
//定义成员变量时要指定默认值
final int x = 3;
//定义常量
public static final double PI = 3.14;
public final void test(){}
}
class Sub extends FinalMethodTest{
//下面方法定义将出现编译错误,不能覆盖final方法
public void test(){}
}
示例2:(能“覆盖”)
class PrivateFinalMethodTest{
private final void test(){}
}
class Sub extends PrivateFinalMethodTest {
//下面方法定义不会出现问题
public void test(){}
}
从上面两个示例我们可以获知:对于一个private修饰的方法,只能在本类中访问,其子类无法访问,所以子类无法重写该方法——如果子类中定义一个与父类private修饰的方法相同的方法名、相同的形参列表、相同返回值类型的方法时,也不能重写,只是定义了新的方法而已。因此,即使使用final修饰一个private访问权限的方法时,依然可以在其子类中定义与该方法具有同名、同形参、同返回值类型的方法。
7.抽象类
7.1抽象类概述
抽象定义:
·抽象就是从多个事物中将共性的,本质的内容抽取出来
·例如:狼和狗共性都是犬科,犬科就是抽象出来的概念
抽象类:
Java中可以定义没有方法的方法,该方法的具体实现子类完成,该方法成为抽象方
法,包含抽象方法的类就是抽象类
抽象方法的由来:
·多个对象都具有相同的功能,但是功能具体内容有所不同,那么在抽取过程中, 只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法成为 抽象方法
·例如:狼和狗都有吼叫的方法,可以吼叫内容是不一样的。所以抽象出来的犬科 虽然有吼叫功能,但是并不明确吼叫的细节
7.2抽象类特点
◆抽象类和抽象方法必须用abstract关键字来修饰
◆抽象方法只有方法声明,没有方法体,定义在抽象类中
格式:修饰符 abstract 返回值类型 函数名(参数列表)
◆抽象类不可以被实例化,也就是不可以用new创建对象。原因如下:
·抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬类是一个抽象的概念,真实存在的是狼和狗
·而且抽象类即使创建了对象,调用抽象方法也没有意义
◆抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类还是抽象类
附注:
抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只是该事物出现了一些看不懂的东西,也就是该事物的功能,需要明确出现,又无法定义主体,所以通过抽象方法来表示。
抽象类比一般类多个了抽象函数,就是在类中可以定义抽象方法。抽象类不可以实例化。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象
示例:
abstract class Student{
abstract void study();
void sleep(){
System.out.println("sleep…");
}
}
class ChongCiStudent extends Student{
void study(){
System.out.println("chongcistudy");
}
}
class BaseStudent extends Student{
void study(){
System.out.println("basestudy");
}
}
class AdvStudent extends Student{
void study(){
System.out.println("advstudy");
}
}
class AbstractDemo {
public static void main(String[] args) {
//抽象类不可以实例化s
//newStudent();
newBaseStudent().study();
}
}
运行结果:
base study
练习一:假如我们在开发一个系统时需要对员工进行建模,员工包含 3 个属性:姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
代码实现:
abstract class Employee{
private String name;
private String id;
private double pay;
Employee(Stringname,String id,double pay){
this.name= name;
this.id= id;
this.pay= pay;
}
public abstract void work();
}
class Manager extends Employee{
private int bonus;
Manager(Stringname,String id,double pay,int bonus){
super(name,id,pay);
this.bonus= bonus;
}
publicvoid work(){
System.out.println("managerwork");
}
}
class Pro extends Employee{
Pro(Stringname,String id,double pay){
super(name,id,pay);
}
public void work(){
System.out.println("prowork");
}
}
练习二:获取一段程序运行的时间。
原理:获取程序开始和结束的时间并相减即可。(获取时间:System.currentTimeMillis();)
模版方法设计模式
在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。
代码实现:
abstract class GetTime{
public final void getTime(){
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
publicabstract void runcode();
}
class SubTime extends GetTime{
public void runcode(){
for(int x=0; x<20; x++){
System.out.print(x);
}
}
}
class TemplateDemo{
public static void main(String[] args) {
SubTime gt = new SubTime();
gt.getTime();
}
}
运行结果:
01234567891011121314151617181920毫秒:1
7.3抽象类相关问题
◆问:抽象类中是否有构造函数?
答:有,抽象类是一个父类,要给子类提供实例的初始化。
◆问:抽象关键字abstract不可以和哪些关键字共存?
答:final:被final修饰的类不能有子类,而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写,而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了,可是抽象方法运行没意义。
◆问:抽象类中可不可以没有抽象方法?
答:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象
二、接口
初期理解,可以认为是一个特殊的抽象类;
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
格式:interface{}
接口中的成员修饰符是固定的。
·成员常量:public static final
·成员函数:public abstract
附注:接口中的成员都是public修饰的,接口是不可以创建对象的,因为有抽象方法
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”
特点:
·接口是对外暴露的规则
·接口是程序的功能扩展
·接口可以用来多实现
·类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
·接口与接口之间可以有继承关系
示例:
interface Inter {
public static final int NUM = 3;
public abstract void show();
}
interface InterA {
public abstract void show();
}
class Demo {
public void function() {}
}
class Test extends Demo implements Inter,InterA {
public void show() {}
}
class InterfaceDemo {
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.NUM);
System.out.println(Test.NUM);
System.out.println(Inter.NUM);
}
}
运行结果:
3
3
3
练习:数据库的操作
代码实现原理:
interface UserInfoDao{
public void add(User user);
public void delete(User user);
}
class UserInfoByJDBC implementsUserInofDao{
public void add(User user){
1.JDBC连接数据库;
2.使用sql添加语句添加数据;
3.关闭连接;
}
public void delete(User user){
1.JDBC连接数据库;
2.使用sql添加语句删除数据;
3.关闭连接;
}
}
class UserInfoByHibernate implementsUserInfoDao{
public void add(User user){
1.Hibernate连接数据库;
2.使用sql添加语句添加数据;
3.关闭连接;
}
public void delete(User user){
1.Hibernate连接数据库;
2.使用sql添加语句删除数据;
3.关闭连接;
}
}
class DBOperate{
public static void main(String[] args) {
UserInfoByJDBC uij = new UserInfoByJDBC();
uij.add(user);
uij.delete(user);
UserInfoByHibernate uih = new UserInfoByHibernate();
uih.add(user);
uih.delete(user);
}
}