黑马程序员--Java基础--继承、抽象类、接口、内部类、异常、包

时间:2023-02-18 12:07:37

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

1.继承(extends)

继承的概述

    • 多个类中存在相同属性和行为时,将这些内容抽取到 单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。
    • 多个类可以称为子类,单独这个类称为父类或者超 类。
    • 子类可以直接访问父类中的非私有的属性和行为。
    • 通过extends 关键字让类与类之间产生继承关系。class SubDemo extends Demo{}

好处:

    1. 继承的出现提高了代码的复用性。
    2. 继承的出现让类与类之间产生了关系,提供了多态的前提。

继承弊端:

打破了封装性。

1.1 Java只支持单继承,不支持多继承。

一个类只能有一个父类,不可以有多个父类。

class SubDemo extends Demo{} //ok

class SubDemo extends Demo1,Demo2...//错误

class Fu
{
private int num = 4;

public int getNum()
{
return num;
}
}

//子类继承父类
class Zi extends Fu
{
private int num = 5;


void show()
{
System.out.println(this.num+"....."+super.getNum());
}
}


class ExtendsDemo2
{
public static void main(String[] args)
{
Zi z = new Zi();//创建子类实例
z.show();
}
}


1.2 Java支持多层继承(继承体系)
•class A{}
•class B extends A{}

•class C extends B{}

  • 什么时候定义继承呢?
类与类之间存在所属关系的时候,就定义继承。Xxx是Yyy的一种。Xxx extends Yyy
  • 定义继承需要注意:
1.不要仅为了获取其他类中某个功能而去继承

2.类与类之间要有所属( " is a " )关系,xx1是xx2的一种。

1.3 super关键字
super和this的用法相同
    1. this代表本类引用
    2. super代表父类引用
    3. 当子父类出现同名成员时,可以用super进行区分
    4. 子类要调用父类构造函数时,可以使用super语句

1.4 函数覆盖(Override)

  • 子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重 写或者复写。
  • 父类中的私有方法不可以被覆盖。
  • 在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
  • 覆盖注意事项:
>>覆盖时,子类方法权限一定要大于等于父类方法权限
>>静态只能覆盖静态。
  • 覆盖的应用:

>>当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

//父类
class Fu
{
public static void show()
{
System.out.println("fu show run");
}
}
//子类继承父类
class Zi extends Fu
{//覆盖父类中的方法
public static void show()
{
System.out.println("Zi show run");
}
}

class ExtendsDemo3
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();

}
}


1.5 子类的实例化过程
  1. 子类中所有的构造函数默认都会访问父类中空参数的构造函数
  2. 因为每一个构造函数的第一行都有一条默认的语句 super();
  3. 子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。
  4. 当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。同时子类构造函数中如果使用this调用了本类构造函数时,那么super就没有了,因为super和this都只能定义第一行。所以只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
public class ExtendsSuper {

public static void main(String[] args) {
// TODO Auto-generated method stub
new Zi(6);
}
}
//父类
class Fu
{
int num ;
Fu()
{
num =10;
System.out.println("A fu run");
}
Fu(int x)
{
System.out.println("B fu run..."+x);
}
}
//子类继承父类
class Zi extends Fu
{
int num;
Zi()
{
//super();//调用的就是父类中的空参数的构造函数,默认就存在

System.out.println("C zi run"+num);
}
Zi(int x)
{
this();
//super();//this和super两个不能同时存在
System.out.println("D zi run "+x);
}
}



1.6 final关键字
  1. final可以修饰类,方法,变量。
  2. final修饰的类不可以被继承。
  3. final修饰的方法不可以被覆盖。
  4. final修饰的变量是一个常量。只能被赋值一次。
  5. 内部类只能访问被final修饰的局部变量。

写法规范:常量所有字母都大写,多个单词,中间用_连接。

1.7  单例设计模式

  • 可以保证一个类在内存中对象的唯一性,对与多个对象使用同一个配置信息对象时就需要保证对象的唯一性。
如何保证对象的唯一性呢?
    1. 不允许其他程序使用new创建该类对象。
    2. 在该类创建一个本类的实例。
    3. 对外提供一个方法让其他程序可以获取该对象。

步骤:

    1. 私有化该类构造函数。
    2. 通过new在本类中创建一个本类对象。
    3. 定义一个公有方法将创建的本类对象返回。
