7. JavaSE 面向对象

面向对象

初识面向对象

面向过程&面向对象

  • 面向过程思想

    • 步骤清晰简单,第一步做什么,第二步做什么
    • 面向过程适合处理一些比较简单的问题
  • 面向对象思想

    • 物以类聚,分类的思维模式,思考问题的解决首先需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类 下的细节进行面向过程的思索。
    • 面向对象适合处理复杂问题,适合处理需要多人协作的问题。
  • 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

什么是面向对象

  • 面向对象编程(Object-Oriented programming, OOP)

  • 本质:以类的方式组织代码,以对象的组织(封装)数据。

  • 抽象

  • 三大特性:

    • 封装
    • 继承
    • 多态
  • 从认识论的角度考虑是先有对象后有类。对象是具体的事物,类是对象的抽象。

  • 从代码运行角度考虑是先有类后有对象,类是对象的模板。

类与对象的关系

  • 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但并不能代表某一个具体的事物。
    • 动物、植物、手机、电脑…
    • Person类、Pet类、Cat类等,都是用来描述/定义某一具体的事物应该具备的特点和行为。
  • 对象是抽象概念的具体实例,如张三是人的一个具体实例、张三家里的狗旺财就是狗的一个具体实例。

创建与初始化对象

  • 使用new关键字创建对象

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

  • 类中的构造器也被称为构造方法,创建对象时必须调用。有以下特点:

    • 必须和类的名字相同
    • 没有返回值,也不能写void
  • 一个类即使什么都不写,也会存在一个默认的构造方法。

构造器

public class Person {
    //一个类即使什么都不写,也会存在一个默认的无参构造方法
    //显示地定义构造器
    String name;
    
    //作用:1. 使用new关键字,本质是在调用构造器
    //2. 用来初始化对象的值
    public Person(){} //无参构造
    
    //有参构造 3.一旦定义了有参构造,无参就必须显示定义
    public Person(String name){
        this.name=name;
    }
	//Alt+insert 快捷键插入构造方法
}

内存分析

//定义一个宠物类
public class Pet {
    public String name; //默认 null
    public int age; 	//默认 0
    //无参构造

    public void shout(){
        System.out.println("叫了一声");
    }
}
//应用类,创建调用对象
public class Application {
    public static void main(String[] args) {
        
        Pet dog = new Pet();

        dog.name = "旺财";
        dog.age = 3;
        dog.shout();
    }
}
  • 对象通过引用类型来操作:栈 - - ->堆

封装

  • 该露的露,该藏的藏

    • 程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外
      部干涉;低耦合:仅暴露少量的方法给外部使用。
  • 封装(数据的隐藏)

    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
  • 作用和意义

    1. 提高程序的安全性,保护数据。
    2. 隐藏代码的实现细节
    3. 统一用户的调用接口
    4. 提高系统的可维护性
    5. 便于调用者调用。

继承

  • 继承的本质是对某一批类的抽象,从而实现对世界更好地建模。

  • extends的意思是”扩展“。子类是父类的扩展,使用关键字extends来表示。

  • Java中类只有单继承,没有多继承!一个类只能继承一个父类。

  • 继承是类与类之间的一种关系,此外还有依赖、组合、聚合等。

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)子类继承父类。

  • 子类和父类之间,从意义上讲应该具有”is a“的关系。

//学生类(子类)继承 人类(父类)
public class Student extends Person{ /*Person extends Object*/
    ...
}

super& this

  • super
    super访问或调用父类中的属性和方法。
//super调用父类中的属性
public class Person {
    protected String name = "Dongyou";
}

public class student extends Person{
  String name = panbin;
  public void test(String name) {
      System.out.println(name); //东友
      System.out.println(this.name); //panbin
      System.out.println(super.name); //Dongyou
  }
}
//super调用父类中的方法
public class Person { //父类
    public void print() {
        System.out.println("Person");
    }
}

public class student extends Person{ //子类
  public void test1() {
      super.print();
  }
}
//调用父类构造器
public class Person{ }
public class Student extends Person{ 
  //编译通过,子类构造器中会隐式的调用父类的无参构造器 
  //super(); 
  public Student(){ } 
}

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

  • this
    在子类中可以使用this来表示访问或调用子类中的属性或方法.

  • super 与 this区别:
    代表的对象不同:
    this: 本身调用者这个对象。
    super:代表父类对象。
    前提:
    this: 没有继承也可以使用。
    super: 只能在继承条件才可以使用
    构造方法
    this():本类的构造
    super():父类的构造

