(六)JAVA学习笔记--面向对象编程

1. 面向对象

  • 面向对象思想:
    不同于面向过程思想多用于处理一些步骤清晰简单的问题
    面向过程适合处理复杂,需要多人协作的问题.

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思维去处理。

1.1 类和对象的关系

  • 是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。

动物、植物、手机、电脑… Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为

  • 对象是抽象概念的具体实例

张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。

Hero h = new Hero() 创建了一个Hero对象,并且引用变量h来指向它。
多个引用可以指向同一个对象,但是一个引用同一时间只能指向一个对象。

  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。

一个类里面只有属性和方法。

2. 面向对象编程

面向对象编程(Object-Oriented Programming, OOP):

  • 本质以类的方式组织代码,以对象的组织(封装)数据
  • 核心抽象
  • 三大特性:封装、继承、多态

2.1 封装

  • 高内聚,低耦合:高内聚就是将类的内部数据操作细节自己完成,不允许外部干涉。低耦合就是尽量暴露少量的方法给外部使用。
  • 数据的隐藏:禁止访问一个对象中数据的实际表示,而是通过操作接口来访问。属性的私有: private;get/set;

2.2 继承

  • 继承
    • java中只有单继承,没有多继承
    • 继承是类和类之间的关系,除此之外还有依赖、组合、聚合等
    • 继承关系的两个类分别为:子类(派生类)、父类(基类)
    • 子类和符类在意义上有“is a”的关系
   子类名+extend+父类名

2.2.1 继承的特性

  • 子类拥有父类非 private 的属性、方法。
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。
  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
  • 如果父类是抽象类,那么子类一定要实现父类的抽象方法,如果父类是普通类,那么子类可以不重现父类的方法。
    java不支持多继承,但是支持多重继承
    在这里插入图片描述

2.2.2 继承关键字

extend关键字:java中类是单一继承,所以extend只能继承一个类,可以

