Java的面向对象

面向对象

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

  • 从认识论的角度考虑是先有对象后有类。对象是对具体的事物,而类是抽象的,是对对象的抽象。

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

方法回顾

  • 静态方法:加static,可以直接类名点方法名调用Student.say();。

  • 非静态方法:需要实例化类名:Student student = new Student();然后再student.say();。

  • 静态方法和类一起加载,非静态方法不实例化不加载,所以静态方法调用不了没有实例化的非静态方法。

  • 一个程序里面只能有一个public class但是可以有多个class,类里面的字段叫作属性

package com.oop.demo;
//引用传递:对象,本质还是值的传递;
public class Demo{
   public static void main(Sting[] args){
       Person person = new Person();
       System.out.println(person.name);
       Demo.change(person);
       System.out.println(person.name);
  }
   public static void change(Person person){
       //person是一个对象,指向的→Person person = new Person();这是一个具体的人,可以改变属性。
       person.name = "秦疆"
  }
}
//定义了一个Person类,有一个属性:name
class Person{
   String name; //null
}

创建与初始化对象

  • 使用new关键字创建对象后自动生成无参构造,但写了有参无参就失效。

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

  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下两个特点:

    • 必须和类的名字相同

    • 必须没有返回类型,也不能写void。

  • 一个类里面只有属性(字段)和方法,this指代它存在的类名,可以用它去点用属性,可参考Python的self

package com.oop.demo;
//类是模版,等待被调用的基本不会具体的值。
public class Student{
   //属性:字段
   String name; //null
   int age; //0
   //方法
   public void study(){
       System.out.println(this.name + "在学习");
  }
}
//一个项目只存在一个main方法
public class Application{
   public static void main(String[] args){
       //类,抽象,用new实例化出一个对象
       //类实例化后会返回一个自己的对象
       //student对象就是一个Student类的具体实例
       Student xiaoming = new Student();
       Student xiaohong = new Student();
       xiaoming.name = "小明";
       xiaoming.age = 3;
       System.out.println(xiaoming.name);
       System.out.println(xiaoming.age);
       System.out.println(xiaohong.name);
       System.out.println(xiaohong.age);
  }
}
  • 查看IDEA编辑的Java文件编译后生成的对应的class文件的方法:点头部第三排倒数第二个图标(project structure)→ 在models下面 → 点击最右边 Add Content Root → 把文件夹里的out目录添加进去 → 左侧基础语法栏就有了out目录

  • 一个类即使什么都不写也会存在一个隐式构造器产生一个无参构造方法


  • 一旦定义了同名有参构造,就必须显示定义无参构造。

  • 构造器的实际作用

    • 使用new关键字本质是在调用构造器

    • 用来初始化值

package com.oop.demo;
public class Person{
   //一个类即使什么都不写它也会存在一个方法
   //显示的定义构造器
   String name;
   //实例化初始值
   public Person(){
       
  }
   //一旦定义了同名有参构造,就必须显示定义无参构造。
   public Person(String name){
       this.name = name;
  }
}
public class Application{
   public static void main(String[] args){
       Person person = new Person("Awa");
       System.out.println(person.name);
  }
}
//构造器:
1、和类名同名
   2、没有返回值
//作用:
   1、new 本质是在调用构造方法
   2、初始化对象的值
//注意点:
   定义有参构造之后,如果想使用无参构造就必须再重新显示的定义一个无参构造了。
  • ALT + INSERT → 一键生成有参/无参构造函数

创建对象的内存分析

  • string预定义类型是不可变的,所以被当做常量处理。

  • 方法区存放类信息,具体的对象存放在堆,栈是程序运行空间,在main函数上方的方法只是一个引用或者变量名的空壳,作为堆中方法的对象入口,运行时对象从堆中取,类的方法和静态资源(如常量)从方法区中取,方法区也属于堆。

  • 属性:也叫字段、field、成员变量

  • 默认初始化:

    • 数字:0 和 0.0

    • char : u0000

    • Boolean : false

    • 引用以及其他:null

  • 对象的创建和使用

    • 必须使用new关键字创造对象、构造器,Person kuang = new Person()。

    • 对象的属性使用 kuang.name

    • 对象的方法使用 kuang.sleep()

封装

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

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

  • 封装是针对属性的,用在方法上的很少。

  • 重点:属性私有(private),类中用公共方法get和set修改私有属性。

