Java 复习笔记4 - 面向对象

本文是复习笔记,初学者若是不理解面向对象的概念能够看看个人另外一篇博客:http://www.javashuo.com/article/p-zjxmduaw-ht.htmlhtml

对象的内存

image-20191118185432821

变量定义在栈中,而具体的对象数据则在堆区java

构造方法

构造函数,构造器python

语法要求设计模式

1.函数名称与类名一致
2.不容许有返回值类型 
3.不容许使用return语句返回数据

特色:安全

new 对象时会自动执行 
1.能够重载多个构造方法
2.当咱们没有定义任何构造方法时,会自动生成无参的空构造方法
3.一旦咱们定义类构造方法,系统将不会自动生成

构造方法只能经过两种方式来调用:ide

1.在构造方法中可经过this去调用其余的构造方法
2.经过new 关键字  
//在普通方法中也不能去掉用构造方法 
public class Constructer {
    public Constructer(){
    }
    public Constructer(String name){
        this();  //调用空参数构造方法
    }
}

注意注意:调用其余构造方法只能位于方法的第一行语句;

方法参数查找采起就近原则

当对象建立时对象的全部属性都会被自动初始化

================================

封装

特色:

1.只能经过规定的接口(方法)访问内部数据函数

2.隐藏了内部实现细节,为了隔离复杂度,和提升安全性,提升了类内部的维护性性能

使用:

修改方法或属性的权限为private 私有化测试

为私有的属性提供对外开放(public)的setter 和getter方法;this

public class Test {

    private String name;//该属性被私有化 此时该属性尽在该类内部能够访问