方法的重写

  • 重写:需要有继承关系,子类重写父类的方法!
    1. 方法名必须相同
    2. 参数列表必须相同
    3. 修饰符:范围可以扩大但不能缩小;Public > Protected > Default > Private
    4. 抛出的异常:范围可以被缩小,但不能扩大;ClassNotFoundException(小)---> Exception(大)
    5. 重写,子类的方法和父类必须一致;方法体不同!
    6. 返回类型可以相同,也可以不同,如果不同的话,子类重写后的方法返回类型必须是父类方法返回类型的子类型
      例如:父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型
A类继承B类 
A和B中都一个相同的静态方法test 
B a = new A(); 
a.test();//调用到的是B类中的静态方法test 
A a = new A(); 
a.test();//调用到的是A类中的静态方法test 可以看出静态方法的调用只和变量声明的类型相关 这个和非静态方法的重写之后的效果完全不同
  • 私有方法不能被子类重写,子类继承父类后,是不能直接访问父类中的私有方法的,那么就更谈不上
    重写了。
public class Person{ private void run(){} }
//编译通过,但这不是重写,只是俩个类中分别有自己的私有方法 
public class Student extends Person{
 private void run(){} 
}
  • 静态方法不能重写
  1. 父类的静态方法不能被子类重写为非静态方法 //编译出错
  2. 父类的非静态方法不能被子类重写为静态方法;//编译出错
  3. 子类可以定义与父类的静态方法同名的静态方法(但是这个不是覆盖)
  • 为什么要重写:
    1. 父类的功能,子类不一定需要,或者不一定满足
    2. 重写父类方法的快捷键: Alt + Insert 选择 Override;

多态

  • 多态编译:类型

  • 即同一方法可以根据发送对象的不同而采用不同的行为方式

  • 一个对象的实际类型是确定的,但可以指向对象的引用可以有很多

  • 多态注意事项:

    1. 多态是方法的多态,属性没有多态
    2. 父类和子类,有联系 类型转换异常!
    3. 存在条件:继承关系,子类重写父类方法,父类引用指向子类对象! father f1 = new Son();

instanceof和类型转换

  • instanceof 引用类型比较,判断一个对象是什么类型
    // Object > String
    // Objest > Person > Student
    // Objest > Person > Teacher
    Object object = new Student();
    // X instanceof Y,X引用指向的对象是不是Y的子类
    System.out.println(object instanceof Student); //true
    System.out.println(object instanceof Person); //true
    System.out.println(object instanceof Teacher); //false
    System.out.println(object instanceof Object); //true
    System.out.println(object instanceof String); //false
	
    //类型之间的转化:父-子(高-低),低可以转换为高
    Person obj = new Syudent(); //只能用Person方法(重写了用子类重写过的方法)
    (Syudent)obj.go(); //强转之后可以用Student方法(Student->go())
  • 类型转换

    1. 父类引用指向子类的对象
    2. 把子类转换为父类,向上转型,会丢失自己原来的一些方法
    3. 把父类转换为子类,向下转型,强制转换,才调用子类方法
    4. 方便方法的调用(转型),减少重复的代码,简洁。

修饰符

static(静态)

1、static变量

  • 在类中,使用static修饰的成员变量,就是静态变量,也称为类变量,反之为非静态变量。
private static int age;  //静态变量
private double score; //非静态变量
  • 静态变量与非静态变量的区别
    • 静态变量属于类的,"可以"使用类名来访问,非静态变量是属于对象的,"必须"使用对象来访问.
public class Student{
   private static int age;
   private double score;
   public static void main(String[] args) {
       Student s = new Student(); 
       System.out.println(Student.age); //静态访问,推荐使用类名访问静态成员 
       System.out.println(s.age); 
       System.out.println(s.score); //非静态访问,要先new对象再访问
    } 
}
* 静态变量对于类而言在内存中只有一个,能被类的所有实例所共享。
* 实例变量对于类的每个实例都有一份,它们之间互不影响.
* 在加载类的过程中为静态变量分配内存,实例变量在创建对象时分配内存,所以静态变量可以使用类名来
直接访问,而不需要使用对象来访问.

2、static方法

  • 在类中,使用static修饰的成员方法,就是静态方法,反之为非静态方法。
  • 静态方法和非静态方法的区别
    • 方法的调用方式不同
