Java OOP

Java OOP

一、面向对象编程

​ 面向对象是设计方法,它的基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。

从现实世界中提取出事物的属性特征,把他们抽象成系统中的类。

访问修饰符:

访问修饰符范围
private私有,仅允许类内部访问
default默认,允许类内部访问以及同包下访问
protected受保护,允许类内部访问、同包下访问以及子类中访问
public公有,所有地方都能访问,包括不同包

类修饰符只有public公有和default默认

public class A {

    private String a = "a";

    String b = "b";

    protected String c = "c";

    public String d = "d";

    private void f1(){
        System.out.println("A private f1()...");
    }

    void f2(){
        System.out.println("A default f2()...");
    }

    protected void f3(){
        System.out.println("A protected f3()...");
    }

    public void f4(){
        System.out.println("A public f4()...");
    }
}
public class B extends A{

}
public class C extends A {

    public void f1(){
        //System.out.println(super.b); //属性b是默认的,子类无法访问
        System.out.println(super.c); //属性c是受保护的,可以在子类中访问
    }

    public void f2(){
        //super.f2(); //f2()方法是默认的,子类无法访问
        super.f3(); //f3()方法是受保护的,可以在子类中访问
    }
}
package com.hubu.oop.modifier;

import com.hubu.oop.C;

public class ModifierTest {
    public static void main(String[] args) {

        A a = new A();
        B b = new B();
        C c = new C();

        //System.out.println(a.a); //属性a是私有的,只能在类内部访问
        System.out.println(a.b);
        System.out.println(a.c);
        System.out.println(a.d);
        System.out.println("-----------------------");

        //System.out.println(b.a); //属性a是私有的,只能在类内部访问
        System.out.println(b.b);
        System.out.println(b.c);
        System.out.println(b.d);
        System.out.println("-----------------------");

        c.print(); 
        c.function();
    }
}

/*
b
c
d
-----------------------
b
c
d
-----------------------
c
A protected f3()...
*/
package com.hubu.oop;

import com.hubu.oop.modifier.A;
import com.hubu.oop.modifier.B;

public class ModifierTest {
    public static void main(String[] args) {

        A a = new A();
        B b = new B();
        C c = new C();

        //System.out.println(a.a); //属性a是私有的,只能在类内部访问
        //System.out.println(a.b); //属性b是默认的,不能在不同包下访问
        //System.out.println(a.c); //属性c是受保护的的,不能在不同包下访问
        System.out.println(a.d);
        System.out.println("-----------------------");

        //System.out.println(b.a); //属性a是私有的,只能在类内部访问
        //System.out.println(b.b); //属性b是默认的,不能在不同包下访问
        //System.out.println(b.c); //属性c是受保护的的,不能在不同包下访问
        System.out.println(b.d);
        System.out.println("-----------------------");

        c.print();
        c.function();
    }
}

/*
d
-----------------------
d
-----------------------
c
A protected f3()...
*/

在这里插入图片描述

二、封装

​ 封装指的是将对象的实现细节隐藏起来,然后通过一些公用方法getter/setter来暴露该对象的功能。

对象的状态信息都被隐藏在对象内部,外界无法直接操作和修改。对一个类或对象实现良好的封装,可以实

现以下目的:

  • 隐藏类的实现细节;

  • 让使用者只能通过getter/setter方法来访问数据,限制对成员变量的不合理访问;

  • 可进行数据检查,从而有利于保证对象信息的完整性;

  • 便于修改,提高代码的可维护性

public class Person {
    private String name;

    private String gender;

    private int age;

    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}
Person person = new Person("张三", "男", 20);
System.out.println(person.getName()); //张三
System.out.println(person.getGender()); //男
System.out.println(person.getAge()); //20

三、继承

​ 继承是面向对象实现软件复用的重要手段,当子类继承
父类 后,子类作为一种特殊的父类,将直接获得父类被protectedpublic修饰的属性和方法,对于default修饰的属性和方法只有同包下的子类能够继承。

Java是单继承的,不支持多继承,一个类只能有一个直接父类,因为当多个父类中包含相同的方法时,子类在调用该方法或重写该方法时会产生混淆。

public class Base {

    private int n1 = 1;

    int n2 = 2;

    protected int n3 = 3;

    public int n4 = 4;

