java 父类赋值子类_JAVA学习第二天

java的四大特性:抽象,继承,封装,多态

2945390d316eed631fb2791798a2b336.png

面向对象:

面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象(即万物皆对象)。 

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)的主要思想是把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一个事物在整个解决问题的步骤中的行为。面向对象程序设计中的概念主要包括:对象、类、数据抽象、继承、动态绑定、数据封装、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。

对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是动作。对象则是数据和动作的结合体。对象不仅能够进行操作,同时还能够及时记录下操作结果。

方法是指对象能够进行的操作,方法同时还有另外一个名称,叫做函数。方法是类中的定义函数,其具体的作用就是对对象进行描述操作。 

继承简单地说就是一种层次模型,这种层次模型能够被重用。层次结构的上层具有通用性,但是下层结构则具有特殊性。在继承的过程中类则可以从最顶层的部分继承一些方法和变量。类除了可以继承以外同时还能够进行修改或者添加。通过这样的方式能够有效提高工作效率。在这里举一个例子,当类X继承了类Y后,此时的类X则是一个派生类,而类Y属于一个基类。 继承是从一般演绎到特殊的过程,可以减少知识表示的冗余内容,知识库的维护和修正都非常方便。更有利于衍生复杂的系统。

类是具有相同特性(数据元素)和行为(功能)的对象的抽象。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。类具有属性,它是对象的状态的抽象,用数据结构来描述类的属性。类具有操作,它是对象的行为的抽象,用操作名和实现该操作的方法来描述。 类映射的每一个对象都具有这些数据和操作方法,类的继承具有层次性和结构性,高层次对象封装复杂行为,具体细节对该层次知识保持透明,可以减小问题求解的复杂度。

封装是将数据和代码捆绑到一起,对象的某些数据和代码可以是私有的,不能被外界访问,以此实现对数据和代码不同级别的访问权限。防止了程序相互依赖性而带来的变动影响,面向对象的封装比传统语言的封装更为清晰、更为有力。有效实现了两个目标:对数据和行为的包装和信息隐藏。 

多态是指不同事物具有不同表现形式的能力。

多态机制使具有不同内部结构的对象可以共享相同的外部接口,通过这种方式减少代码的复杂度。一个接口,多种方式。 

动态绑定指的是将一个过程调用与相应代码链接起来的行为。

动态绑定是指与给定的过程调用相关联的代码只有在运行期才可知的一种绑定,它是多态实现的具体形式。 

消息传递:对象之间需要相互沟通,沟通的途径就是对象之间收发信息。消息内容包括接收消息的对象的标识,需要调用的函数的标识,以及必要的信息。消息传递的概念使得对现实世界的描述更容易。 

面向对象的方法就是利用抽象、封装等机制,借助于对象、类、继承、消息传递等概念进行软件系统构造的软件开发方法。

他们的特征:

面向对象的三大特点(封装,继承,多态)缺一不可。

封装性
封装性的概念
封装性是面向对象思想的三大特征之一,封装就是隐藏实现细节,仅对外提供访问接口。实现细节部份包装、隐藏起来的方法。
封装有:属性的封装、方法的封装、类的封装、组件的封装、模块化封装、系统级封装…
封装的好处:模块化、信息隐藏、代码重用、插件化易于调试、具有安全性
封装的缺点:会影响执行效率

封装之前:属性都可以直接访问和修改
class User{
  String name;
  int age;
}

封装之后:
class User{
  //属性是成员变量,私有化属性,使得其他对象不能直接访问属性
  private String name;
  private int age;
  //参数及方法内定义的变量是局部变量
  public void setName(String name){
  this.name = name;}
  public String getName(){
  return name;}
}

成员变量和局部变量的区别
a、在类中的位置不同
成员变量:在类中定义
局部变量:在方法中定义或者方法的参数
b、在内存中的位置不同
成员变量:在堆内存(成员变量属于对象,对象进堆内存)
局部变量:在栈内存(局部变量属于方法,方法进栈内存)
c、生命周期不同
成员变量:随着对象的创建而存在,随着对象的销毁而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
d、初始化值不同
成员变量:有默认初始化值,引用类型默认为null
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用
注意:
局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。