public class Student{   
      public static void run(){};
      public void go(){};
    }

    poublic void test(){
      Student.run(); //类.方法()
      Student s = new Student();
      s.go(); //对象.方法();
    }
  • 父类的静态方法可以被子类继承,但是不能被子类重写
  • 父类的非静态方法不能被子类重写为静态方法 ;
  • this和super在类中属于非静态的变量.(静态方法中不能使用)

3、代码块和静态代码块

【类中可以编写代码块和静态代码块】

public class Person {
  {
     //代码块(匿名代码块) 
  }
  static{
    //静态代码块 
  } 
}
  • 匿名代码块和静态代码块的执行

    • 因为没有名字,程序不能主动调用这些代码块。
    • 匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。同时匿名代码块在每次创建对象的时候都会自动行.
    • 静态代码块是在类加载完成之后就自动执行,并且只执行一次.
  • 匿名代码块和静态代码块的作用

    • 匿名代码块的作用是给对象的成员变量初始化赋值
    • 静态代码块的作用是给类中的静态成员变量初始化赋值。

4、创建和初始化对象的过程

Student s = new Student();

  1. 类加载,同时初始化类中静态的属性
  2. 执行静态代码块
  3. 分配内存空间,同时初始化非静态的属性(赋默认值,0/false/null)
  4. 调用Student的父类构造器
  5. 对Student中的属性进行显示赋值(如果有的话) 6. 执行匿名代码块
  6. 执行构造器
  7. 返回内存地址

finnal

  • 用final修饰的类不能被继承,没有子类。
  • 用final修饰的方法可以被继承,但是不能被子类的重写。
  • 用final修饰的变量表示常量,只能被赋一次值.其实使用final修饰的变量也就成了常量了,因为值不会再变
    了。

abstract

abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那
么该类就是抽象类。

1、抽象类和抽象方法的关系

抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

2、语法

/*
声明类的同时,加上abstract修饰符就是抽象类
声明方法的时候,加上abstract修饰符,并且去掉方法的大括号,同时结尾加上分号,该方法就是抽象方法。
*/

//抽象类
public abstract class Action{
  //抽象方法
   public abstract void doSomething(); 
}
//子类实现父类中定义的抽象方法
public void doSomething(){...}

3、抽象类 vs 抽象方法

  1. 抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类。
  2. 抽象类不能使用new来创建对象,它是用来让子类继承的。
  3. 抽象方法只有方法的声明,没有实现,让其子类实现。
  4. 子类继承抽象类,必须实现抽象类的所有方法,否则该子类也要声明为抽象类。

接口(interface)

1、接口的本质

  • 普通类:只有具体实现
  • 抽象类:具体实现和规范(抽象方法)都有!
  • 接口:只有规范!

2、接口与抽象类的区别

  • 声明类的关键字是class,声明接口的关键字是interface。
  • 继承的关键字是extends
  • 实现的关键字是implements
  • 一个类可以实现多个接口
  • 一个子类只能继承一个抽象类

3、接口中的方法都是抽象方法

//interface接口,接口都要有继承类
//实现类(implements 可以继承多个接口)
//多继承,利用接口实现多继承
public interface UserService {
    //接口中所有定义的就去其实都是抽象的 默认修饰  public abstract
     void add();
     void delete();
     void update();
     void query();
}

4、接口中的变量都是静态常量(public static final

接口中可以不写任何属性,但如果写属性了,该属性必须是public static final修饰的静态常量。

public interface UserService {
    //常量- 默认修饰 public static final
     int AGE = 99;
}

5、一个类可以实现多个接口

public class Student implements A,B,C,D{
 //Student需要实现接口A B C D中所有的抽象方法
 //否则Student类就要声明为抽象类,因为有抽象方法没实现 
}

6、一个接口可以继承多个父接口

public interface A {
    public void testA();
}

public interface B {
    public void testB();
}

public interface C extends A,B {
    public void testC();
}

//Student相当于实现了A B C三个接口,需要实现所有的抽象方法
// Student的对象也就同时属于A类型 B类型 C类型
public class Student  implements C{
    public viod testA(){}
    public viod testB(){}
    public viod testC(){}
}

7、接口的作用

接口的最主要的作用是达到统一访问,就是在创建对象的时候用接口创建

【接口名】 【对象名】= new 【实现接口的类】

这样你想用哪个类的对象就可以new哪个对象了,不需要改原来的代码。

//定义动物接口
public interface Animal {
    void eat();
}
//熊猫类实现了动物接口
public class Panda implements Animal{
    @Override
    public void eat() {
        System.out.println("熊猫吃竹子!");
    }
}
//羊类实现了动物接口
public class Sheep implements Animal{

    @Override
    public void eat() {
        System.out.println("羊儿吃草!");
    }
}
main:
Animal panda = new Panda(); 
Animal sheep = new Sheep();
panda.eat();  //熊猫吃竹子!
sheep.eat();  //羊儿吃草!

内部类

内部类概述

内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内
部类,而A类相对B类来说就是外部类了。

内部类不是在一个java源文件中编写俩个平行的俩个类,而是在一个类的内部再定义另外一个类。 我们
可以把外边的类称为外部类,在其内部编写的类称为内部类。

内部类分为四种:

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类

成员内部类(实例内部类、非静态内部类)

public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    //内部类
    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }
    }
}