package com.oop;
import com.oop.demo.Student;
/*
1.提高程序的安全性,保护数据。
2.隐藏代码的实现细节
3.统一接口(用户只能使用get和set)
4.系统可维护性增强了
*/
public class Application{
   public static void main(String[] args){
       Student s_one = new Student();
       //String name = s_one.getName();
       s_one.setName("秦疆");
       System.out.println(s_one.getName());
  }
}

package com.oop.demo;
public class Student{
   private String name;
   private int id;
   private char sex;
   //提供一些可以操作这个属性的方法
   //提供一些public的get、set方法
   //get 获得这个数据
   public String getName(){
       return this.name;
  }
   //set 给这个数据设置值
   public void setName(String name){
       this.name = name;
  }
   //ALT + INSERT可以针对属性只能生成get和set方法
}

继承

  • 继承的本质是对某一批类的抽象(相当于把类再次进行抽象),从而实现对现实世界更好的建模。

  • extends的意思是扩展,子类是父类的扩展。

  • Java中类只有单继承没有多继承

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

    • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。

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

    • public、protected、default、private

  • Java中所有类都默认直接或者间接的继承object类


package com.oop.demo;
//Person 父类
public class Person{
   public Person(){
       System.out.println("Person无参构造被执行了");
  }
   protected String name = "kuangshen";
}
package com.oop.demo;
//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person{
    public Student(){
        //此处隐藏了默认调用父类无参构造的代码 super();
        //调用父类的构造器,必须写在子类构造器的第一行。
       System.out.println("Student无参构造被执行了");
  }
   private String name = "qingjiang";
   public void test(String name){
       System.out.println(name);
       System.out.println(this.name);
       System.out.println(super.name);
  }
}
package com.oop;

import com.oop.demo.Person;
import com.oop.demo.Student;

public class Application{
   public static void main(String[] args){
       Student student = new Student();
       student.test(name:"秦疆")
  }
}
  • this 指向类自身,super指向父类。

  • 私有的属性或者方法无法被继承

super的注意点

  • super调用父类的构造方法,必须在构造方法的第一个位置。

  • super只能出现在子类的方法或者构造方法

  • super和this不能同时调用构造方法

super vs this

  • 代表的对象不同:

    • this:本身调用的这个对象

    • super:代表父类对象的应用

  • 使用前提:

    • this:没有继承也可以使用

    • super:只能在继承条件下才可以使用

  • 构造方法:

    • this();调用本类的构造

    • super();调用父类的构造

方法的重写

package com.oop.demo;
//重写都是方法的重写,和属性无关。
public class B{
   public static void test(){
       System.out.println("B=>test()");
  }
}
package com.oop.demo;

public class A extends B{
   public static void test(){
       System.out.println("A=>test()");
  }
}
package com.oop;

public class Application{
   //静态方法和非静态方法区别很大
   public static void main(String[] args){
       //方法的调用只和左边定义的数据类型有关
       A a = new A();
       a.test();
       
       //父类的引用指向了子类
       B b = new A(); //没有static时子类就重写了父类的方法
       b.test();
  }
}
  • 静态方法是类的方法,非静态是对象的方法;有static时 b 调用了 B 类的方法,因为 b 是用 B 类定义的;没有static时,b 调用的是对象的方法,而此时 b 是用 A 类new出来的。

  • 重写只跟非静态方法有关,因为静态类在类加载的时候就出来了改变不了。

  • 重载是本类里函数名相同其他不同的写法,重写是子父类才有的。

重写的要点

  1. 方法名必须相同

  2. 参数列表必须相同

  3. 修饰符:范围可以扩大,但不能缩小。public > protected > default > private

  4. 抛出的异常:范围可以缩小但不能扩大

  5. 子类方法和父类必须一致但方法体不同

  • 为什么需要重写?

    • 父类的功能子类不一定需要,或者不一定满足。

    • 快捷键:ALT + INSET 选中 override。

多态

  • 实现动态编译,类型在执行过程中才被确定,增强可扩展性。

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

  • 一个对象的实际类型是确定的,但可以指向对象的引用(父类或者有关系的类)的类型有很多种。

  • 多态的存在条件

    • 有继承关系

    • 子类重写父类方法

    • 父类引用指向子类对象 Father f1 = new Son();

  • 注意:多态是 方法 的多态,属性并没有多态性。

