面向对象
就是凡是都是靠别人来完成的自己不用亲力亲为就可以达到目的。
面向过程和面向对象不一定都是互斥的,但可以全部面向过程不能全部面向对象(所有的事情自己亲历亲为也是可以的,我们之前写的所有代码都是面向过程。)
例如:让女朋友做饭 你这个角度而言 面向对象(自己不用做,指挥别人就可以)
从女朋友这个角度而言呢?如果做 面向过程;如果GF不做 叫外卖 面向对象
从餐馆这个角度而言呢?应该是要做饭 面向过程;如果餐馆不做 可以叫别家餐馆做
.....别的餐馆都不做 这个饭始终没人做 相互踢皮球
从中可以看出什么 这个事最终要被处理-面向过程
面向过程其实是最基本的思路 而面向对象是基于面向过程的思路
对象与类
面向对象的好处
可以仿真模拟现实生活中的事物、可以实现更加复杂的业务逻辑。
如:淘宝在买东西的时候 查看商品->付款->生成订单->物流信息。
在面向对象的思想里面
p1=创建一个Point对象并且制定x=1,y=2
p2=创建一个Point对象并且指定x=3,y=4
p1.x p1.y
p1.getDistance(p2) 让p1自己去和p2之间计算距离
好处在于将数据和操作统一起来了
意味着将点拟人化 该事物的数据和操作就可以统一起来了
对象
就是真实存在的且具体事物就称之为对象。
类相当于大楼设计图纸、对象就是由该图纸所建成的大楼。
类就是用于描述事物的
类主要描述事物的那些方面呢?
属性-数据-成员变量
行为-函数-成员函数
比如我们要描述一个坐标
属性 x y
行为 getDistance()
class Point{
double x;
double y;
public double getDistance(Point other){
...
}
}
也就是说,这个事物本身自带的属性,后来培养的行为。
注意*:一个类一个.java文件,可以方便我们的管理,也就意味着当我们创建一个类的时候,其实特就定义了一个数据类型。
对象就是由一个类所描述的内容从而产生一个具体的事物。
在我们创建对象时需要用new关键字。
格式为:类名/数据类型 变量名 = new 类名()
访问对象中的成员的格式为:变量名.成员
对象的内存图解
1.在堆内存中开辟一个空间并分配地址
2.按照类的描述,在该空间中定义成员变量并且有默认初始化值
3.加载成员函数进入方法区(只加载一次)
4.对象创建完毕,将空间地址赋值给相应的变量
5. 变量(p1/ p2)调用成员变量
先通过该变量所存储的地址去堆空间中找,然后在该空间中找相应的成员变量。
6.变量(p1/p2)调用成员函数
直接去方法区中找该成员函数
将该函数加载进栈内存开始运行
为了方便区分哪个对象调用的该成员函数
由this这个关键字段 来区分 this主要存的是当前对象的地址
注意:当成员函数在操作变量的时候
先在当前函数的空间里找 局部变量
如果没有找到,再去this所指向的堆内存中对象所属空间里去找
class Test02{
/*
定义一个解决一元二次方程的类
数据
a,b,c,delt
行为
isSolved() 判断是否有解
getRoot1() 获取第一个解
getRoot2() 获取第二个解
QuadrationEquation
*/
public static void main(String[] args){
QuadrationEquation qe=new QuadrationEquation();
qe.a=10;
qe.b=50;
qe.c=5;
System.out.println(qe.isSovled());
System.out.println(qe.getRoot1());
System.out.println(qe.getRoot2());
}
}
class Test03{
/*
点类Point
数据 x横坐标 y纵坐标
行为 getDistance(Point other) 返回当前点和传入点other之间的距离
三角形类Triangle
数据 Point p1,p2,p3
行为 getArea() 返回当前三角形的面积
getLength() 返回当前三角形的周长
*/
public static void main(String[] args){
Triangle t=new Triangle();
Point p1=new Point();
p1.x=0;
p1.y=10;
Point p2=new Point();
p2.x=0;
p2.y=0;
Point p3=new Point();
p3.x=10;
p3.y=0;
t.p1=p1;
t.p2=p2;
t.p3=p3;
System.out.println(t.getLength());
System.out.println(t.getArea());
}
}
class Test01{
public static void main(String[] args){
Point p1=new Point();
Point p2=new Point();
p1.x=10;
p1.y=20;
p2.x=30;
p2.y=40;
System.out.println(p1.getDistance(p2));
System.out.println(p2.getDistance(p1));
}
}
封装与private关键字
private关键字就是一个权限关键字。(public、protected默认不写)
private关键字表示私有权限,该成员变量或成员函数只能够在类中被访问,外界不可访问。
我们可以通过对象直接修改对象中的成员变量
弊端:如果赋予了一个错误的值 那么势必回导致程序后期的运行结果
就是说我们对象的成员变量并不是完全需要向外界可访问的
如果能够被外界访问 那么势必也能被外界修改
class Person{
int age;
}
Person p=new Person();
System.out.println(p.age); //只是访问并打印
p.age=-10; //不符合现实业务逻辑 给出的是一个错误的数据
我们防范外界直接修改的成员变量,private私有化关键字
成员变量加了private的话 修改不行了 获取也不行了
此时就死局了~ 不能保证后期对成员变量不进行修改或获取
setter是Java当中的修改器,主要修改成员变量
本身就是一个成员函数,命名一般为:setXxx 、setAge 、setName
getter是Java中的访问器,主要访问成员变量(返回成员变量) getter看需求
*********建议:今后的代码中 成员变量一律私有 然后再去设置访问器和修改器
******** 注意:如果成员变量和局部变量重名了 如何区分呢?只需要在成员变量之前加this.即可
面向对象的三大特征:封装、继承、多态。
封装:从字面意义上来看 封装就是将一些杂七杂八的东西进行统一的管理
最大的好处就是节约代码 方便管理 降低耦合性
在代码中有什么体现:
循环:主要解决有规律且重复性执行的代码
函数:主要解决具有独立功能的一段代码 后期进行复用即可
数组:主要解决多种同类型数据的操作 统一操作 方便类管理
类:主要将数据和行为进行统一方便操作 仿真现实中的事物
成员变量与局部变量的区别
存储位置
局部变量存储在栈内存中函数所属的空间里
成员变量存储在堆内存中对象所属的空间里
生命周期
局部变量随着函数的进栈而存在,随着函数的弹栈而消亡
成员变量随着对象的创建而存在,随着对象的销毁而消亡
访问范围
局部变量的访问范围仅仅在函数中
成员变量的访问范围在当前类中
初始化值
局部变量必须先进行初始化值,之后才能被调用。
成员变量在创建时有默认初始化值。
构造函数
我们只能将对象创建完毕之后,再进行对成员变量的赋值。
有些对象创建之前成员变量就有值(不含默认初始化)
有些对象创建之后成员变量才有值
构造函数的主要作用:在创建对象之时对成员变量进行赋值操作
构造函数的格式:
权限修饰符 函数名(参数列表){
函数体;
}
对比和之前学过的函数来说
构造函数
没有函数类型关键字 :
没有返回值类型(并不意味着没有return)
函数名必须是类名
但凡创建一个对象 构造函数就执行一次
问题:我们之前并没有写构造函数
如果类中没有定义任何构造函数的情况下,则会有一个默认无参的构造函数
public ClassName(){}
如果类中有明显定义的构造函数 则默认无参的构造函数不存在了
所以 一般而言 我们最好将那个无参的构造函数写出来!!!!!!!
成员变量的赋值其实经历了三个阶段
默认初始化- 显式初始化 - 针对性初始化
类中成员变量被赋值 构造函数
6.7 对象的内存图解II
1.在堆内存中开辟一个空间并分配地址
2.对成员变量进行【默认初始化】
3.相应的构造函数进栈 刚开始就对成员变量进行【显式初始化】
4.接着再去执行构造函数中的内容【针对性初始化】
5.构造函数执行完毕 弹栈 将对象的地址赋值给相应变量即可
class Person{
private String name="小明"; //每一个Person对象创建之前都叫小明
private int age=5; //每一个Person对象创建之前都是5岁
//private int wheelNum=4; //轮胎的个数
/*
隐藏默认的
public Person(){
}
*/
public Person(){ //无名 无年龄
System.out.println("姓名:"+this.name+",年龄:"+this.age);
}
public Person(int age){//无名 有年龄
this.age=age;
System.out.println("姓名:"+this.name+",年龄:"+this.age);
}
public Person(String name,int age){ //有名 有年龄
this.name=name;
this.age=age;
System.out.println("姓名:"+this.name+",年龄:"+this.age);
}
void show(){
System.out.println("今年"+age+"岁");
}
public void setAge(int age){
if(age<0||age>150){ //局部变量age
this.age=0; //成员变量age
}else{
this.age=age; //左边成员 右边局部
}
}
public int getAge(){
return age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
}
class Point{
private double x;
private double y;
public Point(){
}
public Point(double x,double y){
this.x=x;
this.y=y;
}
public double getDistance(Point other){
return Math.hypot(x-other.x,y-other.y);
}
public void setX(double x){
this.x=x;
}
public void setY(double y){
this.y=y;
}
public double getX(){
return this.x;
}
public double getY(){
return this.y;
}
}
/**
仿照Java自带的String类 写一个自己的MyString类,
基本支持String中大部分的功能。
其实String的本质就是一个一维的字符数组,然后再加上其相关的操作
数组的长度一旦确定 则不可更改;
String的长度一旦确定 也不可更改;
更重要的是,String的内容一旦确定 也不可更改;
String是一个不可变对象 意味着我们不能在原地修改String的内容 只能重新创建一份进行修改。
@author HENG
@version 1.0
*/
class MyString{
private char[] value;
/**
创建一个空串"",也就是说字符串长度为0
*/
public MyString(){
this.value=new char[0];
}
/**
创建一个由指定字符编码组成的字符串
*/
public MyString(byte[] bytes){
this.value=new char[bytes.length];
for(int i=0;i<value.length;i++){
value[i]=(char)bytes[i];
}
}
/**
创建一个由指定字符组成的字符串
*/
public MyString(char[] value){
this.value=new char[value.length];
for(int i=0;i<value.length;i++){
this.value[i]=value[i];
}
}
public char charAt(int index){
if(index<0||index>=value.length){
System.out.println(">>>角标越界"+index);
return (char)-1;
}
return value[index];
}
public int indexOf(int ch){
for(int i=0;i<value.length;i++){
if(value[i]==ch){
return i;
}
}
return -1;
}
public int length(){
return value.length;
}
//比较两个字符串之间的大小
//两个字符数组从左到右挨个比较元素 如果出现第一个不相等的元素 返回差值即可
//如果某一个数组遍历完了也没有发现不相等的 返回长度的差值即可
public int compareTo(MyString other){
int min=Math.min(length(),other.length());
for(int i=0;i<min;i++){
if(value[i]!=other.value[i]){
return value[i]-other.value[i];
}
}
return length()-other.length();
}
public boolean contains(MyString other){
char[] c1=value;
char[] c2=other.value;
if(c2.length>c1.length){
return false;
}
for(int i=0;i<c1.length;i++){
boolean flag=true;
for(int j=0;j<c2.length;j++){
if(c2[j]!=c1[i+j]){
flag=false;
break;
}
}
if(flag){
return true;
}
}
return false;
}
}
/**
Stack是一个简单的由一维数组实现的栈结构
支持入栈出栈等常见操作,但不支持动态扩容操作
为了方便简化代码,默认此Stack中只能存储int型数据
@author HENG
@version 1.0
*/
public class Stack{
private int[] data; // 容器 用于存储栈元素 data.length表示栈的最大容量
private int top=-1; // 栈顶标记 用于标记栈顶元素的位置 当栈为空时 top=-1 栈中有效元素的个数top+1
private int capacity=10; //默认最大容量为10
/**
创建一个默认容量为10的栈
*/
public Stack(){
this(capacity);
}
/**
创建一个指定容量为capacity的栈
@param capacity 由调用者传入的指定容量
如果capacity<0 则容量置为0
如果capacity>100 则容量置为100
*/
public Stack(int capacity){
if(capacity<0){
capacity=0;
}
if(capacity>100){
capacity=100;
}
this.data=new int[capacity];
}
/**
将元素e入栈,如果栈当前已满,则无法加入
@param e 用户指定入栈的元素
*/
public void push(int e){
if(size()==data.length){
System.out.println(">>>栈已满,无法添加元素"+e);
return;
}
data[++top]=e;
}
/**
从栈中弹出一个元素,如果栈已经是空,则返回-1即可(没有学异常 -1表示错误)
@return 返回当前栈顶的元素,如果栈为空则返回-1
*/
public int pop(){
if(isEmpty()){
System.out.println(">>>栈为空,无法弹栈元素");
return -1;
}
return data[top--];
}
/**
获取当前栈顶元素(不出栈),如果栈已经是空,则返回-1即可
@return 返回当前栈顶的元素,如果栈为空则返回-1
*/
public int peek(){
if(isEmpty()){
System.out.println(">>>栈为空,无法获取栈顶元素");
return -1;
}
return data[top];
}
/**
判断当前栈是否为空
@return true表示栈空 否则栈不为空
*/
public boolean isEmpty(){
return top==-1;
}
/**
清空当前的栈
*/
public void clear(){
top=-1;
}
/**
获取栈中有效元素的个数
*/
public int size(){
return top+1;
}
/**
返回栈的字符串表现形式
*/
public String toString(){
if(isEmpty()){
return "[]";
}
String s="[";
for(int i=0;i<size();i++){
s+=data[i];
if(i==size()-1){
s+="]";
}else{
s+=",";
}
}
return s;
}
}
//需求:实现一个简单的栈结构Stack
class Test01{
public static void main(String[] args){
Stack stack=new Stack(20);
for(int e=1;e<=20;e++){
stack.push(e);
}
stack.push(21);
System.out.println(stack.size());
System.out.println(stack.toString());
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.toString());
System.out.println(stack.isEmpty());
stack.clear();
System.out.println(stack.toString());
System.out.println(stack.pop());
}
}
class Test02{
public static void main(String[] args){
Demo d1=new Demo();
Demo d2=new Demo(10);
Demo d3=new Demo(10,20);
Demo d4=new Demo(10,20,30);
}
}
class Demo{
int a;
int b;
int c;
public Demo(){
this(0,0,0);
//Demo(0,0,0);//这种调用方式 其实是把Demo(...)当成成员函数去调用了
}
public Demo(int a){
this(a,0,0);//Demo(a,0,0);
}
public Demo(int a,int b){
this(a,b,0);//Demo(a,b,0);
}
public Demo(int a,int b,int c){
this();
}
public void Demo(int a,int b,int c){
}
public void show(){
System.out.println(a+","+b+","+c);
}
private void init(){
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
System.out.println(".....");
}
}
//需求:模仿String自定义MyString
class Test03{
public static void main(String[] args){
MyString s1=new MyString(); //""
MyString s2=new MyString(new byte[]{97,98,99});//"abc"
MyString s3=new MyString(new char[]{'a','b','c'});//"abc"
MyString ms1=new MyString("abcdefgh".toCharArray());
MyString ms2=new MyString("def".toCharArray());
System.out.println(ms1.charAt(3));//d
System.out.println(ms1.indexOf('b'));//1
System.out.println(ms1.length());//8
System.out.println(ms1.compareTo(ms2));//-3
System.out.println(ms1.contains(ms2));//true
}
}
static关键字
1.成员变量+static=静态变量
当我们在设计类的时候发现多个对象中有共享数据的时候,我们就可以把这个共享数据定义为静态的。
name age虽然每个对象都有但是值不一样 对象特有数据->成员变量
country虽然每个对象也都有 但是值一样 对象共有的数据->静态变量
静态的东西从堆中对象的空间里抽取出来,放到了静态方法区
我们去上课 要喝水 水杯/水壶 ?特有的 饮水机 ? 共有的
上述可以知道,共有的东西存在于一个特定的空间,静态方法区
我们去上课,饮水机在公司,下课后饮水机依然在公司
上述可以说明:共有的数据要活的比对象长。
2.成员函数+static=静态函数
静态函数就i是意为着对象的共有行为吗?不是
我们当前的成员函数如果不访问任何成员变量的情况下,这个函数就可以定义为静态的。
这就意味着静态函数不能直接调用当前类中的成员变量。
因为静态的生命周期要长于对象。
所以静态是优先于对象存在的。
静态也称之为是类的成员,我们可以直接通过类去调用。
(类:静态成员。)
为什么主函数是静态的?
主函数是程序的入口,优于其他的运行。
假设主函数是非静态的,那么必须先创建对象才能调用主函数。
静态变量有没有初始化值?有(运行时常量 永久代JVM)
也有显示初始化
3.类的分类
实体类
就是为了描述一个事物 person point
类中基本内容都是成员函数/成员变量
也会存在一个静态成员
工具类
提供一些已经被实现好的功能 向外部供应。
所以工具类中基本上全都是静态函数
类.Xxx 类.Xxx() Math Arrays
为什么工具类中都是静态的?
1.共有的
2.长时间存在
3.加载一次,后期随便使用
一个道理:
当我们钉一个钉子的时候 找榔头
你是造一个榔头 还是用已存在的榔头?
如果工具类可以被创建对象 是不是就想用的时候去创建
效率较低
主类/测试类
主要用于运行/测试代码
这个类中会有主函数的存在
实体类是可以存在主函数的,但是不推荐
工具类一般不会存在主函数。
静态函数中是否存在this这个关键字?不可能
静态变量,我们在定义的时候,一般而言这个变量是不可修改的 加上final修饰。
对象的内存图解Ⅲ
Javac将Java源代码进行编译生成字节码文件。
Javac将字节码文件加载进虚拟机中 ,开始运行程序。
字节码具体被加载进方法区里面
方法区可以分为非静态方法区和静态方法区。
所有非静态的代码(二进制)内容加载进非静态区
所有静态代码(二进制)内容加载进静态代码区
JVM接下来会根据参数(Java字节码文件名(类名))去静态区找主函数
将主函数代码加载进虚拟机栈进行运行
静态变量与成员变量的区别
生命周期
成员变量随着对象的创建而创建,随着对象的消亡而消亡。
静态变量随着类的加载而创建,随着程序结束而消失。
调用方式
成员变量必须先创建对象再通过对象去调用
静态变量可以被对象调用也可以直接用类调用
储存位置
成员变量存在于堆内存中对象的所属空间里
静态变量存在于静态方法区中类的所属空间里
命名
成员变量-对象的特有属性
静态变量-对象的共有属性 类成员
代码块
代码块{……}
局部代码块:存在于函数当中(包括函数)for(){……} if(){……}
构造代码块:直接在类中出现的{……}
当对象创建一次时,构造代码块执行一次,作用等同于构造函数
静态代码块:直接在类中出现的static{……}
当类被加载的时候,仅仅只执行一次
作用于对类进行一些初始化操作 JDBC
单例模式
设计模式:就是我们的前辈们总结的一些编程技巧
它并不是随着Java的诞生而诞生的
它是由Java的广大使用者总结出来的一套编程经验(常见26种)
单例模式:使用场景是某一个类只能创建一个对象
1.既然只能创建一个对象的话,就不能让外界去创建对象
限制使用new是不现实的
只能从对象的创建流程中考虑,只要有一个步骤不行,对象就创建不出来。
开辟空间分配地址,是由计算机底层觉得的,我们是控制不了的
构造函数执行,只需将构造函数私有化即可
2.既然外界不能创造对象,我们还是得保证对象的创建
所以我们只能在类内部创建对象
Single s = new Single();
它能否写成成员变量的形式?
所以private static
3.内部创建出对象,还是得向外界提供
因为private外界不能直接访问
所以见解,向外界提供一个函数,外界通过调用函数获取对象。
继承
继承的由来:当我们在定义若干个类的时候,发现某一些类中具有相同的属性和行为,那么,我们就可以将这些相同的部分进行抽取,独立生成另外一个类,那么这个生成出来的类我们称之为是父类,其他的容易被抽取内容的类称之为子类。
子类与父类之间就是所谓的继承关系,用extends来表示
在现实生活中,父与子之间是一对多 还是多对多的关系?
在Java中,继承是单继承(一个子类只能有一个父类,但是一个父类可以有若干个子类)
但是在C++当中,继承是多继承的,不太符合实际的社会问题,所以Java更加符合现实编程
(也不代表Java当中就没有多继承的时候! 如果是类与类之间就必须是单继承;如果是接口与接口之间,可以是多继承)
既然有了继承,那么在Java当中就有了继承体系
既然有了父子关系,那么就会产生族谱这个东西
说一点:我们常用的继承体系 异常体系 集合体系 IO体系 网络体系 多线程体系
C->B->A(C就是B的儿子,A就是C的爷爷)
在Java继承体系当中,所有的类的最终父类都是Object! (Object就不存在爸爸了)
如果我们在定义一个类的时候,没有显著声明父类的时候 南无该类的父类默认是Object
*****注意一点:在分析设计继承的时候一定要符合社会社会常识问题
子类和父类之间必须是is a 关系 子类必须是父类的一种同一体系的
所以,千万不要为了获取某个类的功能而把这个类叫爸爸!
子父类中成员变量/静态变量的特点
如果只有子类有 且非私有 那么就调用子类的
如果只有父类有 且非私有 那么就调用父类的
如果子父类都有 且非私有 那么久调用子类的
(成员变量之间是不存在重写的!!!)
子类.属性 顺序 子类对象成员->子类静态->父类成员->父类静态
子类成员函数在内部调用变量时 局部变量->子类对象成员->子类静态->父类成员->父类静态
子父类中构造函数的特点
现象:子类构造函数在调用运行的时候 先执行父类的构造函数
在子类的构造函数当中,有一句默认的super(……)隐藏了,而且这句代码必须在第一行
对super的调用必须是构造器中的第一个
为什么?道理很简单 当儿子问老爸要钱的时候 ,老爸是需要准备一下钱的
既然子类继承自父类 那么必然会继承到父类当中的一些数据
所以,在子类构造函数当中,必须先让父类把这些数据进行一些初始化才能继承给子类
*****注意:父类的构造函数被调用,但不代表父类就被创建对象了!
所以this是当前对象的引用,而super不是父类对象的引用,而是父类空间的引用
并且注意super(……)如果父类没有默认无参构造函数那么子类构造函数中super()失效了
所以在调用父类构造函数时,一定要注意父类构造函数的参数情况!适时修改super(……)
this(……)是当前类中 本类构造函数调用本类构造函数的方式
super(……)是本类构造函数调用父类构造函数的方式
都必须在第一行,那么这两者是否有冲突?
如果本类构造函数中不存在调用关系,那么每一个本类构造函数第一句都是super(……)
如果本类构造函数当中存在调用关系,那么最后被调用的第一句绝对就是super(……)
this(……)的调用是单向的还是递归的? 是单向的,那么最后被调用的第一句绝对就是super(……)
子父类中成员函数的特点:
如果只有子类有 且非私有 南无就调用子类的
如果只有父类有 且非私有 那么就调用父类的
如果子父类都有 且非私有 那么就调用子类的(函数重写)
函数重写:在子父类中,同名函数
函数由什么组成:函数声明(权限 类型 返回值类型 函数名 参数列表)+ 函数体({}里面的内容)
重写的意义在于哪?在于子类继承了父类的函数声明(功能),但是子类可以将该函数的具体实现进行优化或更改
郭德纲会说相声 郭麒麟也会说相声
子承父业!
1.保留父类的功能声明 子类可以进一步优化
2.保留父类的功能声明子类可以重新定义
重写当中应该注意到的一些细节:
1.函数重名 但是参数列表不同 不构成重写关系
2.函数重名 参数列表相同 构成重写关系 返回值类型也必须是一样的
3.子类重写父类函数时,权限>=父类权限
4.当然 如果父类权限为private 子类压根就继承不到 何谈重写
抽象类
抽象类:模糊不清的类 不具体的类
当我们抽取一个父类的时候,发现子类当中的一些共同方法在父类中无法进行具体的实现
并且这些方法只能在子类中具体实现时,父类当中这些函数就只保留函数声明即可,不必写函数体
那么此时这个函数就是抽象函数! 有抽象函数的类就是抽象类
特点:
1.抽象类能否创建对象?不能,对象本身是一个具体的东西 而抽象类中含有不具体的内容
2.抽象类必须作为父类存在吗? 必须 抽象类就是等着被继承呢!
3.抽象类和一般类有什么区别? 就是一般类的特殊情况
唯一的区别只在于抽象类中有抽象函数!!!
4.抽象类中必须有抽象函数吗?不一定AWT界面开发
5.abstract这个关键字不能和那些关键字共存?
private抽象函数就等着被子类重写实现呢!
static 静态是优先于对象存在的,在加载静态数据的时候 肯定是具体的数据
类名.抽象函数是很尴尬的!
如果抽象函数是可以被静态的,那么我们还需要对象干什么?
多态
多种状态:就是指一个对象可以有多种状态(他在继承体系中的位置变化)
D->C->B->A->Animal
位置变化只能是向上变,但不能低于自身
对于C而言可以当作B来看,也可以当作A来看,也可以当作Animal
小明 男人 人 动物
小明的学校开家长会,小明不想让他爸爸去,他让大明去 大明就是小明他哥
大名冒充爸爸去参加家长会的话,那么所表现出来的行为是不是应该符合他爸的行为呢?
不同的视觉有不同的角色
无论是在那个视觉场合,对象本身变了没有?只不过我们需要对象在不同的场合表现出相对应的行为
在Java中,多态代码表现就是父类引用指向子类对象
多态的前提:继承 重写
多态的好处:对代码的功能进行了可扩展性
子类对象可以当作父类对象去使用,但是有限制,只能调用父类函数或重写函数
不能使用子类特有的行为
多态当中 成员变量的特点:只能访问父类中的成员变量
多态当中 成员函数的特点:
如果子类没有重写 且父类中有 调用父类的
如果子类有重写的 调用子类重写的
如果不存在重写 父类没有 那就报错了