public class Test{
   public static void main(String[] args) {
       //实例化成员内部类分两步
       //1、实例化外部类
       Outer outObject = new Outer();
       //2、通过外部类调用内部类
       Outer.Inner inObject = outObject.new Inner();
       //测试,调用内部类中的方法
       inObject.in();//打印:这是内部类方法 
    } 
}

静态内部类

  • 使用static修饰的内部类就叫静态内部类。

  • static一般只修饰变量和方法,平常不可以修饰类,但是内部类却可以被static修饰。

  • 注意

    • 我们上面说的内部类能够调用外部类的方法和属性,在静态内部类中就行了,因为静态内部类没有了指向外部类对象的引用。除非外部类中的方法或者属性也是静态的。这就回归到了static关键字的用 法。
      1. 2、静态内部类能够直接被外部类给实例化,不需要使用外部类对象
      1. Outer.Inner inner = new Outer.Inner();
      1. 3、静态内部类中可以声明静态方法和静态变量,但是非静态内部类中就不可以声明静态方法和静态变

局部内部类

public class Outer {
   private int id; //在method01方法中有一个Inner内部类,这个内部类就称为局部内部类
   public void method01(){
      class Inner{
         public void in(){
           System.out.println("这是局部内部类");
         }
     }
  } 
}

  • 局部内部类是在一个方法内部声明的一个类
  • 局部内部类中可以访问外部类的成员变量及方法
  • 局部内部类中如果要访问该内部类所在方法中的局部变量,那么这个局部变量就必须是final修饰的
  • 注意:
    • 在局部内部类中,如果要访问局部变量,那么该局部变量要用final修饰
    • 局部内部类不能通过外部类对象直接实例化,而是在方法中实例化出自己来,然后通过内部类对象
      调用自己类中的方法。看下面例子就知道如何用了。

匿名内部类

  • 如果一个对象只要使用一次,那么我们只需要new Object().method()。不需要给这个实例保存到该类型变量中去。这就是匿名对象。

  • 匿名内部类需要依托于其他类或者接口来创建

    • 如果依托的是类,那么创建出来的匿名内部类就默认是这个类的子类
    • 如果依托的是接口,那么创建出来的匿名内部类就默认是这个接口的实现类。
  • 匿名内部类的声明必须是在使用new关键字的时候,

    • 匿名内部类的声明及创建对象必须一气呵成,并且之后能反复使用,因为没有名字。
//A是一个类(普通类、抽象类都可以),依托于A类创建一个匿名内部类对象
A a = new A(){
   //实现A中的抽象方法
   //或者重写A中的普通方法 
};

//1. 匿名内部类除了依托的类或接口之外,不能指定继承或者实现其他类或接口,同时也不能被其他类所
//继承,因为没有名字。
//2. 匿名内部中,我们不能写出其构造器,因为没有名字。
//3. 匿名内部中,除了重写上面的方法外,一般不会再写其他独有的方法,因为从外部不能直接调用到。(间
接是调用到的)
  • 使用匿名内部类
public class Test {
    public static void main(String[] args) {
       //如果我们需要使用接口中的方法,我们只需要走一步,就是使用匿名内部类,直接将其 类的对象创建出来。
       new Test1(){
           public void method(){
             System.out.println("实现了Test接口的方法");
           }
       }.method();
    }
}

interface Test1{
   public void method(); 
}
posted @   panbin_2006  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示