package com.oop;
public class Application{
   public static void main(String[] args){
       //一个对象的实际类型是确定的,但它可以指向的引用类型不确定,父类的引用指向子类。
       Student s1 = new Student();
       //Student 能调用的方法都是自己的或者继承自父类的
       Person s2 = new Student();
       //Person 父类型可以指向子类,但是不能调用子类独有的方法
       Student s3 = new Student();
       
       s2.run(); //子类重写了父类的方法那就执行子类的方法
       s1.run();
       //对象能执行哪些方法主要看对象左边的类型,和右边关系不大。
  }
}
package com.oop.demo;
public class Person{
   public void run(){
       System.out.println("run");
  }
}
package com.oop.demo;
public class Student extends Person{
   @Override
   public void run(){
       System.out.println("son");
  }
   public void eat(){
       System.out.println("apple");
  }
}
  • 多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)

  • 不能实现重写的情形:

    • static 方法,属于类,但是不属于实例。

    • final 修饰的方法是常量,无法重写。

    • private 是私有的方法,也不能重写。

instance of 关键词

import java.util.Scanner;
public class Application{
   public static void main(String[] args){
       //Object > String
       //Object > Person > Teacher
       //Object > Person > Student
       Object object = new Student();
       
       System.out.println(object instanceof Student);
       //true
       System.out.println(object instanceof Person);
       //true
       System.out.println(object instanceof Object);
       //true
       System.out.println(object instanceof Teacher);
       //false
       System.out.println(object instanceof String);
       //false
  }
}
  • 通过 instance of 判断 两个相互间有联系 的类型是否相似,然后再进行之后的强制转换。

  • instance of严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:

boolean result = obj instanceof Class
  • 其中obj为一个对象,Class表示一个类或者一个接口,当obj为Class的对象或者是其直接或间接子类,或者是其接口的实现类,结果result都返回true否则返回false。

  • 注意: 编译器会检查obj是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时的状态决定。

package com.oop;

import com.oop.demo.Person;
import com.oop.demo.Student;
import com.oop.demo.Teacher;
import java.util.Scanner;

public class Application{
   public static void main(String[] args){
       //高容量               低容量
       Person obj = new Student();
       //强制转换
       Student student = (Student)obj;
       student.go();
       //((Student)obj).go();
  }
}
  • 子类转换为父类 时可能会丢失自己本来的一些方法!

Student student = new Student();
student.go();
Person person = student;
  • 子类一定是父类的一个实例,而父类不一定是子类的一个实例,所以在父类向子类转型时,要强制类型转换。

  • 子转父: 向上转型, 直接转, 丢失子类中原本可直接调用的特有方法 父转子: 向下转型, 强制转, 丢失父类被子类所重写掉的方法

static 总结

package com.oop.demo;

public class Student{
   private static int age;
   private double score;
   
   public static void main(String[] args){
       Student s1 = new Student();
       
       System.out.println(Student.age);
       //静态变量的话推荐直接使用类名去访问,体现它是静态变量。
       //对于这个类而言在内存中静态变量只有一个,能被类中所有实例共享。
       System.out.println(s1.age);
       System.out.println(s1.score);
  }
}
package com.oop.demo;
public class Person{
  {
       //赋初始值用,与对象同时产生,还在构造方法之前。
       System.out.println("匿名代码块"); //再输出
  }
   static {
       System.out.println("静态代码块"); //先输出,但不管如何重复生成新的对象调用时它都只会在第一次执行一回就不再执行了。
  }
   public Person(){
       System.out.println("构造方法"); //后输出
  }
   public static void main(String[] args){
       Person person = new Person();
  }
}
  • 通过 final 修饰的类不能被继承,它没有子类了。

package com.oop.demo;

//导入静态包
import static java.lang.Math.random;
import static java.lang.Math.PI;

public class Test{
   public static void main(String[] args){
       System.out.println(random());
       System.out.println(PI);
  }
}

抽象类

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

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

  • 抽象类不能使用 new 关键字来创建对象,它是用来让子类继承的,只能靠子类去实现它,它本身是一种约束。

  • 抽象方法只有方法的声明,没有方法的实现,它是用来让子类实现的。

  • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

