面向对象
继承:
1,提高了代码的复用性
2,让类与类之间产生了关系,有了这个关系,才有了多态的特性
注意:
千万不要为了获取其他类的功能,简化代码而继承
必须是类与类之间有所属关系才可以继承。
java中只支持单继承,不支持多继承
因为多继承容易带来安全隐患:当多个父类中定义了相同的功能
当内容不同时,子类对象不确定要运行哪一个。
但是java保留这种机制,并用另一种体现形式来完成表示。多实现
java支持多层继承
如何使用一个继承体系中的功能呢?
要使用体系,先查阅父类描述,因为父类中定义的是体系中的共性内容。
通过了解共性功能,就可以知道该体系的基本功能
那么这个体系已经可以基本使用了。
在具体调用时,要创建最子类的对象
1,因为有可能父类不能创建对象
2,创建子类对象能使用更多的功能,包括基本的也包括特有的
简单一句话:查阅父类功能,创建子类对象使用功能。
继承体系的性质:
1,变量
如果子类中出现非私有的成员变量时
子类要访问本类中的变量用this,要访问父类的变量用super
this代表的是本类对象的引用
super代表的是父类对象的引用
2,子父类中的函数
当子类出现和父类一模一样的函数时
当子类对象调用该函数,会运行子类函数的内容
如同父类的函数被覆盖一样
这种情况是函数的另一特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中
子类虽具备该功能,该功能的内容却和父类不一致
这时没有必要定义新功能,保留父类的功能定义,并重写功能内容
覆盖:
1,子类覆盖父类必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败
2,静态只能覆盖静态。
记住:
重载:只看重名函数的参数列表
重写:子父类方法要一模一样。
3,子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行
那是子类的构造函数默认第一行有一条隐式的语句super();
super()会访问父类中空参数的构造函数,而且子类中所以构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类建立对象时,需要先查看父类是如何对这些数据进行初始化的
所以子类在对象初始化时要先访问一下父类中的构造函数
如果要访问父类中指定的构造函数,可以通过手动定义super语句来指定
注意:super语句一定定义在子类构造函数的第一行
子类的实例化过程
结论:
1 子类的所有构造函数,默认都会访问父类中空参数的构造函数
因为子类每一个构造函数内的第一行都有一句隐式的super()
2 当父类中没有空参数的构造函数时,子类必须通过super或this语句来指定要访问的构造函数
3 当然子类构造函数的第一行也可以手动指定this语句来访问本类中的构造函数
子类中至少有一个构造函数会访问父类中的构造函数
代码演示1:
class Father
{
Father()
{
System.out.println("Father Run");
}
Father(int x)
{
System.out.println("Father....."+x);
}
int num=4;
void show()
{
System.out.println("show Father");
}
void speak()
{
System.out.println("vb");
}
}
//子类继承父类
class Son extends Father
{
//子类构造函数,第一行默认有一个空参数的super()
Son()
{
//super();
//super(4);
System.out.println("Son run");
}
//带参数的构造函数
Son(int x)
{
System.out.println("Son..."+x);
}
//int num=5;
//覆盖show方法
void show()
{
System.out.println("show Son");
}
//覆盖speak方法
void speak()
{
System.out.println("java");
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Son s = new Son();
Son s1 = new Son(4);
s.show();
s.speak();
//System.out.println(s.num+"...."+s.num);
}
}
final:最终。作为一个修饰符
1,可以修饰类、变量、函数
2,被final修饰的类不可以继承。为了避免被继承,被子类复写功能
3,被final修饰的方法不能被复写
4,被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员变量,又可以修饰局部变量
当在描述事物时,一些事物的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便阅读
而这个值不需要改变,加上final修饰。作为常量:所有的字母都大写,如果由多个单词组成,单词间通过下划线_连接
5,内部类定义在类中局部位置上时,只能访问局部被final修饰的局部变量
代码演示2:
class Demo
{
final int x=3;
public static final double PI=3.14;
final void show1()
{}
void show2()
{
final int y = 4;
System;out.println(3.14);
}
}
抽象类:
当多个类中出现相同功能,但是功能主题不同
这时可以进行向上抽取,这时只抽取功能定义,不抽取功能主体
抽象类的特点:
1,抽象方法一定在抽象类中
2,抽象方法和抽象类abstract关键字修饰
3,抽象类不可以用new创建对象,因为调用抽象方法没意义
4,抽象类中的抽象方法要被使用必须由子类复写其抽象方法后,建立子类对象调用
5,如果子类只覆盖了部分抽象方法,该子类还是一个抽象类
抽象类和一般类没有太大的区别。
该如何描述事物,就如何描述事物,只不过该事物中出现了一些看不懂的东西。
这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体
抽象类比一般类多了抽象方法
抽象类不可以实例化
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象
代码演示3:
//定义一个抽象类
abstratc class Student
{
//定义一个抽象方法
abstract void study();
}
//定义一个子类继承抽象类
class BaseStudent extends Student
{
//覆盖study()方法
void study()
{
System.out.println("Base Student");
}
}
class AdvStudent extends Student
{
//覆盖父类的方法,实现自己的方法体
void study()
{
System.out.println("Adv Student");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
new Student();
//new BaseStudent().study();
}
}
接口:
初期理解,可以认为是一个特殊的抽象类
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示
class用于定义类
Interface用于定义接口
接口定义时,格式特点:
1,接口中常见定义:常量,抽象方法
2,接口中的成员都有固定修饰符
常量:public static final
方法:public abstract
接口是不能创建对象的,因为有抽象方法。
需要被子类实现,子类对抽象接口中的方法全部覆盖后,子类才可以实例化
否则子类是一个抽象类
接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
代码演示4:
interface Inter
{
//定义常量NUM
public static final int NUM=3;
//定义抽象方法
public abstract void show();
}
interface InterA
{
public abstract void methord();
}
//定义类实现Inter接口
class Test implements Inter
{
//覆盖show方法
public void show() {}
}
class demo
{
public void Fuction() {}
}
interface A
{
public abstract void methordA();
}
interface B // extends A
{
public abstract void methordB();
}
//接口可以继承多个接口
interface C extends A,B
{
public abstract void methordC();
}
//D类实现了C接口,必须覆盖3个方法
class D implements C
{
public void methordA() {}
public void methordB() {}
public void methordC() {}
}
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);
}
}
模板方法模式:
需求:获取一段程序运行的时间
原理:获取程序开始和结束的的时间,并相减
获取时间:System.currentTimeMillis();
当代码完成优化后,就可以解决这类问题
这种方式:模板方法设计模式
什么是模板方法呢?
在定义功能时,功能的一部分是确定的,但是有一部分不确定
那么这时就将不确定的部分暴露出去。由该类的子类去完成
代码演示5:
abstract class GetTime
{
public final void getTime()
{
long start = System.currentTimeMillis();//方法运行前的时间
runcode(); //不确定的方法
long end = System.currentTimeMillis(); //方法运行后的时间
System.out.println("毫秒 :"+(start+end));
}
public abstract void runcode();
}
class SubGetTime extends GetTime
{
//子类实现runCode方法
public void runcode()
{
for(int x=0; x<400; x++)
{
System.out.print(x);
}
}
}
class TemplateDemo
{
public static void main(String[] args)
{
//创建子类对象
SubGetTime gt =new SubGetTime();
//调用父类的方法
gt.getTime();
}
}
多态:
可以理解为事物存在的多种体现形态
1,多态的体现
父类引用指向了自己的子类对象
父类引用可以接受自己的子类对象
2,多态的前提
必须是类与类之间有关系,要么继承,要么实现
通常还有一个前提,存在覆盖
3多态的好处
多态的出现大大的提高了程序的拓展性
4,多态的弊端
提高了拓展性,但只能使用父类的引用访问父类中的成员
5,多态的应用
代码演示6:
//定义一个抽象的父类
abstract class Animal
{
//共性方法
public abstract void eat();
}
class Cat extends Animal
{
//子类实现自己的eat方法
public void eat()
{
System.out.println("eat fish");
}
//子类独有的catchMouse方法
public void catchMouse()
{
System.out.println("catch mouse");
}
}
class Dog extends Animal
{
//子类实现自己的eat方法
public void eat()
{
System.out.println("eat bone");
}
//子类独有的lookafter方法
public void lookafter()
{
System.out.println(" look after ");
}
}
class Pig extends Animal
{
//子类实现自己的eat方法
public void eat()
{
System.out.println("siliao");
}
//子类独有的方法
public void gongdi()
{
System.out.println("gongdi");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
//Cat c= new Cat();
//c.eat();
//c.catchmouse();
//fuction(new Dog());
//fuction(new Pig());
Animal a = new Dog();
//类型提升。(向上转型)
a.eat();
//如果想要调用猫中的特有方法时,如何操作
//强制将父类的引用,转成子类类型(向下转型)
if (a instanceof Cat)
{
Cat c= (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog)
{
Dog c=(Dog)a;
c.lookafter();
}
//千万不要出现这样的操作,将父类对象转换成子类类型
//我们能转换的是父类引用指向自己的子类对象时,该引用可以被提升,也可以被强制转换
//多态自始至终都是子类对象在做着变化
//c.eat();
//fuction(new Cat());
//fuction(new Dog());
//fuction(new Pig());
}
/*
public static void fuction(Cat c)
{
c.eat();
}
public static void fuction(Dog d)
{
d.eat();
}
*/
public static void fuction(Animal a)
{
a.eat();
}
}
在多态中成员函数的特点:
在编译时期,参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过;如果没有,编译失败
在运行时期,参阅对象所属的类中是否有调用的方法
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边
多态中成员变量的特点:
无论编译和运行,都参考左边
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边
代码演示7:
class Father
{
//int num =5;
void method1()
{
System.out.println("Father method_1");
}
void method2()
{
System.out.println("Father method_2");
}
}
class Son extends Father
{
//int num =8;
void method1()
{
System.out.println("Son method_1");
}
void method3()
{
System.out.println("Son method_3");
}
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
Father f = new Son(); //父类引用指向子类对象
f.method1(); //编译时看父类中有无method1(),运行时看子类Son中有无定义method1()
f.method2();
f.method3(); //编译失败
}
}
Object:
是所有对象的直接或间接父类
该类中定义的是所有对象都具备的功能
异常:
就是程序在运行时出现不正常情况
1,异常的由来:
问题也是现实生活中的具体事物,也可以通过java的类的形式进行描述。并封装成对象
其实就是java对不正常情况进行描述后的对象体现
对于问题的划分:两种:一种是严重的问题,一种是非严重的问题
对于严重的,java通过Error类进行描述
对于Error一般不编写针对性的代码对其进行处理
对于非严重的,java通过Exception类进行描述。
对于Exception可以使用针对性的处理方式进行处理
无论Error或者Exception都有一些共性内容
比如:不正常的信息,引发原因等
Thowable
|--Error
|--Exception
2,异常的处理:
java提供了特有的语句进行处理
try
{
需要被检测的代码;
}
catch(异常类变量)
{
处理异常的代码;
}
finally
{
一定会执行的语句;
}
3,对捕获到的异常进行常见方法操作
String getMessage(); 获取异常的信息
在函数上声明异常
便于提高安全性,让调出进行处理。不处理编译失败
对多异常的处理
1,声明异常时,建议声明更具体的异常。这样处理可以更具体
2,对方声明几个异常,就对应几个catch块。不要定义多余的catch块
如果多个catch中的异常出现继承关系,父类异常catch块放在最下边
建议在进行catch处理时,catch中一定要具体的处理方式
不要简单的定义一句 e.printStackTrace();
也不要简单的就写一条输出语句
代码演示8:
class Demo
{
int div ( int a ,int b) throws ArithmeticException , ArrayIndexOutOfBoundsException
//在功能上通过static关键字声明了该功能有可能出现问题
{
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,0);
System.out.println("x=="+x);
}
catch(ArithmeticException e) //Exception e = new ArithmeticException();
{
System.out.println("除零");
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
//其实jvm默认的异常处理机制,就是在调用printStackTrace方法。打印异常在堆栈的跟踪信息
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.getMessags());
System.out.println("下标越界");
}
System.out.println("over");
}
}
因为项目中会出现特有的问题
而这些问题并未被java所描述并封装对象
所以对于这些特有的问题可以按照java的对问题封装的思想
将特有的问题进行自定义的异常封装
自定义异常
需求:在本程序中除数为负数,也视为错误,无法进行运算
那么就需要对这个问题进行自定义的描述
当在函数内部出现throw抛出异常对象,那么就必须要给对应的处理动作
要么在内部try catch 处理,要么在函数上声明让调用者处理
一般情况下,函数类出现异常,函数上需要声明
发现打印的结果中只有异常的名称,却没有异常的信息
因为自定义的异常并未定义信息
如何定义异常信息呢?
因为父类已经把异常信息的操作都完成了
所以子类只要在构造时,将异常信息传递给父类通过super语句
就可以通过getMessage方法获取自定义的异常信息
自定义异常:
必须是自定义类继承Exception
继承Exception的原因:
异常体系有一个特点:因为异常类和异常对象都需要被抛出
他们都具备可抛性。这个可抛性是Throwable体系中独有的特点
只有这个体系中类和对象才可以被throws和throw多操作
throw和throws的区别
throws使用在函数上
throw使用在函数内
throws后跟异常类;可跟多个;用逗号隔开
throw后跟的是异常对象
代码演示9:
class FuShuException extends Exception
{
private String msg;
FuShuException( String msg)
{
this.msg=msg;
}
public String getMessage()
{
return msg;
}
}
class Demo
{
int div( int a , int b ) throws FuShuException
{
if (b<0)
throw new FuShuException("出现了除数是负数的情况");
return a/b;
}
}
class ExceptionDemo2
{
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("除数为负数了");
}
System.out.println("over ");
}
}
RuntimeException:
Exception中有一个特殊的子类异常,RuntimeException
如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过
如果在函数上声明了该异常;调用者可以不用进行处理
之所以不用在函数上声明,是因为不需要调用者处理
当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况
希望停止程序后,对代码进行修正
自定义异常时:如果该异常的发生,无法再继续进行运算
就让自定义异常类继承RuntimeException
对于异常分两种:
1,编译时被检测的异常
2,编译时不被检测的异常(运行时异常。RuntimeException及其子类
代码演示10:
Demo
{
int div ( int a ,int b)
{
if(b==0)
throw new ArithmeticException("被零除");
return a/b;
}
}
ExceptionDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();
int x = d.div(4,1);
System.out.println("x= "+x);
}
}
内部类:
内部类的访问规则:
1,内部类可以直接访问外部类中的成员,包括私有,之所以可以直接访问内部类中的成员,是因为内部类持有了一个外部类的引用。格式:外部类名.this
2,外部类要访问内部类,必须建立内部对象
访问格式:
1,当内部类定义在外部类的成员位置上,也非私有,可以在外部其他类中直接建立内部类对象
格式:
外部类名.内部类名变量名 = 外部对象.内部对象
Outer.Inner in = new Outer().new Inner();
2,当内部类在成员位置上,就可以被成员修饰符所修饰
比如private:将内部类在外部类中进行封装
static:内部类就具备了静态的特性
当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问权限
在外部其他类中,如何直接访问静态内部类的非静态成员呢?
new Outer.Inner().functin();
在外部其他类中,如何直接访问静态内部类的静态成员呢?
Outer.Inner.function();
注意当内部类定义了静态成员,该内部类必须是静态的
当外部类中的静态方法访问内部类时,内部类也必须是static的
当描述事物时,事物的内部还有事物,该事物用内部类描述
因为内部事物在使用外部事物的内容。
代码演示11:
class Outer
{
private static int x;
static class Inner
{
static void fuction()
{
int x = 4;
System.out.println("inner"+ x);
}
}
static class Inner2
{
void show()
{
System.out.println("show run");
}
}
public static void method()
{
//Inner in = new Inner();
//in.fuction();
new Inner2().show();
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
new Outer.Inner().fuction();
//直接访问内部类的成员
//Outer.Inner in = new Outer().new Inner();
//in.fuction();
}
}
内部类定义在局部时
1,不可以被成员修饰符修饰
2,可以直接访问外部类中的成员,因为还持有外部类的引用,但是不可以访问它所在的局部的变量,只能访问被final修饰的局部变量
匿名内部类:
1,匿名内部类其实就是内部类的简写格式
2,定义匿名内部类的前提:
内部类必须是继承一个类或实现接口
3,匿名内部类的格式:new 父类或者接口() {定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。可以理解为带内容的对象
5,匿名内部类中定义的方法最好不超过三个
代码演示12:
abstract class AbsDemo
{
abstract void show();
}
class Outer
{
int x = 3;
/*
class Inner extends AbsDemo
{
void show()
{
System.out.println("show :"+ x);
}
void abc()
{
System.out.println("abc :"+x);
}
}
*/
public void function()
{
//new Inner().show();
new AbsDemo()
{
void show()
{
System.out.println("show :"+x);
}
void abc()
{
System.out.println("abc");
}
}.abc();
}
}