//饿汉式单例设计模式
class Single {
private static Single single = new Single();// 创建本类实例并私有化

private Single() {
}// 私有化构造函数

public static Single getSingle() {// 向外提供获取本类对象的接口
return single;
}
}

// 懒汉式单例设计模式
class Single2 {
private static Single2 single2 = null;// 先不创建实例对象

private Single2() {
}// 私有化构造函数

public static Single2 getIntance() {
if (single2 == null) {
single2 = new Single2();// 用到时才创建本类实例
}
return single2;
}
}

2.抽象类(abstract)

1.抽象定义:

抽象就是从多个事物中将共性的,本质的内容抽取出来。

2.抽象类:

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

3.抽象方法的由来:

多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功 能声明,没有功能主体的方法称为抽象方法。

4.抽象类的特点

1.抽象类和抽象方法必须用abstract关键字来修饰。

2.抽象方法只有方法声明,没有方法体,定义在抽象类中。

5.格式:修饰符abstract 返回值类型函数名(参数列表) ;

6.抽象类不可以被实例化,也就是不可以用new创建对象。原因如下:

1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。

2.而且抽象类即使创建了对象,调用抽象方法也没有意义。

7.抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。


8.抽象关键字不可以和那些关键字共存?

    • private (不可被覆盖)不行
    • static(不需创建对象)不行
    • final(不可被覆盖)不行
public class Abstract {

public static void main(String[] args) {
// TODO Auto-generated method stub//
Programmer p=new Programmer("zhangsan","2014",3500.0);//子类实例化
Manager m=new Manager("zhangsan","2014",3500.0,100);//子类实例化
p.work();
m.work();
}

}
//抽象类
abstract class Employee
{
private String name;
private String id;
private double pay;
Employee(String name,String id,double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}

public abstract void work();//抽象方法

}
//子类继承抽象类
class Programmer extends Employee
{
Programmer(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()//覆盖抽象方法
{
System.out.println("code...");
}
}
//子类继承抽象类
class Manager extends Employee
{
private int bonus;
Manager(String name,String id,double pay,int bonus)
{
super(name,id,pay);
this.bonus = bonus;
}
public void work()//覆盖抽象方法
{
System.out.println("manage");
}
}


3. 接口(interface)

当一个抽象类中所有的方法都是抽象方法的时候,这时就可以将抽象用另一种形式来定义和表示,那就是接口。

1.格式:interface {}
2.接口中的成员修饰符是固定的。

  • 成员常量:public static final
  • 成员函数:public abstract
3.接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。


4.接口的特点
    1. 接口是对外暴露的规则。
    2. 接口是程序的功能扩展。
    3. 接口可以用来多实现。
    4. 类与类之间是继承关系,类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
    5. 接口与接口之间可以有继承关系。
3.1抽象类和接口的异同点:
相同点:

都是不断向上抽取而来的。


不同点:

1.抽象类需要被继承,而且只能单继承。

  接口需要被实现,而且可以多实现。 

2.抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。

 接口中只能定义抽象方法,必须由子类去实现。 
3.抽象类的继承,是is a关系,在定义该体系的基本共性内容。