继承
继承是面向对象三大特征之一
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类

被继承的类称为父类(超类),继承父类的类称为子类(派生类)
通过继承可以实现代码重用
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
构造器而言,它只能够被调用,而不能被继承,可以通过使用super()进行调用,
对于继承而已,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器(通过super()),而且必须是在子类构造器中做的第一件事(第一行代码)。
对于protected而言,它指明就类用户而言,他是private,但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的
Java 的继承是单继承,但是可以多重继承,


语法:[访问权限] class 子类名 extends 父类名{
  类体定义;
}

示例:
public class Dog{
private String name;
private String sex;
public void eat(){System.out.println(“吃饭”);}
}
public class HomeDog extends Dog{
  //类的定义
}
public class HuskyDog extends Dog{
  //类的定义
}

protected(受保护的访问权限修饰符,用于修饰属性和方法,使用protected修饰的属性和方法可以被子类继承)


(1)继承是发生在多个类之间
(2)继承使用关键字extends
(3)JAVA只能单继承,允许多层继承
(4)被继承的类叫父类(超类),继承父类的类叫子类(派生类)
(5)在父类中的非私有属性和方法可以被子类继承
(6)protected(受保护的访问权限修饰符),修饰的属性或方法可以被子类继承
(7)构造方法不能被继承
(8)创建对象会调用构造方法,调用构造方法不一定就是创建对象
(9)实例化子类对象,会先调用父类的构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super(...)来调用父类的带参构造方法,super也只能在子类构造方法中的第一句

继承的好处:
a、提高代码的复用性
b、提高代码的维护性
c、让类与类之间产生关系,是多态的前提
继承的缺点:增强了类与类之间的耦合性

 对象初始化

public class CodeBlockTest {
  public static void main(String[] args) {
  Child child = new Child();
}
}
class Father {
  public static String fatherStr1 = "fatherStr1(静态字段初始化值)";
  public String fatherStr2 = "fatherStr2(字段初始化值)";
  static {
    System.out.println("父类静态代码块:" + fatherStr1);
    fatherStr1 = "fatherStr1(静态代码块赋值)";
  }
  {
    System.out.println("父类构造代码块:" + fatherStr2);
    fatherStr2 = "fatherStr2(构造代码块赋值)";
  }
  public Father() {
  System.out.println("父类构造函数块:" + fatherStr2);
  fatherStr2 = "fatherStr2(构造函数赋值)";
  }
}


class Child extends Father {
  public static String childStr1 = "childStr1(静态字段初始化值)";
  public String childStr2 = "childStr2(字段初始化值)";
  static {
    System.out.println("子类静态代码块:" + childStr1);
    childStr1 = "childStr1(静态代码块赋值)";
  }
  {
    System.out.println("子类构造代码块:" + childStr2);
    childStr2 = "childStr2(构造代码块赋值)";
  }
  public Child() {
  System.out.println("子类构造函数:" + childStr2);
  childStr2 = "childStr2(构造函数赋值)";
  }
}
// 输出结果:
// 父类静态代码块:fatherStr1(静态字段初始化值)
// 子类静态代码块:childStr1(静态字段初始化值)
// 父类构造代码块:fatherStr2(字段初始化值)
// 父类构造函数块:fatherStr2(构造代码块赋值)
// 子类构造代码块:childStr2(字段初始化值)
// 子类构造函数:childStr2(构造代码块赋值)

通过每执行一个代码块或构造函数,输出字段在上一代码块执行后的值,以此来探究对象的初始化顺序。
由目前的输出结果可知,对于对象的初始化顺序,我们可以得出以下结论:
1. 父类静态字段初始化
2. 父类静态代码块、子类静态字段初始化 (接下来探究两者的顺序)
3. 子类静态代码块
4. 父类普通字段初始化
5. 父类构造代码块
6. 父类构造函数
7. 子类普通字段初始化
8. 子类构造代码块
9. 子类构造函数

public class CodeBloacTest2 {
  public static void main(String[] args) {
  Child child = new Child();
}
}
class Father {
  public static String fatherStr = "(静态字段初始化值)";
  static {
    System.out.println("父类静态代码块:fatherStr" + fatherStr);
    fatherStr = "(静态代码块赋值)";
  }
}


