4.7 抽象类
1、当多个类中出现相同功能,但是功能主体不同,这时可以向上进行抽取。只抽取功能而不抽取功能的主体。
2、抽象类的特点:
(1)抽象方法一定定义在抽象类中。
(2)抽象方法和抽象类都必须被abstract关键字修饰。
(3)抽象类不能用new创建对象,因为调用抽象方法没有意义。
(4)抽象类中的方法要被调用,必须由子类复写其所有的抽象类方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类一定还是一个抽象类。
abstract class Student
{
abstract void study();
}
class BaseStudent extends Student
{
void study()
{
System.out.println("adv study");
}
}
class AdvStudent
{
void study()
{
System.out.println("study");
}
}
3、抽象类可以强迫子类做一些事情。
4、抽象类和一般类没有太大的不同,该怎样如何描述事物就如何描述事物,只不过该事物出现了一些看不懂的东西。这写看不懂的东西也是该事物的功能,需要明确出现。但是无法定义主体。通过抽象方法来表示。
5、抽象类比一般类多了抽象函数,抽象类不可以实例化。
6、特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
例子:员工、经理。注意:Employee既不是员工也不是经理而是两者共同抽取出来的。
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 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("maneger work");
}
}
class Pro extends Employee
{
Pro(String name, String id, double pay)
{
super(name, id, pay);
}
public void work()
{
System.out.println("pro work");
}
}
7、模版方法设计模式:
/*
需求:获取一段程序运行的时间。
原理:获取程序开始和结束的时间并相减即可。
获取时间:currentTimeMillis();
当代码完成优化后,就可以解决这类问题。
这种方法称为,模版方法设计模式。
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的
部分在使用不确定的部分,那么这时候就就将不确定的部分暴露出去,由该类的子类去完成。
*/
abstract class GetTime
{
public final void getTime()//不让复写这个函数
{
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();;
System.out.println("毫秒"+(end-start));
}
public abstract void runCode();//暴露出去
}
class SubTime extends GetTime
{
public void runCode()
{
for (int x = 0; x < 4000; x++)
{
System.out.println(x);
}
}
}
class Demo
{
public static void main(String[] args)
{
SubTime gt = new SubTime();
gt.getTime();
}
}
4.8 接口
1、接口:初期理解可以是特殊的抽象类。当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。class用于定义类,interface用于定义接口。
2、接口定义时结构特点:
(1)接口中常见定义:常量、抽象方法
(2)接口中的成员都有固定修饰符。只要是上边是interface这些固定修饰符都可以省略,但是写的时候最好写全,便于阅读。
常量:public static final
方法:public abstract
(3)接口中的成员都是public。
3、接口不可以创建对象,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才能实例化,否则子类是一个抽象类。
interface Inter
{
public static final int NUM = 3;
public abstract void show();
}
class Test implements Inter
{
public void show(){}
}
class Demo
{
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);
}
}
4、接口可以被类多实现。也是对多继承不支持的转换形式。
5、为什么不能多继承而可以多实现;因为继承的话父类有方法主体,继承的话不知道调用哪个。多实现因为接口中都是抽象方法,只有函数名没有主体。如果重复则全部覆盖就是了。
interface Inter
{
public static final int NUM = 3;
public abstract void show();
}
interface InterA
{
public abstract void show();
}
class Test implements Inter, InterA
{
public void show(){}
}
6、一个类在继承另一个类的同时还可以实现多个接口。
7、接口与接口之间是继承关系且可以多继承。某一个类如果想想实现某一个接口,必须覆盖接口继承的所有函数。
interface A
{
void methodA();
}
interface B extends A
{
void methodB();
}
class C implements B
{
public void methodA(){]
public void methodB(){}
}
interface A
{
void methodA();
}
interface B
{
void methodB();
}
interface D extends B, A
{
}
8、接口特点:
接口是对外暴露的规则。
接口是程序的功能扩展。
接口的出现降低耦合性。
接口可以用来多实现。
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系。
9、继承是is a, 接口是like a。
abstract class Student
{
abstract void study();
void sleep()
{
System.out.println("Sleep");
}
}
interface Smoking
{
void smoke();
}
class ZhangSan extends Student implements Smoking
{//抽烟是一部分有的,不是所有人都有的,所以把抽烟变为接口
//接口用于扩展功能。
void study(){}
public boid smoke(){}
}
4.9、多态
1、多态可以理解为事物存在的多种体现形态。
2、多态的基本体现:父类的引用指向了自己的子类对象。父类的引用也可以接受 自己的子类对象。
Animal c = new Cat();
c.eat();
多态的前提:必须是类与类之间有关系,要么继承要么实现。
多态的好处:多态的出现大大的提高了程序的扩展性。通常还有一个前提存在覆盖。
多态的局限性;提高了扩展性,但是只能用父类的引用访问父类的成员。
abstract class Animal
{
abstract void eat();//不确定所以抽象,强迫
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void CatchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("吃饲料");
}
public void gongDi()
{
System.out.println("拱地");
}
}
//-----------------------------------------
class Demo
{
public static void main(String[] args)
{
/*Cat c = new Cat();
Dog d = new Dog();
function(c);
function(d);
function(new Pig());*/
Animal c = new Cat();
c.eat();
function(new Cat());
function(new Dog());
function(new Pig());
}
public static void function(Animal a)
{
a.eat();//不能是抓老鼠,看家等。。
}
/*
public static void function(Cat c)
{
c.eat();
}
public static void function(Dog d)
{
d.eat();
}
public static void function(Pig p)
{
p.eat();
}*/
}
3、如果想要调用猫的特有方法时,如何操作?强制将父类的引用转成子类类型。
Cat c = (Cat)a;
c.catchMouse();
4、多态自生至终都是子类对象在做着变化。
下边是错误的,不能将父类对象转换成子类对象。能转换的是父类引用指向自己子类对象时,该引用可以被提升也可以被强制转换。
// Animal a = new Animal();
// Cat c = (Cat)a;
5、关键字 instanceof 。
class Demo
{
public static void main(String[] args)
{
function(new Dog());
function(new Cat());
}
public static void function(Animal a)
{
if (a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}
else if (a instanceof Dog)
{
Dog d = (Dog)a;
d.kanJia();
}
}
}
6、多态的应用例子:
abstract class Student
{
public abstract void study();
public void sleep()
{
System.out.println("躺着睡");
}
}
class BaseStudent extends Student
{
public void study()
{
System.out.println("base study");
}
public void sleep()
{
System.out.println("坐着睡");
}
}
class AdvStudent extends Student
{
public void study()
{
System.out.println("adv study");
}
}
class DoStudent
{
public void doSome(Student stu)//多态
{
stu.study();
stu.sleep();
}
}
class Demo
{
public static void main(String[] args)
{
DoStudent ds = new DoStudent();
ds.doSome(new BaseStudent());
ds.doSome(new AdvStudent());
}
}
7、在多态中成员函数的特点:
在编译时期,参阅引用型变量所属的类中是否有调用的方法。如果有编译通过,如果没有编译失败。
在运行时期。参阅对象所属的类中是否有调用的方法。
总结:成员函数在多态调用时,编译看左边,运行看右边。
class Fu
{
void method1()
{
System.out.println("fu method_1");
}
void method2()
{
System.out.println("fu method_2");
}
}
class Zi extends Fu
{
void method1()
{
System.out.println("zi method_1");
}
void method3()
{
System.out.println("zi method_3");
}
}
class Demo
{
public static void main(String[] args)
{
Fu f = new Zi();
f.method1();
f.method2();
//f.method3();编译失败,编译时期看引用型变量,没有所以失败。
}
}
输出zi 1 fu 2。
8、在多态中成员变量的特点:无论编译还是运行都参考左边。
在多态中静态成员函数的特点:无论编译和运行都参考左边。
9 Object类
(1)、是所有对象的直接或者间接父类。该类中定义的是所有对象都具备的功能。
(2)、equals()比较的是地址。能接受任意类型的对象显然是多态的应用。
10、内部类
(1)内部类的访问规则:
内部类可以至访问外部类的成员,包括私有。之所以可以直接访问外部类的成员是因为内部类中持有了一个外部类的引用,写法是 外部类.this。
外部类要访问内部类,必须建立内部类对象。
(2)直接访问内部类方式:例如
Outer.Inner in = new Outer().new Inner();
访问格式:
当内部类定义在外部类的成员位置上而且非私有,可以在外部其他类中可以直接建立内部类对象。
格式。
当内部类在成员位置上,就可以被成员修饰符所修饰。比如private:将内部类在外部类中进行封装。static:内部类就具有static的特性。当内部类被静态修饰后,只能直接访问外部类的静态成员。
在外部其他类中,如何直接访问静态内部类的非静态成员呢?
new Outer.Inner().function();
在外部其他类中,如何直接访问静态内部类的静态成员呢?
Outer.Inner().function();
注意:当内部类中定义了静态成员,该内部类必须是静态的。
当外部类的静态方法访问内部类时,内部类也必须是静态的。
4.10 异常
1、异常:程序在运行时出现的不正常现象。
2、异常由来:问题也是现实生活中一个具体的事物, 也可以通过java的类的形式进行描述,并封装成对象。其实就是java对不正常情况进行描述后的对象体现。
3、对于问题的划分:一种是严重的,一种是非严重的。
对于严重的,java通过Error类进行描述。一般不编写针对性的代码进行处理。
对于非严重的,java通过Exception类进行描述。可以使用针对性的处理方法进行处理。
异常:
class Demo
{
int div(int a, int b)
{
return a/b;
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
int x = d.div(4,0);
System.out.println("x="+x);
}
}
错误:内存溢出
class InnerClassDemo
{
public static void main(String[] args)
{
byte [] arr = new byte[1024*1024*1024];
}
}
4、不论Error还是Exception都有一些共性的内容,
Throwable
!--Error
!--Exception
5、异常的处理
java提供了特有的语句进行处理
try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码(处理方式);
}
finally
{
一定会执行的语句;
}
6、对捕获到的异常对象进行常见方法操作String getMesage(); String toString(); void printStackTrace()
class Demo
{
int div(int a, int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try//抛出异常 new AritchmeticException()
{
int x = d.div(4,0);
System.out.println("x="+x);//出现异常后后边不再执行。
}
catch (Exception e)//捕获异常Exception a = new AritchmeticException()
{ //多态
System.out.println("除零拉");
System.out.println(e.getMessage());
System.out.println(e.toString());//异常名称,异常信息
e.printStackTrace();//异常名称、信息、出现的位置
//其实jvm默认的异常处理机制,就是在调用printStackTrace()
//打印异常的堆栈跟踪信息
}
}
}
7、没问题的话catch的内容不会运行。
8、throws Exception抛出异常,表示这个地方可能出现出现异常。使用这个地方就必须再将异常抛出或者处理异常。
class Demo
{
int div(int a, int b) throws Exception//在功能上通过throws的关键字声明了该功能
{ //有可能出现问题
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,0);
System.out.println("x="+x);
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
}
(1)声明异常时,建议声明更为具体的异常,这样可以处理的更具体。
(2)原则:对方声明几个异常就对应有借个catch块,不要定义多余的catch块。
如果多个catch块的异常出现继承关系,父类异常catch块出现在最下边。
建议在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义一句 e.printStackTrace(),也不要就书写一条输出语句。
class Demo
{
int div(int a, int b) throws ArrayIndexOutOfBoundsException, ArithmeticException
{//不能两个异常,因为检测到异常后边就不执行了。
int[] arr = new int [a];
System.out.println(arr[4]);
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,1);
System.out.println("x="+x);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("被零除了");
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("角标越界啦");
}
/*catch(Exception e) 用这个一个也可以运行,但是没有针对性,并且当第三种异常出现时候会被隐藏。
{
System.out.println(e.toString());
}*/
}
}
10、因为项目中会出现特有的问题,这些问题并未被java所描述且封装对象,所以对于这些特有的问题可以按照java对问题封装的思想,将特有的问题进行自定义的异常封装。
例子+解释+注意:
/*本例需求:对于除数是-1,也视为错误的无法进行运算的。
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。
要么在内部try catch处理。
要么在函数上声明让调用者处理。
一般情况在,函数内部出现异常,函数上需要声明。
发现e.toString()打印的结果中只有异常的名称,没有异常的信息,因为自定义的异常定义信息。
如何定义异常信息?
因为父类中已经把异常信息的操作都完成了。
所以子类只要在构造时,将异常信息传递给父类通过super语句,
那么就直接可以通过getMessage方法获取自定义的异常信息。
自定义异常:
必须是自定义类继承Exception。
继承Exception原因:
一场体系有一个特点,因为异常类和异常对象都需要被抛出。
他们都具备可抛性。这个可抛性是throwable这个体系的独有特点。
这有这个体系中的类和对象才可以被throws和throw操作。
*/
class FuShuException extends Exception//getMessage()
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a, int b) throws FuShuException
{//手动建立对象手动抛出
if (b < 0)
throw new FuShuException("by fushu");//手动通过throw关键字抛出异常
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
System.out.println("除数出现负数了");
}
}
}
throws使用在函数上,throw使用在函数内。
throws后面跟异常类,可以跟多个,用逗号隔开。
throw后面跟的是异常对象。
12、RuntimeException()
Exception中有一个特殊的子类异常RuntimeException()。
如果在函数内抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用处理,编译一样通过。
之所以不用在函数上声明, 是因为不想要调用者处理。当异常发生时,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望程序停止后对代码进行修正。
自定义异常:如果该异常发生时,无法继续进行运算,就让自定义异常继承RuntimeException()。
对于异常分两种:
1、编译时被检测的异常。
2、编译时不被检测的异常。(RuntimeException()及其子类)
13、关键字finally
finally中存放的是一定会被执行的代码。通常用于关闭资源。catch加了return,finally仍然会被读到。加了exit则不会读到了。
14、try可以和finally一起(第三种格式)
记住一点catch用于处理异常,没有catch就代表异常没有被处理过。如果该异常是检测时异常,就必须声明。
15、异常在子父类覆盖中的体现 :
子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或该异常的子类。
如果父类方法抛出多个异常,那么子类在覆盖方法时只能抛出父类异常的子集。
如果父类或接口中没有异常抛出,那么在子类覆盖方法时,也不可以抛出异常。如果子类发生异常就必须进行try处理,绝对不能抛。
16、异常总结:
好处:1将问题进行封装。
2将正常流程代码和问题处理代码相分离,便于阅读。
处理原则:1处理方式有两种,try或者throw。 2调用抛出异常的功能时,抛出几个就处理几个。一个try对应多个catch。
4.11 包(package)
1、关键字package,包名所有字母小写。
2、总结:
包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
不同包中的子类可以访问父类中被protected权限修饰的成员。
包与包之间可以使用的权限只有两种,public protected
publicprotected default private
同一个类中 OKOK OK OK
同一个包中 OKOK OK
子类 OKOK
不同的包中 OK
3、为了简化类名的书写,使用一个关键字,import
import a.b.c.*
注意上述写法不能导入c中的包只能导入c中的类,想要导入d包中的类只能
import a.b.c.d.*
不建议用通配符,要哪个导哪个。
如果导入的多个包中有同名的类,使用类的时候必须加包名。
建议域名反向定义。
4、jar包:java的压缩包