  接口的实现是 like a 关系,在定义体系额外功能。

public class InterfaceDemo {

public static void main(String[] args) {
// TODO Auto-generated method stub
//创建实例
C c = new C();
c.show();
ClassImp ci=new ClassImp();
ci.method();
}

}
//接口A
interface A
{
public void show();
}
//接口B
interface B
{
public int add(int a,int b);
}

class C implements A,B//多实现
{

public int add(int a,int b)
{
return a+b+3;
}
public void show(){
System.out.println("A show");
}

}

class D
{
public void method()
{}
}

//继承的同时实现接口
abstract class E extends D implements A,B
{
public void show(){
System.out.println("show");
}
public int add(int a,int b){
return a+b;
}
}

interface Inter1
{
void show();
}
interface Inter2
{
void method();
}
//接口与接口之间是继承关系,而且接口可以多继承。
interface Inter3 extends Inter1,Inter2
{
void function();
}

class ClassImp implements Inter3
{
//覆盖3个方法。
public void show(){
System.out.println("Inter1show");
}
public void method(){
System.out.println("Inter2method");
}
public void function(){
System.out.println("Inter13function");

}
}


4.多态

定义:某一类事物的多种存在形态。

体现:

父类或者接口的引用指向或者接收自己的子类对象。

作用:

多态的存在提高了程序的扩展性和后期可维护性

前提:

1.需要存在继承或者实现关系

2.要有覆盖操作


多态的好处:

提高了代码的扩展性,前期定义的代码可以使用后期的内容。


多态的弊端:

前期定义的内容不能使用(调用)后期子类的特有内容。


多态的特点

1.成员变量:

>>只看引用变量所属的类。

简单说:编译和运行都参看等号的左边.

2.成员函数(非静态):

>>编译时:要查看引用变量所属的类中是否有所调用的成员。

>>在运行时:要查看对象所属的类中是否有所调用的成员。

简单说:编译看左边,运行看右边。


3.静态函数。
>>编译时:参考引用型变量所属的类中的是否有调用的静态方法。
>>运行时:参考引用型变量所属的类中的是否有调用的静态方法。

简单说,编译和运行都看左边。

public class Polymorphism {

public static void main(String[] args) {
// TODO Auto-generated method stub
Fu.method();
Zi.method();
Fu f = new Zi();//多态
f.method();//静态函数,编译运行都看左边
f.show();//非静态函数,编译看左边,运行看右边
System.out.println(f.num);//成员变量,编译运行都看左边
Zi z = new Zi();
System.out.println(z.num);
}

}
//父类
class Fu
{
int num = 3;
void show()
{
System.out.println("fu show");
}

static void method()
{
System.out.println("fu static method");
}
}
//子类继承父类
class Zi extends Fu
{
int num = 4;
void show()
{
System.out.println("zi show");
}

static void method()
{
System.out.println("zi static method");
}
}


4. 内部类

内部类

将一个类定义在另一个类的里面,对里面那个 类就称为内部类(内置类,嵌套类)。

访问特点:

1.内部类可以直接访问外部类中的成员,包括私有成员。

2.而外部类要访问内部类中的成员必须要建立内部类的对象。


内部类的位置

1.内部类定义在成员位置上
>>可以被private static成员修饰符修饰。
>>被static修饰的内部类只能访问外部类中的静态成员。
2.内部类定义在局部位置上
>>也可以直接访问外部类中的成员。
>>同时可以访问所在局部中的局部变量,但必须是被final修饰的。

// class Test6作为外部类
public class Test6 {
// 私有成员变量
private String name;
private int age;

public static void main(String[] args) {
// TODO Auto-generated method stub
outerClassMethod_1();
}
private static void outerClassMethod_1() {
// 创建一个内部类的实例对象,并调用内部类的私有成员变量及函数
Test6.InnerClass innerClass = new Test6().new InnerClass();
String name = innerClass.nameIn;
int age = innerClass.ageIn;
innerClass.innerClassMethod();
System.out.println("outerClassMethod_1 run...");
}
private static void outerClassMethod_2() {

System.out.println("outerClassMethod_2 run...");
}

// 创建内部类
class InnerClass {
//私有成员变量
private String nameIn;
private int ageIn;

private void innerClassMethod() {
// 内部类对外部类私有成员变量的访问,并调用外部函数
String name = Test6.this.name;
int age = Test6.this.age;
Test6.outerClassMethod_2();
System.out.println("innerClassMethod run...");
}

}
}


4.1 匿名内部类

1.就是内部类的简化写法。

2.前提:

>>内部类可以继承或实现一个外部类或者接口。

3.格式为:

>>new 外部类名或者接口名(){覆盖类或者接口中的代码, (也可以自定义内容。)}

4.简单理解:

>>就是建立一个带内容的外部类或者接口的子类匿名对象。


public class InnerClass {

public static void main(String[] args) {
new Outer().method();
}
}
abstract class Demo
{
abstract void show();
}

class Outer
{
int num = 4;

class Inner extends Demo
{
void show()
{
System.out.println("showInner ..."+num);
}
}

{
new Inner().show();
new Demo()//匿名内部类。
{
void show()
{
System.out.println("showInnerClass..."+num);
}
}.show();
}
}

5.异常

异常的体系

Throwable

1.Error

通常出现重大问题如:运行的类不存在或者内存溢出等。

不编写针对代码对其处理

2.Exception

在运行时运行出现的一起情况,可以通过try catch finally

Exception和Error的子类名都是以父类名作为后缀。


5.1 Throwable中的方法

1.getMessage()

>>获取异常信息,返回字符串。

2.toString()

>>获取异常类名和异常信息,返回字符串。

3.printStackTrace()

>>获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

4.printStackTrace(PrintStream s)

>>通常用该方法将异常内容保存在日志文件中,以便查阅。

5.2 throws和throw
throws用于标识函数暴露出的异常。

throw用于抛出异常对象。


throws与throw的区别:
>>thorws用在函数上,后面跟异常类名。

>>throw用在函数内,后面跟异常对象。

public class Demo
{
public int method(int[] arr,int index)
{

if(arr==null)
throw new NullPointerException("数组的引用不能为空!");

if(index>=arr.length)
{
throw new ArrayIndexOutOfBoundsException("数组的角标越界:"+index);
}
if(index<0)
{
throw new ArrayIndexOutOfBoundsException("数组的角标不能为负数:"+index);
}
return arr[index];
}
}

class ExceptionDemo2
{
public static void main(String[] args)
{
int[] arr = new int[3];

Demo d = new Demo();
int num = d.method(null,-30);
System.out.println("num="+num);
System.out.println("over");
}

}



5.3 异常处理
try
{
需要检测的代码;
}
catch(异常类变量)
{
异常处理代码;
}
finally
{
一定会执行的代码;

}

>>Finally代码块只有一种情况不会被执行。就是在之前执行了System.exit(0)。

5.4 自定义异常
  1. 自定义类继承Exception或者其子类。
  2. 通过构造函数定义异常信息。
  3. 通过throw将自定义异常抛出。
异常的分类:
1.编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。 
这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。
这样的问题都可以针对性的处理。

2.编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。

那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。

所以自定义异常时,要么继承Exception。要么继承RuntimeException。

5.5 异常细节