    public Base() {
        System.out.println("Base()被调用...");
    }

    public Base(int n1, int n2, int n3, int n4) {
        System.out.println("Base(int n1, int n2, int n3, int n4)被调用...");
        this.n1 = n1;
        this.n2 = n2;
        this.n3 = n3;
        this.n4 = n4;
    }

    public int getN1() {
        return n1;
    }

    public void setN1(int n1) {
        this.n1 = n1;
    }

    public int getN2() {
        return n2;
    }

    public void setN2(int n2) {
        this.n2 = n2;
    }
}
public class Sub extends Base {

    public Sub() {
        //super(); //子类构造时默认调用父类的无参构造函数
        System.out.println("Sub()被调用...");
    }

    public Sub(int n1, int n2, int n3, int n4) {
        super(n1, n2, n3, n4); //可以指定调用父类的构造器
        //this(); //在构造函数中this()和super()不能同时使用,因为它们都需要在构造函数第一行
        System.out.println("Sub(int n1, int n2, int n3, int n4)被调用...");
    }

    public void print() {
        //System.out.println(this.n1); //子类不能直接访问父类中的private成员变量
        System.out.println(this.getN1()); //但是可以调用父类中的方法间接访问私有成员变量
        System.out.println(this.n2);
        System.out.println(this.n3);
        System.out.println(this.n4);
    }
}
public class Final extends Sub{
    public Final() {
        System.out.println("Final()被调用..."); //调用父类的构造函数时会层层向上直到Object
    }
}
Sub sub = new Sub();
System.out.println("----------------------");
Sub sub1 = new Sub(100, 200, 300, 400);
System.out.println("----------------------");
sub.print();
System.out.println("----------------------");
sub1.print();

System.out.println("----------------------");
Final f = new Final();

/*
Base()被调用...
Sub()被调用...
----------------------
Base(int n1, int n2, int n3, int n4)被调用...
Sub(int n1, int n2, int n3, int n4)被调用...
----------------------
1
2
3
4
----------------------
100
200
300
400
----------------------
Base()被调用...
Sub()被调用...
Final()被调用...
*/

super

调用属性和方法时,直接从父类向上查找

public class A {

    protected String a = "a";
}
public class B extends A{

    protected String b = "b";
}
public class C extends B{

    protected String a = "1";

    protected String b = "2";

    protected String c = "c";

    public void getA(){
        System.out.println(a);
    }

    public void getB(){
        System.out.println(b); //相当于this.b,从本类开始查找属性b,如果本类中存在则使用,如果没有才向父类层层向上查找
    }

    public void getC(){
        System.out.println(c);
    }

    public void getSuperA(){
        System.out.println(super.a); //直接向父类层层向上查找属性a
    }

    public void getSuperB(){
        System.out.println(super.b);
    }
}
C c = new C();
c.getA();
c.getB();
c.getC();
c.getSuperA();
c.getSuperB();

/*
1
2
c
a
b
*/

重载和重写:

  1. 重载发生在同一个类中,多个方法之间方法名相同、参数列表不同。重载与方法的返回值以及访问修饰符无关,即重载的方法不能根据返回类型进行区分。
  2. 重写发生在父类子类中,子类的方法名、参数列表必须与父类方法 相同。另外,返回值要小于等于父类方法,抛出的异常要小于等于父类方法,访问修饰符则要大于等于父类方法。还有,若父类方法的访问修饰符为private,则子类不能对其重写。
public class A {
    
    //两个f1()方法构成重载
    protected A f1(){
        return null;
    }

    //子类无法重写private方法
    private A f1(boolean flag)
    {
        if(flag)
        {
            return new A();
        }
        return null;
    }
}
public class B extends A{

    //重写父类方法, 返回值可以是原来的子类, 访问修饰符可以增大范围不能缩小
    @Override
    public B f1() {
        return null;
    }
}
public class C extends B{

    //重写父类方法, 返回值可以是原来的子类, 访问修饰符可以增大范围不能缩小
    @Override
    public C f1() {
        return null;
    }
}

四、多态

​ 多态指的是父类的引用指向子类的对象,但运行时依然表现出类的行为特征,这意味着同一个类

型的对象在执行同一个方法时,可能表现出多种行为特征。

编译类型运行类型

定义时等号的左边为编译类型,等号的右边为运行类型,编译类型在定义对象时就确定了,但运行类型是可