    public Test(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

概念:

包的本质是一个文件夹

当一个项目中出现大量的类是,维护性会下降,咱们能够采起分包的方式来管理 ,将相关的类放到同一个包中,方便管理维护代码,

命名规范:

采用域名倒叙,+模块名称+功能名称

全小写

image-20191119101847961

注意:不一样包中能够存在相同名称的类

import

import用于导入某个类

语法:

1.导入某个包下的某个类 import com.yh.package1.Cat;
2.导入某个包下的全部类 import com.yh.package2.*;

注意:
1.当上述两条语句出现了相同的类名称时(package2中也有Cat),优先使用的是语义更清晰的即import com.yh.package1.Cat
2.第二种语法,仅能导入包下的类,不能导入包下的子包

咱们也能够在代码中使用彻底限定类名来避免冲突:

com.yh.package1.Cat c1 = new com.yh.package1.Cat();
c1.show();

static

static修饰的成员称之为静态成员

特色:

1.可使用类名直接调用(固然对象也能够)

2.而且static修饰的成员属于类名称空间中,也就是说全部该类对象,共享该资源,相反的若是非static修饰的成员是每一个对象独有的,例如每一个人的name属性不一样;

3.静态成员的生命周期跟随的类,类加载时被加载,虚拟机结束时被销毁;

4.在静态方法中不可使用this关键字,由于static修饰的成员比对象先被加载

5.静态方法中只能访问静态成员

代码块

使用一对大括号产生一个代码块,固然了方法也是代码块

1.构造代码块

咱们还能够在类中直接使用{}来定义出一个构造代码快 像下面这样

class Person(){
  {
    //构造代码块,会在建立类的时候先于构造函数执行,没建立一个对象就执行一次
  }  
}

2.普通代码块

class Person{
  void show(){
    //方法体    
    
    {
      // 普通代码块
    }
    
    //方法体
  }
}

3.静态代码块

在类中使用static来修饰的代码块就是静态代码块,其执行时机是在类被加载时,执行一次,后续不在执行,

何时使用?

当咱们须要在使用类以前作一些初始化操做时

问题?

1.代码块是不是局部做用域 ? 是的每一个代码块都是局部的

2.静态代码块中是否能够存在方法? 不能够局部代码块建立方法没有意义

代码块的执行顺序

静态代码块 -> 构造代码块 -> 构造方法

===============================================================

继承

一个类和另外一个类之间的关系,是一种什么是什么的关系(A is aB)

如猪是动物,

好处:子类继承父类能够直接使用父类开放(非private)的已存在成员

image-20191119142825009

使用

extends关键字创建继承关系,一个类只能有一个父类

class Animal{
  String name;
}
class Person extends Animal{
 public void show(){
   System.out.println(this.name);//直接访问父类成员
 }
}

重写/覆盖

override

什么是覆盖:

​ 当子类出现了与父类彻底一致(相同返回值类型,相同名称,相同参数列表)的方法时将产生覆盖

什么时候使用覆盖:

​ 当父类的方法 没法直接知足需求时子类能够覆盖父类已有方法

注意:

1.子类覆盖时方法的返回值类型必须与原始方法返回值类型一致,或者是原始方法返回值类型的子类
2.子类覆盖方法时要求方法的权限必须大于等于原始方法的权限
3.父类的静态成员不会被覆盖,当子类定义了与父类彻底一致的方法时,这个方法实际是属于子类的与父类没有关系
4.当子类出现方法名称与参数列表与父类方法一致时则认为是覆盖,要求返回值类型必须一致或是其子类

也就是说若是子类想定义新的方法要么名称不一样,要么参数列表不一样

补充:子类也能够定义与父类相同的属性,将覆盖父类相同的属性

权限修饰符

修饰符 本类 同包 子类 其余包
public yes yes yes yes
protected yes yes yes no
default yes yes no no
private yes no no no

继承注意事项:构造方法没法被继承

super

当子类覆盖了父类的方法时,按照顺序将优先执行子类中的方法,当咱们须要执行父类方法时

能够在子类中使用super关键字来调用父类的方法

class A{
  public A(){
           
  }
    public show(){
    System.out.println("hello java!");
  }
}

class B extends B{
  public B(){
           super().show();
  }
}
注意:在子类的构造方法中系统会默认添加super(); 若是父类不存在空参构造函数将编译错误,咱们也能够手动的使用super来调用其余的构造函数;
强调:super()必须放在第一行

对象的建立过程

1.加载类(仅发生一次)

2.加载父类静态资源

3.加载子类静态资源

4.执行父类构造代码块

5.执行父类构造函数

6.执行子类构造代码块

7.执行子类构造函数

8.完成

this与super

this:

​ 本类的,属性,方法,构造器

super:

​ 父类的,属性,方法,构造器

注意:this和super不能同时出如今构造函数中

image-20191119212436052

final

翻译为最终,能够修饰类,变量,方法

1.在继承关系中,被final修饰的方法没法被覆盖

2.final修饰变量时,就变成了常量,一旦初始化值不容许修改

3.final修饰类时,表示该类不能被继承

注解

JDK1.5 推出

用于对方法或变量进行说明

image-20191120001010853

源码注解尽在编译完成时自动去除,用于对元素进行标记,方便编译器识别,例如@override

编译时注解指的是编译成class后依然存在的注解

运行时注解指的是,会对程序逻辑产生影响的注解,例如autowrite,transaction等..

=============================================

单例模式

常见23中设计模式,及其分类;

image-20191120005925881

什么是单例:

某个类有且仅有一个实例,那ta就是单例类

image-20191120010309895

java中的实现方式:

1.将构造函数私有化 以禁止外界本身初始化

2.利用static仅加载一次的特性,建立一个静态的对象做为类的私有属性

3.提供访问这个静态对象的方法

按照建立对象时机不一样能够分为两种

1.饿汉式: 直接在声明static属性时建立对象 这种方法是线程安全

class SingleInstance{
  private static SingleInstance obj = new SingleInstance();
  private SingleInstance(){
    //私有化的构造函数 
  }
  public SingleInstance getInstance(){
    return obj;
  }
}

2.懒汉式:在获取对象是若是发现对象为空才建立 这种方法是非线程安全

public class Emperor {
     //定义私有构造方法
    private Emperor(){
        System.out.println("create instance!");
    }
     //定义私有静态类对象
    private static Emperor obj = null;
     //定义公有静态方法返回类内的私有静态对象
     public static Emperor get_instance(){
         if(obj == null){
             obj = new Emperor();
         }
         return obj;
     }
}

优缺点:

优势:

​ 节省空间,提升性能

缺点:

​ 扩展困难

​ 当对象长期不使用,困难会被回收致使异常

使用场景:

当程序须要共享同一份对象的数据时

当每一个对象数据都相同时,则没有必要建立对个对象

当须要保证某些数据的一致性时,例如: 要生成惟一的id,若是每一个对象都有一份本身的生成方式则可能形成数据错误

image-20191120094929672

================================

多态

定义:

​ 多个不一样对象能够响应同一个方法产生各自不一样的行为

分类:

1.编译时多态

​ 指的是在编译阶段就能识别具体要执行的方法是哪个,一般是方法重载,经过参数不一样决定调用哪一个方法

2.运行时多态

​ 只有在运行时才能肯定到底调用哪一个方法

image-20191120095446497

注意下面的内容都是针对运行时多态,是任何OOP语言的共同特性

特色:

出现多态的两个必要条件

1.必须具有继承关系

2.父类指针指向子类对象

ps:在python中没有这两个限制,python是动态类型,编译期间不会检查对象具体类型,更加灵活,可是也增长了风险

向上转型

指的是用父类指针引用子类实例,会自动将子类转换为父类,转换后将隐藏子类特有的成员,只能使用父类中定义的方法和属性,

向下转型

当明确某个对象就是某个子类对象时能够强制转换为子类,如此就能够从新访问子类中独有的成员

当不能明确对象是不是某个类型时可使用 instanceof

if(obj instanceof Objcet){
  System.out.println("obj is  ObjectClass instance");
}else{
  System.out.println("obj not  ObjectClass instance");
}
注意:因为静态方法不能被重写,当子类和父类存在彻底相同的静态方法时,向上转型后对象默认调用的是父类中的静态方法,也就是说不存在多态,调用的方法是明确的,跟随类型的

abstract 抽象类

什么是抽象类:

抽象类指的是类中包含抽象方法的类,

抽象方法指的是没有任何方法体的方法

使用abstract关键字类表示抽象方法 或抽象类

何时使用抽象类:

当父类须要规定子类应该具有某些方法,但父类自己不清楚方法具体如何实现时使用抽象类,

用于提早告诉子类,你应该具有哪些方法

特色:

存在抽象方法的类没法直接实例化产生对象,必须由子类继承并实现全部抽象方法才能实例化子类

如子类没有实现全部抽象方法,那么子类也只能做为抽象类

实例:

abstract class Person{
         abstract public void show();
}

class Student extends Person{
  @Override
  public void show(){
    System.out.println("show");
  }
}

注意:

1.若是设计子类的人清楚的知道本身应该作的事情时(方法),能够不用抽象类,抽象类本质就是限制子类必须怎么怎么滴

2.须要注意final 不能与 abstract 同时出现

interface 接口

接口是一组功能(方法)的定义

接口的做用:

定义一组协议方法,让子类遵循协议中的要求去实现具体的操做,对于使用者而言,只须要掌握接口中定义的方法的使用便可,无需关心,具体是哪个类实现的,更不用关心是如何实现的, 这将类的设计者和类的使用者之间的耦合度大大的下降了,

定义语法:

interface Name {
  
}

特色:

接口本质是一个类,可是与普通类有着众多区别

1.接口中的方法默认都是抽象的不能有方法体

2.接口中定义的变量默认都是静态常量

3.接口中的成员必然是public修饰的,即时没有明确声明

4.接口没法直接实例化

5.接口能够继承一个或多个其余接口

6,一个类能够同时实现多个接口

JDK1.8以后

某些状况下,接口中声明了不少抽象方法,然而子类不须要实现所有,而是想仅实现部分须要的,这种状况在1.8以前是不容许的,除非把这个子类变成抽象的,这不够灵活;

  • 1.8以后,增长了新的方法修饰符,default,能够在接口中直接编写方法体,被default修饰的方法,子类能够选择性的重写
  • 新增特性,能够为方法加上static修饰符

新特性的测试

案例:

package com.yh.src;

interface HDMI {
    static int a = 0;
    // 上述代码等价于public static final int a = 0;

    //抽象方法
    void generalMethod();
    //上述代码等价于public void test();

    //默认方法 子类可选重写
    default void defaultMethod(){
        System.out.println("默认的方法体");
    }
    //静态方法 子类不可重写
    static void staticMethod(){
        System.out.println("静态方法体");
    }
}

class AA implements HDMI {
    @Override
    public void generalMethod() {
        System.out.println("AA Implements generalMethod!");
        HDMI.staticMethod();//静态方法只能有接口名称调用
    }

    @Override
    public void defaultMethod() {
        //super.test1();//没法经过super调用接口中的默认方法
        HDMI.super.defaultMethod();//须要使用接口名称.super来调用
    }
}

public class InterfaceRunner {
    public static void main(String[] args) {
        AA a = new AA();
        a.generalMethod();
        a.defaultMethod();//调用未被重写的默认方法
        //a.staticMethod();//没法直接调用接口中的静态方法
        HDMI.staticMethod();//静态方法只能由接口名称调用

    }
}
default方法总结:
  • default方法子类能够选择是否重写,调用顺序与普通继承相同,优先调用对象重写的方法,
  • 当须要在重写方法中调用接口中的默认实现时使用接口名称.super.方法名称HDMI.super.defaultMethod();
static方法总结:
  • static方法没法被重写,也不能使用super调用,不管在什么位置只能使用接口名称来调用

PS:其实取消抽象方法和接口同样能够实现设计-使用松耦合,直接使用普通类做为父类,子类本身实现该实现的方法,固然了java为何占领企业开发也正是由于其严谨,标准规范,

你会发现,为了提升灵活性接口好像变得和抽象类很是类似,.......干脆像python同样别整这么多约束,,,

接口多实现

与普通类不一样的是一个类能够同时实现多个接口,增长了子类的扩展性

可是也带来了访问不肯定性,

  • 状况1:多个接口存在相同的方法(名称与参数列表相同)
interface IA{
    default void test(){
        System.out.println("IA test!");
    }
}

interface IB{
    default void test(){
        System.out.println("IB test!");
    }
}
//实现类直接编译失败,
class IMA implements IA,IB{

}
解决方案:
//在子类中重写冲突的方法,便可 当须要调用默认方法时,使用`接口名称.super.xx`来调用
class IMA implements IA,IB{

    @Override
    public void test() {
        IA.super.test();
        IB.super.test();
    }
}
  • 状况2:父类中存在相同的方法
interface IA{
      default void test(){
          System.out.println("IA test!");
      }
  }
  
  interface IB{
      default void test(){
          System.out.println("IB test!");
      }
  }
  
  class P{
      public void test(){
          System.out.println("IB test!");
      }
  }
  
  class IMA extends P implements IA,IB{
  }

​ 这种状况下不会出现问题,将直接调用父类中的方法

  • 状况3:多个接口中出现了重复的常量
interface IA{
    int a =10;
    int b =100;
}

interface IB{
    int a =20;
}

class P{
    int a = 20000;
}

class IMA extends P implements IA,IB{
    public void  func(){
        System.out.println(a);//编译错误
        System.out.println(IA.a);//使用接口名称来明确
        System.out.println(super.a);//使super来指定访问父类
        System.out.println(b);//不存在重复的时直接能够访问
        
    }
}

此时子类没法明确选择一个要访问的常量,必须使用接口名称来明确

要访问父类时使用super来明确

补充:

子类对象能够直接访问接口中没有冲突的静态变量,可是没法直接访问静态方法须要使用接口名称调用

===================================

内部类

什么是内部类:

当一个类定义在一个类的范围中,则这个类称之为内部类,与之对应的是包含这个内部类的外部类

普通内部类(较少用)

实例:

class A{//外部类
  class B{ //内部类
    }
}

实例化内部类

public class InnerClass {
    public static void main(String[] args) {
        //实例化内部类方法1
        A.B obj; //定义
        obj = new A().new B();
        //实例化内部类方法2
        A aobj = new A();
        obj = aobj.getInner();
        //实例化内部类方法3
        obj = aobj.new B();
    }
}
class A{
    public B getInner() {
        return new B();
    }
    class B{
    }
}

内部类访问外部类的成员

public class InnerClass {
    public static void main(String[] args) {
        //实例化内部类方法1
        A.B obj; //定义
        obj = new A().new B();//实例化
        obj.test();//调用方法访问外部类的成员
    }
}
class A{
    int inta = 100;
    int intb = 200;
    public B getInner() {
        return new B();
    }
    class B{
        int intb = 300;
        public void test(){
            System.out.println(inta);//100 能够直接访问外部类成员
            System.out.println(intb);//300 冲突时优先访问内部
            System.out.println(A.this.intb);//200 冲突时指定访问外部
        }
    }
}

外部类访问内部类成员

class A{
    int inta = 100;
    int intb = 200;
    public void accessInner() {
        B b = new B();//实例化
        b.test();//访问
    }

    class B{
        int intb = 300;
        public void test(){
            System.out.println(inta);
            System.out.println(intb);
            System.out.println(A.this.intb);
        }
    }
}

外部类访问内部类成员时须要先实例化内部类的对象,经过对象访问便可

注意:普通内部类中不能包含任何static修饰的成员

静态内部类 (不经常使用)

能够将内部类使用static修饰,此时他就是静态内部类

特色:

  • 与普通内部类的不一样之处,与static修饰的其余成员有着相同的特征:

  • 在类被加载时就一块儿加载,因此,能够直接实例化,不须要先实例化外部类;

  • 一样的在静态内部类中不能够直接访问非静态修饰的外部类成员,也不能够访问外部类的this;
  • 静态内部类中的能够包含静态成员而且也能够直接调用(外部类.内部类.属性);普通类中不能够有静态成员;

实例:

public class InnerClass2 {
    public static void main(String[] args) {
        System.out.println(OUT.IN.a);//直接访问静态内部类的静态成员
        OUT.IN.test();//访问静态内部类方法
    }
}

class OUT{
    static int a = 10;
    String name = "jack";
    static class IN{
        static int a = 100;
        static public void test(){
            System.out.println(a);//优先访问内部类
            System.out.println(OUT.a);//指定访问外部类
            System.out.println(new OUT().name);//访问外部非静态成员
        }
    }
}

局部内部类 (不经常使用)

定义在方法中的类称为局部内部类

意义不大..没啥用..略过了

class A{
    public void test(){
    class B{
      //局部内部类
        }
  }
}

特色:不容许使用static修饰局部内部类,和其中的任何成员,要使用该类必须做为返回值return出去

匿名内部类 (很经常使用)

什么是匿名内部类

字面意思就是没有名字的类

经过new 实例化接口或是抽象类,同时提供相应的方法实现

实例:

interface USB{
  void open();
}

class PC {
    public void working(){
        //此处须要一个实现了USB接口的对象来完成操做
    //咱们能够定义一个类实现USB接口先下面的A类同样
    //而后实例化A对象来完成OPEN操做
    new A().open();
        //咱们也可使用匿名内部类的方式来简化操做 
    new USB(){
              public void open(){
            System.out.println("OPEN......");
        }
    }.open;
  }
}
class A implements USB{
  public void open(){
    System.out.println("OPEN......");
  }
}

在上面的例子中咱们能够看出匿名内部类有如下优势:

  • 代码更加紧凑

  • 使用更便捷

固然匿名内部类的缺点也很明显:

  • 这个类产生的对象也是没有名字的,只能在定义的地方使用一次
  • 语法格式看起来比较乱

何时使用匿名内部类

1.临时须要某个接口或是抽象类的独享完成某个功能

2.当整个任务的部分代码已经完成,可是剩下一部分关键代码须要由使用这个功能的人来完成

案例:

PC类的working方法须要传入一个USB接口对象才能完成整个任务

interface USB{
    void open();
}
public class PC {
    public static void main(String[] args) {
        new PC().working(new AP());//之前的方式:定义类实现方法而后实例化
        new PC().working(new USB() {//匿名内部类的方式:在当前位置完成上述所有操做
            @Override
            public void open() {
                System.out.println("open on NonNameClass");
            }
        });
    }
    public void working(USB usb){
        usb.open();
    }
}
class AP implements USB{
    public void open(){
        System.out.println("OPEN......on AP");
    }
}

PS:我的以为匿名内部类+匿名对象其实 与OC中的block,python中的函数对象要完成的事情是同样的,即方法的使用者实现部分代码,而后做为参数传给某个方法(本质就是回调机制),可是JAVA中定义方法必须借助类,因此才有了匿名内部类这么一说,归根结底是由于JAVA强制面向对象致使的;

补充:匿名内部类中可使用构造代码块

代码设计规范----单一功能职责:

有且只有一个引发功能变化的缘由
若是在一个类当中,承担的职责越多,耦合度越高,这意味着这个类的重用性越低
相关文章
相关标签/搜索