package com.oop.demo;
//abstract 抽象类 必须要有extends,继承才能有用,只可惜是单继承。
public abstract class Action{
   //写个约束,等别人帮我们实现。
   //abstract 抽象方法,只有方法的名字没有方法的实现。
   public abstract void doSomething();
}
package com.oop.demo;
//抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非它的子类也是抽象,等待子子类去实现。
public class A extends Action{
   @override
   public void doSomething(){
       //待写入
  }
}
  • 接口可以多继承

  • 抽象方法必须在抽象类中,但是抽象类中可以写普通的方法。

接口

  • 普通类:只有具体实现

  • 抽象类:具体实现和规范(抽象方法)都有

  • 接口:只有规范,自身无法写入方法,是专业的约束和实现分离,架构师写好接口,码农填入方法,面向接口编程。

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能...”的思想。如果你是汽车,则必须能跑。

  • 接口的本质是契约,就像我们的法律一样,制定好后大家都要遵守。

  • 面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为何我们讨论设计模式都只针对具备了抽象能力的语言?(c++、Java、c#等)就是因为设计模式所研究的,实际上就是如何合理的去抽象。

  • 声明类的关键字是class,声明接口的关键字是 interface 

package com.oop.demo;

//interface 定义的关键字,接口都需要有实现类。
public interface UserService{
   //接口中的所有定义其实都是抽象的 public abstract
   public abstract void add();
   //等价于void add(String name); 反正public abstract是默认值,自动会变灰的。
}
package com.oop.demo;
//抽象类:只有 通过extends 单继承
//类 可以实现多继承 通过implements 接口
//(执行)实现接口的类,就需要重写接口中的方法。
public class UserServiceImpl implements UserService{
   @Override
   public void add(String name){
       System.out.println("hi");
  }
}
package com.oop.demo;

public interface TimeService{
   void timer();
}
package com.oop.demo;
//利用接口实现多继承
public class UserServiceImpl implements UserService, TimeService{
   @override
   public void add(String name){
       System.out.println("hi");
  }
   public void timer(){
       System.out.println("hello");
  }
}
  • 接口跟类有区别,类里面有方法的实现。接口只有通过 interface 完成的方法的定义,再用 implements 去进行方法的实现。

  • 抽象类用 abstract 去定义 class 再用 extends 单继承,接口用 implements 实现 interface 定义的方法实现伪多继承。

  • 接口里面定义的属性(字段)都会变成常量 比如 int AGE = 99;会变成 public static final int AGE = 99;所以一般大家基本不在接口里定义属性,都是定义一些方法,然后通过接口的约束去实现它。

  • 接口不是类,不能被实例化,它里面没有构造方法。

内部类

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

package com.oop.demo;
public class Outer{
   private int id = 10;
   public void out(){
       System.out.println("this is outer function");
  }
   
   public class Inner{
       public void in(){
           System.out.println("this is inner function");
      }
       //内部类可以访问外部类的一些方法,获得外部类的私有属性。
       public void getId(){
           System.out.println(id);
      }
  }
}
package com.oop;

import com.oop.demo.Outer;
public class Application{
   public static void main(String[] args){
       Outer outer = new Outer();
       //通过这个外部类来实例化内部类
       Outer.Inner inner = outer.new Inner();
       inner.getID();
  }
}
  • 静态内部类没法访问外部类的非静态属性,理由也是因为静态类和类一起初始化生成的原因,它先于非静态类生成。

  • Java中可以有多个 class 类,但只能有一个被 public 修饰的类。

package com.oop.demo;

public class Outer{
   //局部内部类
   public void method(){
       class Inner{
           public void in(){
               
          }
      }
  }
}
  • 匿名对象的使用

package com.oop.demo;

public class Test{
   public static void main(String[] args){
       //没有名字初始化类,不用将实例保存到变量中。
       //匿名内部类,这就是匿名对象的使用。
       new Apple().eat();
       new UserService(){
           @override
           public void hello(){
               
          }
      }
  }
}

class Apple{
   public void eat(){
       System.out.println("perple");
  }
}

interface UserService{
   void hello();
}

 只有一个方法的接口叫做函数式接口,可以用lambda表达式简化。

Java的静态内部类介绍:https://www.cnblogs.com/heavenplus/p/9451181.html

posted on 2021-11-04 17:55  愿将过往均储藏  阅读(29)  评论(0)    收藏  举报

导航