以改变的

public class Animal {

    public void shut(){
        System.out.println("动物在叫唤...");
    }
}
public class Cat extends Animal{

    @Override
    public void shut() {
        System.out.println("小猫喵喵叫...");
    }
}
public class Dog extends Animal{

    @Override
    public void shut() {
        System.out.println("小狗汪汪叫...");
    }
}
//编译类型为Animal,运行类型为Cat
Animal animal = new Cat();
animal.shut();
//运行类变为Dog
animal = new Dog();
animal.shut();

/*
小猫喵喵叫...
小狗汪汪叫...
*/

向上转型向下转型

向上转型指父类的引用指向了子类的对象,编译器自动完成

向下转型指将父类的引用强转为子类的引用,之后可以调用子类的成员

public class Animal {

    public void shut(){
        System.out.println("动物在叫唤...");
    }
}
public class Cat extends Animal{

    @Override
    public void shut() {
        System.out.println("小猫喵喵叫...");
    }

    public void eat(){
        System.out.println("小猫在吃鱼...");
    }
}
public class Dog extends Animal{

    @Override
    public void shut() {
        System.out.println("小狗汪汪叫...");
    }

    public void eat(){
        System.out.println("小狗在吃骨头...");
    }
}
Animal animal = new Cat();
animal.shut();
//animal.eat(); //animal编译类型为Animal,无法访问子类特有的成员变量和成员方法
Cat cat = (Cat) animal; //向下转型后可以访问
cat.eat();
System.out.println("-------------------------");
animal = new Dog(); 
animal.shut();
Dog dog = (Dog) animal; //向下转型后可以访问
dog.eat();

/*
小猫喵喵叫...
小猫在吃鱼...
-------------------------
小狗汪汪叫...
小狗在吃骨头...
*/

当父类和子类中存在同名的属性时,由调用者的编译类型决定

class A {
    int n = 1;
}
class B extends A {
    int n = 2;
}
A a = new B(); //a的编译类型为A
B b = new B(); //b的编译类型为B
System.out.println(a.n); //1
System.out.println(b.n); //2

instanceof

判断对象的运行类型是否为XX类型或XX类型的子类型

class A {
    
}

class B extends A {

}
A a = new B(); //a的运行类型为B
A a1 = new A(); //a1的运行类型为A
System.out.println(a instanceof A); //true
System.out.println(a instanceof B); //true
System.out.println(a1 instanceof A); //true
System.out.println(a1 instanceof B); //false

动态绑定机制

  1. 当调用对象方法时,方法会和该对象的运行类型进行绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明哪里使用
public class A {

    int i = 10;

    public int getI() {
        return i;
    }

    public int f1(){
        return i + 10;
    }

    public int f2(){
        return getI() + 10;
    }
}
public class B extends A {

    int i = 20;

    @Override
    public int getI() {
        return i;
    }

    @Override
    public int f1() {
        return i + 10;
    }
}
//b的编译类型为A,运行类型为B
A b = new B();
//调用B类的f1()方法 ---> i + 10 = 20 + 10 = 30
System.out.println(b.f1()); //30
//B类中没有重写f2()方法,调用从A类继承的f2()方法 ---> getI()[动态绑定机制调用B类的getI()方法] + 10 = 20 + 10 = 30
System.out.println(b.f2()); //30

五、静态static

静态变量,又称为类变量,在类加载时创建,为该类的所有对象共享

静态方法中只能使用静态变量和静态方法,不能使用thissuper关键字

静态变量和静态方法可以直接通过类名调用而不需要创建对象实例,也能使用对象调用

public class A {

    int n = 1;

    static String a = "a"; //静态变量

    public static void setA(String a) {
        //this.a = a; //静态方法中不能使用this关键字
        A.a = a;
    }

    public static void f1(){
        //n = 2; //静态方法中只能使用静态变量和静态方法
        setA("1"); 
    }

    public void f2(){
        n = 1; //普通方法中既能使用静态变量/方法也能使用普通变量/方法
        setA("b");
    }
}
public class B extends A {

    public static void print(){
        //System.out.println(super.a); //静态方法中不能使用super关键字
        System.out.println(a);
    }
}
A.setA("~");
System.out.println(A.a); //~ 类名调用静态变量

六、代码块