  1. RuntimeException以及其子类如果在函数中被throw抛出,可以不用在函数上声明。
  2. 一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或异常的子 类。
  3. 如果父类抛出多个异常,那么重写(覆盖)方法必须抛出那些异常 的一个子集,不能抛出新的异常。

异常的注意事项:

1.子类在覆盖父类方法时,父类的方法如果抛出了异常,

那么子类的方法只能抛出父类的异常或者该异常的子类。
2.如果父类抛出多个异常,那么子类只能抛出父类异常的子集。
简单说:子类覆盖父类只能抛出父类的异常或者子类或者子集。 
注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try .

5.6 异常处理的原则


1.函数内容如果抛出需要检测的异常,那么函数上必须要声明。

否则必须在函数内用trycatch捕捉,不然编译失败。

2.如果调用到了声明异常的函数,要么trycatch要么throws,否则编译失败。

3.什么时候catch,什么时候throws 呢?

功能内容可以解决,用catch。

解决不了,用throws告诉调用者,由调用者解决 。

4.一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。

内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。

class Demo
{
public int show(int index)throws ArrayIndexOutOfBoundsException//声明异常
{
//抛异常
if(index<0)
throw new ArrayIndexOutOfBoundsException("越界啦!!");
int[] arr = new int[3];
return arr[index];
}
}


class ExceptionDemo5
{
public static void main(String[] args)
{
Demo d = new Demo();
//捕捉异常
try
{

int num = d.show(-1);
System.out.println("num="+num);
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());

}
finally//通常用于关闭(释放)资源。
{
System.out.println("finally");
}

System.out.println("over");

}
}


6 包(package)
  1. 对类文件进行分类管理。
  2. 给类提供多层命名空间。
  3. 写在程序文件的第一行。
  4. 类名的全称的是 包名.类名。
  5. 包也是一种封装形式。
6.1 四种权限


public protected default private
同一类中
同一包中
子类中

不同包中



6.2 import

import的作用:简化类名。

  1. 一个程序文件中只有一个package,可以有多个import。
  2. 用来导包中的类,不导入包中的包。
导包原则:用到哪个类就导入哪个类。

例:import java.util.ArrayList

6.3Jar包
Java的压缩包
  1. 方便项目的携带。
  2. 方便于使用,只要在classpath设置jar路径即可。
  3. 数据库驱动,SSH框架等都是以jar包体现的。
6.4Jar包的操作
通过jar.exe工具对jar的操作。
1.创建jar包
>>jar -cvf mypack.jar packa packb
2.查看jar包
>>jar -tvf mypack.jar [>定向文件]
3.解压缩
>>jar -xvf mypack.jar
4.自定义jar包的清单文件
>>jar –cvfm mypack.jar mf.txt packa packb