public class Animal {
    
     
    private String name;   
    private int id; 
    public Animal(String myName, String myid) {
    
     
        //初始化属性值
    } 
    public void eat() {
    
      //吃东西方法的具体实现  } 
    public void sleep() {
    
     //睡觉方法的具体实现  } 
} 
public class Penguin  extends  Animal{
    
     
}

implements关键字:使java具有多继承的特性,使用范围为类继承接口的情况,实现了多个接口的继承()接口跟接口之间采用逗号分隔。

public interface A {
    
    
    public void eat();
    public void sleep();
}
public interface B {
    
    
    public void show();
}
public class C implements A,B {
    
    
}

super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。

super使用的注意点:
1.super 调用父类的构造方法,必须在构造方法的第一个
2.super 必须只能出现在子类的方法或者构造方法中!
3.super 和 this 不能同时调用构造方法!

class Animal {
    
    
  void eat() {
    
    
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
    
    
  void eat() {
    
    
    System.out.println("dog : eat");
  }
  void eatTest() {
    
    
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
    
    
  public static void main(String[] args) {
    
    
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。

2.3 多态

多态可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

  • 多态
    • 多态是方法的多态,属性没有多态性
    • 父类和子类,有联系才能转换,否则会发生异常,类型转换异常:ClassCastException
    • 存在条件:继承关系

父类的抽象方法,即abstract修饰的方法需要重写,父类为普通方法可以重写也可以不重写。不能重写的情况:
1.static方法,它属于类,不属于实例
2.final常量,被final修饰的无法修改,属于常量池
3.private私有方法,不能被重写

2.3.1 操作符的多态

同一个操作符在不同情境下,具备不同的作用
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接

2.3.2 类的多态

1.父类的引用可以指向子类,但不能调用子类独有的方法。
2.方法的调用只和左边定义的数据类型有关,和右边关系不大。
3.动态编译:类型:可扩展性更强
4.即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)

  • 多态存在的条件
    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象
//子类能调用的方法都是自己的或继承父类的
  Student s1 = new Student();//子类
  //父类可以指向子类,但是不能调用子类独有的方法
  Person s2 = new Student();//父类
  Object s3 = new Student();
  /*一个对象的实际类型是确定的
  可指向的引用类型不确定,父类的引用指向子类
  */
  //但是子类重写父类方法后,指向子类的方法
  s2.run();//父类有,子类没有,子类继承父类方法
  s1.run();//子类重写后,执行子类方法
  s1.eat();//子类独有方法
  ((Student) s2).eat();
  //父类不能调用子类独有方法,会被强制转换为子类

判断对象类型
instanceof:判断一个对象是什么类型
eg:System.out.println(x instanceof y); :true or false (能不能编译通过,看x所指向的实际类型是不是y的子类型)
转换

  • 父类引用指向子类的对象,不可以子类引用指向父类。
  • 把子类转换为父类,向上转型;
  • 把父类转换为子类,向下转型,强制转换(可能会丢失方法)
  • 方便方法的调用,减少重复的代码,简介
//类型之间的转化 : 父---子
//高               低
Person s1 = new Student();
//高转低可以直接转;低转高,需要强制转
//
Student s2 = (Student) s1;
s2.go();
//或((Student) s1).go();

2.4 static关键字详解

静态方法在类的时候就已经加载了
static加上方式叫静态方式,加上属性叫静态属性

2.4.1 静态属性

public class Student  {
    
    
      private static int age;//静态的变量,可以被类中共享,多线程比较常用
      private double score;//非静态变量
  public static void main(String[] args) {
    
    
      Student s1 = new Student();
      System.out.println(s1.age);//通过方法可以正常调用
      System.out.println(s1.score);
      System.out.println(Student.age);//静态变量,可以直接用类名进行调用
      //System.out.println(Student.score);//非静态变量不可以。
  }
}

2.4.2 静态方法

public class Student  {
    
    
  public void run(){
    
    
      Student.go();
  }
  public static void go(){
    
    
  }
  public static void main(String[] args) {
    
    
      Student.go();//静态方法不需要 new 可以直接调用
      //由于静态方法在类生成的时候就已经存在,所以可以调用静态的
      //Student.run(); 但不能调用非静态的,非静态方法需要 new 出来
      new Student().run();
  }//注解和反射
}

2.4.3 代码块

{
    
    
        //代码块(匿名代码块)
    }//创建对象的时候就已经创建了,在构造器之前
    static {
    
    
        //静态代码块
    }//在类一加载就已经执行,而且只加载一次

2.4.4 加载顺序

static只执行一次

public class Demo03 {
    
    
    //第二加载;适用于赋初值
    {
    
    
        System.out.println("匿名代码块");
    }
    //第一加载;只在第一次执行
    static{
    
    
        System.out.println("静态代码块");
    }
    //第三加载
    public Demo03() {
    
    
        System.out.println("构造方法");
    }
    public static void main(String[] args) {
    
    
        Demo03 s1 = new Demo03();
        System.out.println("==========");
        Demo03 s2 = new Demo03();
        //第二次执行static不在执行
         }
    }

2.4.5 静态导入包

静态导入包后可以直接调用其方法:
//静态导入包

import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Demo03 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println((int)(Math.random()*50));
        //random()随机值,整数,范围(0-50)
        //使用静态导入包后可以直接System.out.println(random());
        System.out.println(PI);
    }
}

2.5 抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法,这样的方法就叫抽象方法,使用修饰符“abstract",当一个类有抽象方法的时候,该类必须被声明为抽象类。

  • abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
  • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
  • 抽象类,不能使用 new 关键字来创建对象,它是用来让子类继承的。
  • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
  • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法;否者该子类也要声明为抽象类,然后由子类实现抽象方法
//abstract 抽象类 类 extends,单继承;       (接口可以多继承)
public abstract class Action {
    
    
   //约束~有人帮我们实现
   //abstract,抽象方法,只有方法名字,没有方法的实现!
   public abstract void doSomething();
}

//抽象类的所有方法,必继承了它的子类,都必须要实现它的方法
public class A extends Action {
    
    
   //除非子类也是抽象方法,那就由子类实现
   @Override
   public void doSomething() {
    
    
   }
}

2.6 接口

2.6.1 定义

普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:属于一种约定
声明类的关键字是class,声明接口的关键字是interface

2.6.2 作用

  • 约束,规范
  • 定义一些方法,让不同的人实现。多个人完成共同的工作。
  • 接口中所有默认的方法public abstract
  • 所有常量默认public static final
  • 接口不能被实例化,接口中没有构造方法。
  • 可以实现多个接口
  • 必须要重写接口中的方法。
  • 声明接口interface,实现接口implements,可以实现多个方法
//interface 接口定义的关键字;接口都需要实现类
public interface Uservrvice {
    
    
    // public void ss(){ }     报错;接口内不能写方法
    //接口中的所有定义其实都是抽象的,默认 public abstract
    public abstract void run();
         void add(String name);
         void delete(String name);

    //接口还可以定义变量,所有定义的属性都是静态的常量
   public static final int AGE = 99;
                       int ABC = 99;
}

2.6.3 抽象类和接口的区别

抽象类 接口
子类只能继承一个抽象类,不能继承多个 子类可以实现多个接口
抽象类可以定义public,protected,package,private、静态和非静态属、final和非final属性 接口中声明的属性,只能是public、静态、final的即便没有显式的声明

2.7 内部类

内部类是指在一个外部类的内部再定义一个类,内部类一般来说包括这四种:成员内部类、局部内部类、静态内部类和匿名内部类。

  • 内部类可以是静态static的,也可以用public,defult,protected和private修饰

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。

2.7.1 成员内部类

最普通的内部类,定义于另一个类的内部,特点如下:

  • 外部类想要访问成员内部类,需要先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
public class Winner {
    
    
    String name;
    int age;
    String country;
    class winGame{
    
    
        int score;
        //非静态内部类,只有在外部对象存在才有意义
        //当比赛的人存在,他的胜利才有意义
        public void win(){
    
    
            if (score>11) {
    
    
                System.out.println(name + "赢得了比赛");
            }
            else
                System.out.println(name + "尚未赢得比赛");
        }
    }

    public static void main(String[] args) {
    
    
        Winner Deng = new Winner();
        Deng.name ="邓丽萍";
        //实例化一个对象,并且在此对象的基础上建立实例化内部类
        winGame sco = Deng.new winGame();
        sco.score = 12;
        sco.win();
    }
}
  • 成员内部类就是作为外部类的成员,可以无条件访问外部类所有成员和方法,包括private成员和静态成员
  • 成员内部类不能含有static的变量和方法,因为成员内部类必须先创建了外部类,才能创建它自己的类。
class Outter {
    
    
    private double radius = 0;
    public Outer(double radius){
    
        //有参的构造方法
        this.radius =radius;
        getDrawInstance().drawShape();//得到成员内部类的对象
    }
    public Draw getDrawInstance() {
    
    
        return new Draw();
    }
    class  Draw{
    
    
        public void drawShape(){
    
    
            System.out.println(radius);
        }
    }

    public static void main(String[] args) {
    
    
        Outter out = new Outter(1.2);
    }
}

小结:
 内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。

  • private修饰,则只能在外部类的内部访问
  • 如果用public修饰,则任何地方都能访问;
  • 如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;
  • 如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。个人理解:由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

2.7.2 局部内部类

局部内部类——就是定义在一个方法或者一个作用域里面的类
特点:主要是作用域发生了变化,只能在自身所在方法和属性中被使用

//在局部位置,可以创建内部类对象,通过对象调用和内部类方法
 class Outter {
    
    
     private int age = 10;
     public void method(){
    
    
         final int age1 = 20; //final修饰后,该值使用完毕后就消失,即age1存储的是常量20,而不是变量名
         class Inner{
    
    
             public void show(){
    
    
                 System.out.println(age);
                 System.out.println(age1);
             }
         }
         Inner i = new Inner();
         i.show();
     }

    public static void main(String[] args) {
    
    
             Outter outter.method();
    }
 }

2.7.3 静态内部类

static是不能用来修饰类的,但是成员内部类可以看做外部类中的一个成员,所以可以用static修饰,这种用static修饰的内部类我们称作静态内部类,也称作嵌套内部类.
但是因为静态内部类不需要外部类的实例,因此不能使用外部类的非static成员变量和成员方法

class Outter {
    
    
    int age = 10;
    static int age2 = 20;
    public Outter() {
    
    
    }

    static class Inner {
    
    
        public void method() {
    
    
           // System.out.println(age);//错误
            System.out.println(age2);//正确
        }
    }

    public static void main(String[] args)  {
    
    
        Outter.Inner inner = new Outter.Inner();
        inner.method();
    }
}

2.8.4 匿名内部类

匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
它继承了一个父类或者接口后,直接实例化一个抽象类,并且实现该抽象方法
这样的类,叫做匿名类

abstract class Person {
    
    
    public abstract void eat();
}
 
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Person p = new Person() {
    
    
            public void eat() {
    
    
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}

在接口上使用匿名内部类:

interface Person {
    
    
    public void eat();
}
 
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Person p = new Person() {
    
    
            public void eat() {
    
    
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}

区别:
不适应匿名内部类

public class Test {
    
     
     public static void main(String[] args) {
    
     
//如果我们需要使用接口中的方法,我们就需要走3步,1、实现接口 2、创建实现接口类的实 例对象 3、通过对象调用方法 
        //第二步 
        Test02 test = new Test02(); 
       //第三步 
        test.method(); 
   } 
}//接口Test1 
interface Test01{
    
     
           public void method(); 
}
//第一步、实现Test01接口 
class Test02 implements Test01{
    
     
@Override 
     public void method() {
    
     
                System.out.println("实现了Test接口的方法"); 
   } 
}

使用匿名内部类

public class Test {
    
    
public static void main(String[] args) {
    
    
//如果我们需要使用接口中的方法,我们只需要走一步,就是使用匿名内部类,直接将其 类的对象创建出来。
    new Test1(){
    
     
        public void method()
              {
    
     
                 System.out.println("实现了Test接口的方法");
                } 
                 }.method(); 
                 } 
      }
 interface Test1{
    
     
 public void method(); 
 }

解析:其实只要明白一点,new Test1(){实现接口中方法的代码}; Test1(){…}这个的作用就是将接口给实
现了,只不过这里实现该接口的是一个匿名类,也就是说这个类没名字,
只能使用这一次,我们知道了这是一个类, 将其new出来,就能获得一个实现了Test1接口的类的实例
对象,通过该实例对象,就能调用该类中的方法了,因为其匿名类是在一个类中实现的。

猜你喜欢

转载自blog.csdn.net/CHAINQWE/article/details/115329755