静态代码块只在类加载时调用一次,代码块在每次创建对象时调用一次

在静态代码块中只能使用静态变量和静态方法

类加载条件:

  1. 调用类中的静态变量和静态方法时会加载该类
  2. 创建子类对象时会加载父类对象
  3. 创建对象实例时会加载该类(包括反射)
public class A {

    int n = getN();

    static {
        System.out.println("A类静态代码块被执行...");
    }

    {
        System.out.println("A类普通代码块被执行...");
    }

    public A() {
        System.out.println("A类的无参构造函数被调用...");
    }

    public int getN() {
        System.out.println("A类的getN()方法被调用...");
        return 1;
    }
}
public class B extends A {

    static int n = getNum();

    static {
        System.out.println("B类静态代码块被执行...");
    }

    {
        System.out.println("B类普通代码块被执行...");
    }

    public static void print(){
        System.out.println("B类的静态方法print()被执行...");
    }

    public B() {
        System.out.println("B类的无参构造函数被调用...");
    }

    public static int getNum() {
        System.out.println("B类getNum()方法被调用...");
        return 1;
    }
}
//首先会加载父类(初始化静态变量并调用静态方法),然后加载子类(初始化静态变量并调用静态方法),然后初始化父类的普通变量并调用父类的普通代码块,调用父类构造函数,然后初始化子类的普通变量并调用子类的普通代码块,最后调用子类的构造函数
B b = new B();
System.out.println("----------------------");
B b1 = new B();

/*
A类静态代码块被执行...
B类getNum()方法被调用...
B类静态代码块被执行...
A类的getN()方法被调用...
A类普通代码块被执行...
A类的无参构造函数被调用...
B类普通代码块被执行...
B类的无参构造函数被调用...
----------------------
A类的getN()方法被调用...
A类普通代码块被执行...
A类的无参构造函数被调用...
B类普通代码块被执行...
B类的无参构造函数被调用...
*/

七、final常量

被final修饰的变量叫做常量,用大写字母命名,赋值后就不能修改了

  • 可以在声明时赋值
  • 可以在构造函数中赋值
  • 可以在代码块中赋值

被static和final修饰的变量只能在声明时(访问时不会导致类加载)或在静态代码块中进行赋值

被final修饰的方法,子类不能进行重写,但是可以调用

被final修饰的类,不能被继承

public class A {

    final int i = 1; //声明时赋值

    final int j;

    final int k;

    static final int m = 4; //声明时赋值

    static final int n;

    static {
        System.out.println("A类的静态代码块执行...");
        n = 5; //静态代码中赋值
    }

    {
        k = 3; //代码块中赋值
    }

    public A() {
        j = 2; //构造函数中赋值
    }

    public final void f1(){

    }
}
public final class B extends A{

    //f1()为final方法不能被重写
    //public final void f1(){
    //
    //}

    public void f(){
        f1(); //调用A类的final方法
    }
}
//B类为final类不能被继承
public class C /* extends B*/{

}
//访问static final修饰的变量不会进行类加载(没有运行静态代码块)
System.out.println(A.m); //4

八、抽象abstract

abstract修饰的类为抽象类,类中可以没有抽象方法,继承该类要么声明为抽象类或重写抽象方法

abstract修饰的方法为抽象方法,没有方法体,只能定义在抽象类中,抽象方法不能被以下修饰符修饰

  • private 私有方法不能被子类重写
  • static 静态方法不能被子类重写
  • final 修饰的方法不能被子类重写
public abstract class A {

    //抽象方法不能被private、final、static修饰
    //private abstract void f1();

    //public final abstract void f1();

    //public static abstract void f1();

    public abstract void f1();
}
public class B extends A {

    //继承抽象类,实现抽象方法
    @Override
    public void f1() {

    }
}
public abstract class C extends A {

}

九、接口

接口中的属性默认为public static final修饰,方法默认为public abstract修饰

jdk1.8以后接口中可以定义静态方法也可以进行默认实现

public interface A {

    public static final int a = 1; //属性

    //public abstract void f1(); //方法
	
    //默认实现
    default void f1(){
        System.out.println(a);
    }

    //静态方法
    static void f2(){
        System.out.println("static: " + a);
    }
}
public class B implements A { //实现接口A