class Child extends Father {
  public static String childStr = fatherStr;
  static {
    System.out.println("子类静态代码块:childStr = fatherStr" + childStr);
    childStr = "(静态代码块赋值)";
  }
}
// 输出结果:
// 父类静态代码块:fatherStr(静态字段初始化值)
// 子类静态代码块:childStr = fatherStr(静态代码块赋值

我们在子类静态字段childStr初始化的时候,赋的是父类静态字段fatherStr的值。由输出结果可知,childStr初始化后的值是父类静态代码块执行后赋予fatherStr的值。由此可知两者的执行顺序为:父类静态代码块==>子类静态字段初始化

初始化顺序(最终结果):

父类静态字段初始化
父类静态代码块
子类静态字段初始化
子类静态代码块
父类普通字段初始化
父类构造代码块
父类构造函数
子类普通字段初始化
子类构造代码块
子类构造函数

引用:https://www.jb51.net/article/111157.htm

方法的重写
方法重写(overriding method)
在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想做一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,以创建的对象类型为准,会调用谁的方法。


重写特性:
a、发生在子父类中,方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)
b、子类抛出的异常不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)
c、子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
d、父类中的方法若使用private、static、final任意修饰符修饰,那么,不能被子类重写。

面试题 :overloading与overriding的区别?

super关键字
可以理解为对父类的引用,使用super来调用父类的属性,方法,和构造方法
super可以完成以下的操作:
a、使用super调用父类中的属性,可以从父类实例处获得信息。
b、使用super调用父类中的方法,可以委托父类对象帮助完成某件事情。
c、使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,默认调用父类的无参构造方法,比如:super();

final关键字
使用final关键字完成以下的操作:
a、使用final关键字声明一个常量
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的
b、使用final关键字声明一个方法
该方法为最终方法,且只能被子类继承,但是不能被子类重写。
c、使用final关键字声明一个类
该类就转变为最终类,没有子类的类,fianl修饰的类无法被继承。
d、在方法参数中使用final,在该方法内部不能修改参数的值(在内部类中详解)

当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化
引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

可以参考:https://www.cnblogs.com/xiaoxi/p/6392154.html

抽象类
抽象类的基本概念和规则:使用abstract关键字声明,描述的特征或者行为是很多对象共有的
           抽象类可以没有抽象方法,有抽象方法的类必须是抽象类,用abstract关键字声明          

           非抽象类继承抽象类必须实现所有抽象方法

           抽象类可以继承抽象类,可以不实现父类抽象方法。 

           抽象类可以有方法实现和属性 

           抽象类不能被实例化 

           抽象类不能声明为final 

           抽象类可以有构造方法

接口
接口的定义格式:
interface 接口名称{
  全局常量 ;
  抽象方法 ;

接口的使用规则:
(1)定义一个接口,使用interface关键字
(2)在一个接口中,只能定义常量、抽象方法,JDK1.8后可以定义默认的实现方法
(3)接口可以继承多个接口:extends xxx,xxx
(4)一个具体类实现接口使用implements关键字
(5)一个类可以实现多个接口
(6)抽象类实现接口可以不实现接口的方法
(7)在接口中定义的方法没有声明 访问修饰符,默认为public
(8)接口不能有构造方法
(9)接口不能被实例化
java8新增
(1)增加了default方法和static方法,这两种方法完全可以有方法体
(2)default方法属于实例,static方法属于类(接口)
(3)接口中的静态方法不会被继承,接口中的静态变量会被继承

 java中抽象类和接口的区别也就出来了!

   1、实现方式不一样 :

            抽象类要被子类继承(extends),接口要被类实现(interface)

ec80eb8d546513ad87470f047d1736b3.png

   2、能做的事情不一样:接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。

c1ce8512f8e9ead6871901ca77b1ccef.png

3、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

4、接口是设计的结果,抽象类是重构的结果。

5、抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。

6、抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。

7、抽象类主要用来抽象类别,接口主要用来抽象功能。

多态性 多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。程序运行期间才确定具体的类。大概可以分为两类:(1)方法的重载与重写(2)对象的多态性
如用父类的引用指向子类对象(用大的类型去接受小的类型,向上转型、自动转换) 

在编程时针对抽象类型的编写代码,称为面向抽象编程(或面向接口编程)父类通常都定义为抽象类、接口

对象的多态性:
对象多态性是从继承关系中的多个类而来,
向上转型:将子类实例转为父类引用
格式:父类 父类对象 = 子类实例 ; 自动转换
以基本数据类型操作为例:int i = ‘a' ; 
(因为char的容量比int小,所以可以自动完成)

向下转型:将父类实例转为子类实例 
格式:子类 子类对象 = (子类)父类实例 ;强制转换
以基本数据类型操作为例:char c = (char)97; 
因为整型是4个字节比char 2个字节要大,所以需要强制完成
a、方法的重载与重写就是方法的多态性表现
b、多个子类就是父类中的多种形态
c、父类引用可以指向子类对象,自动转换
d、子类对象指向父类引用需要强制转换(注意:类型不对会报异常)
e、在实际开发中尽量使用父类引用(更利于扩展)


指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,
该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,
必定是使用子类中定义的这些方法(动态连接、动态调用)。

对于面向对象而言,多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性

java实现多态有三个必要条件:继承、重写、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法

基于继承实现的多态
继承是通过重写父类的同一方法的几个不同子类来体现的
对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,
执行相同动作产生的行为也就不同。

基于接口实现的多态
指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。

instanceof关键字
instanceof 是用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常(ClassCastException)。

语法格式如下:
对象 instanceof 类型--返回boolean类型值

示例:
if(homeChicken instanceof Chicken){
  //...
}
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false
父类的设计法则
通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐,那么如何去设计一个父类呢?
a、父类通常情况下都设计为抽象类或接口,其中优先考虑接口,如接口不能满足才考虑抽象类。

b、一个具体的类尽可能不去继承另一个具体类,这样的好处是无需检查对象是否为父类的对象。

内部类
内部类就是在一个类的内部定义的类。
成员内部类:内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象
内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段
成员内部类格式如下:
class Outer {
  class Inner{}
}
编译上述代码会产生两个文件:
Outer.class和Outer$Inner.class。

在外部创建内部类对象
内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。
那么,根据内部类生成的*.class文件:Outer$Inner.class
“$” 符号在程序运行时将替换成“.”
所以内部类的访问:通过“外部类.内部类”的形式表示。

Outer out = new Outer() ;// 产生外部类实例
Outer.Inner in = null; // 声明内部类对象
in = out.new Inner() ; // 实例化内部类对象

局部内部类
内部类可以作为一个类的成员外,还可以把类放在方法内定义(不常用,匿名内部类可以显示局部内部类的功能)。
在局部内部类里面可以访问外部类对象的所有访问权限的字段,而外部类却不能访问局部内部类中定义的字段
注意:
a、局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
b、局部内部类对象不能使用该内部类所在方法的非final局部变量。
class Outer {
  public void doSomething(){
  class Inner{
    public void seeOuter(){}
  }
  }
}


静态内部类
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法。
静态内部类中也无法访问外部类的非静态成员
class Outer{
  static class Inner{}
}
class Test {
  public static void main(String[] args){
  Outer.Inner n = new Outer.Inner();
}
}

匿名内部类
匿名内部类就是没有名字的内部类。
匿名内部类的三种情况:
(1)继承式的匿名内部类
(2)接口式的匿名内部类
(3)参数式的匿名内部类

在使用匿名内部类时,要记住以下几个原则:
(1)不能有构造方法,只能有一个实例。
(2)不能定义任何静态成员、静态方法。
(3)不能是public,protected,private,static。
(4)一定是在new的后面,用其隐含实现一个接口或继承一个类。
(5)匿名内部类为局部的,所以局部内部类的所有限制都对其生效

局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,
方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已无法使用了,
如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。

注意:在jdk1.8中取消了在局部内部类中使用的变量必须显示的使用final修饰,编译器默认会为这个变量加上final

内部类的作用
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,
内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

依赖外部类对象的:成员内部类,方法内部类,匿名内部类
静态内部类不依赖外部类的对象。所以,我们在项目中优先考虑选择静态内部类(不会产生内存泄露)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值