    //重写抽象方法
    @Override
    public void f1() {
        System.out.println("B");
    }
}
B b = new B();
b.f1(); //B
A.f2(); //static: 1

一个接口可以继承多个接口

public interface A {

}

interface J{

}

interface k extends A, J{

}

十、内部类

1.局部内部类

定义在类的方法或代码块中,作用域在方法或代码块中,其他位置不能访问,不能使用访问修饰符,可以用final修饰

public class Outer {

    private int n1 = 100;

    //代码块
    {
        System.out.println("Outer的代码块...");
        class Inner1{
            //与外部类重名属性
            private int n1 = 200;

            public void f3(){
                System.out.println("Inner2 n1=" + n1); //访问重名遵循就近原则
               	//访问外部类重名属性使用外部类名.this.属性名
                System.out.println("Outer n1=" + Outer.this.n1); 
            }
        }

        new Inner1().f3();
    }

    public void f1(){
        System.out.println("Outer f1()...");
        class Inner2{
            //与外部类重名属性
            private int n1 = 300;

            public void f2(){
                System.out.println("Inner2 n1=" + n1); //访问重名遵循就近原则
                //访问外部类重名属性使用外部类名.this.属性名
                System.out.println("Outer n1=" + Outer.this.n1);
            }
        }

        new Inner2().f2();
    }
}
Outer outer = new Outer();
outer.f1();

/*
Outer的代码块...
Inner2 n1=200
Outer n1=100
Outer f1()...
Inner2 n1=300
Outer n1=100
*/
2.匿名内部类

定义在类的方法或代码块中,作用域在方法或代码块中,不能使用访问修饰符

interface IA{
    void m1();
}
class A {
    public void f1(){
        System.out.println("A f1()...");
    }
}
public class Outer {
    
    private static int n = 100;

    public static void main(String[] args) {

        //接口的匿名内部类
        /*
            class 匿名类(外部类全类名$数字) implements IA{
                @Override
                public void m1() {
                    System.out.println("匿名类实现m1()方法...");
                }
            }
         */
        IA a = new IA() {
            
            int n = 1;
            
            @Override
            public void m1() {
                System.out.println(n); //访问重名遵循就近原则
                System.out.println(Outer.n);
                System.out.println("匿名类实现m1()方法...");
            }
        };
        System.out.println(a.getClass());
        a.m1();

        //类的匿名内部类
        /*
            class 匿名类(外部类全类名$数字) extends A{
                @Override
                public void f1() {
                    System.out.println("匿名内部类重写f1()方法...");
                }
            }
         */
        A a1 = new A() {
            
            int n = 2;
            
            @Override
            public void f1() {
                System.out.println(n); //访问重名遵循就近原则
                System.out.println(Outer.n);
                System.out.println("匿名内部类重写f1()方法...");
            }
        };
        System.out.println(a1.getClass());
        a1.f1(); //动态绑定

    }
}

/*
class com.hubu.oop.innerclass.anonymous.Outer$1
1
100
匿名类实现m1()方法...
class com.hubu.oop.innerclass.anonymous.Outer$2
2
100
匿名内部类重写f1()方法...
*/
3.成员内部类

定义在类的成员位置,作用域为类作用域,可以使用访问修饰符,只能访问外部类的静态成员

public class Outer {

    private int n;

    class Inner{

        private int n = 1;

        public void f1(){
            System.out.println(n); //访问重名遵循就近原则
            System.out.println(Outer.this.n); //访问外部类重名属性使用外部类名.this.属性名
        }
    }

    public Inner getInstance(){
        return new Inner();
    }
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.f1();
System.out.println("----------------------------");
Outer.Inner instance = outer.getInstance();
instance.f1();

/*
1
0
----------------------------
1
0
*/
4.静态内部类

定义在类的成员位置,被static修饰,作用域为类作用域,可以使用访问修饰符,只能访问外部类的静态成员

public class Outer {

    private static int n;

    static class Inner{

        private int n = 1;

        public void f1(){
            System.out.println(n);
            System.out.println(Outer.n);
        }
    }

    public Inner getInstance(){
        return new Inner();
    }
}
Outer.Inner inner = new Outer.Inner();
inner.f1();
System.out.println("----------------------------");
Outer outer = new Outer();
Outer.Inner instance = outer.getInstance();
instance.f1();

/*
1
0
----------------------------
1
0
*/

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值