我是靠谱客的博主 落后香氛,最近开发中收集的这篇文章主要介绍Java笔记——12.面向对象(高级)12.面向对象(高级),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

12.面向对象(高级)


类变量/类方法

关于静态变量在内存中的存储,主要和jdk的版本有关,jdk8以前放在方法区的静态域中,jdk8之后放在堆中(class对象)

注意:关于这一点会在后面的反射中再详细的学习,现在先了解一下即可

只要记住不管static变量在哪里:(1)static变量是同一个类所有对象共享(2)static类变量在类加载的时候就生成了


如何访问类变量/类方法

类名.类变量名(推荐) 或者 对象名.类变量名

修饰符 static 返回类型 类方法名 (形参) (推荐)或者 static 修饰符 返回类型 类方法名 (形参)

类变量/类方法的访问修饰符的访问权限和普通变量一样

小结:静态方法只能访问静态变量和静态方法,普通成员方法既可以访问静态成员也可以访问非静态成员


在IDEA中如何给main方法传递参数?

在命令行界面如何传递参数在第一章笔记Java概述讲过了,现在来讲一下在IDEA如何传递参数

第一步:点击Edit Configurations…

在这里插入图片描述

第二步:在Program arguments里填入参数即可,注意事项和命令行界面一样

在这里插入图片描述


对于访问修饰符,什么情况下该使用什么修饰符呢?

  • 属性通常使用private封装起来
  • 方法一般使用public用于被调用
  • 会被子类继承的方法,通常使用protected
  • 再就是作用范围最小原则,简单说,能用private就用private,不行就放大一级,用默认,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了。

注意:只是一般情况,最终还是要根据实际情况而定


代码块

基本语法:[修饰符] { 代码 };

  • 修饰符可以不写,写的话只能写static
  • 代码块分两种:静态代码块和非静态代码块
  • 最后的;号可以省略(建议不省略)

代码块的调用顺序优先于构造器

相当于另一种形式的构造器(对构造器的补充机制),可以做初始化的操作,如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性


使用细节:

  1. 静态代码块随着类的加载而执行并且只会执行一次(因为类也只会加载一次),如果是普通代码块,每创建一个对象,就执行一次

  2. 类什么时候被加载(重点):(1)创建对象实例时(2)创建子类对象实例,父类也会被加载(3)使用类的静态成员时

  3. 普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次就会调用一次,如果只是使用类的静态成员时,普通代码块并不会执行

  4. 创建一个对象时,在一个类中的调用顺序是(重点):

    (1)调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级是一样的,如果有多个则按它们的顺序调用)

    (2)调用普通代码块和普通属性初始化(注意:普通代码块和普通属性初始化调用的优先级是一样的,如果有多个则按它们的顺序调用)

    (3)调用构造方法

  5. 构造器的最前面其实隐含了super()和调用普通代码块

  6. 创建一个子类对象时(继承关系),它们的调用顺序是(重点):

    (1)父类的静态代码块和静态属性

    (2)子类的静态代码块和静态属性

    (3)父类的普通代码块和普通属性初始化

    (4)父类的构造方法

    (5)子类的普通代码块和普通属性初始化

    (6)子类的构造方法

    本质上就是先加载类信息(1和2),然后再创建父类对象(3和4),最后创建子类对象(5和6)

  7. 静态代码块只能调用静态成员,普通代码块可以调用任意成员


单例设计模式

饿汉式/懒汉式

实现步骤:

  • 构造器私有化=>防止用户直接new
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法 getInstance

两种模式的比较:

  • 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载时就创建了对象实例,而懒汉式是在使用时才创建
  • 饿汉式不存在线程安全问题,懒汉式存在线程安全问题(后面学习线程时会完善)
  • 饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题

使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。

看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式。

//饿汉式
class Cat {
    private String name;
    private static Cat cat = new Cat("小可爱");
    private Cat(String name) {
        this.name = name;
    }
    public static Cat getInstance() {
        return cat;
    }
}

//懒汉式
class Cat {
    private String name;
    private static Cat cat;
    private Cat(String name) {
        this.name = name;
    }
    public static Cat getInstance() {
        if(cat == null){
            cat = new Cat("小可爱");
        }
        return cat;
    }
}

final关键字

一般在下面的情况可以使用final关键字:

  • 当不希望类被继承时,可以用final修饰
  • 当不希望父类的某个方法被子类覆盖/重写时,可以用final修饰
  • 当不希望类的某个属性的值被修改时,可以用final修饰
  • 当不希望某个局部变量被修改时,可以使用final修饰

使用细节:

  1. final修饰的属性又叫常量,一般用XX_XX_XX来命名
  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:(1)定义时(2)在构造器中(3)在代码块中
  3. 如果final修饰的属性是静态的,则初始化的位置只能是:(1)定义时(2)在静态代码块中(不能在构造器中赋值)
  4. final类不能继承,但是可以实例化对象
  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
  6. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final
  7. final不能修饰构造方法(构造器)
  8. final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
  9. 包装类(Integer,Double,Float,Boolean等)都是final类,String类也是final类
//final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
class AA {
    public final static int num = 100;
    static {
        System.out.println("AA 静态代码块");
    }
}

//在main方法中
System.out.println("结果为:" + AA.num);

//输出结果为:100,不会输出静态代码块中的内容
结果为:100

抽象类

基本介绍:

  • 原因:父类方法不确定性,即该方法在父类中实现没什么实际意义
  • 抽象方法就是没有实现的方法,即没有方法体
  • 当一个类中存在抽象方法,需要将该类声明为抽象类
  • 一般来说,抽象方法会被继承,由其子类来实现抽象方法

使用细节:

  1. 抽象类不能被实例化
  2. 抽象类不一定要包含抽象方法,也就是说,抽象类可以没有抽象方法
  3. 一旦类包含了抽象方法,则这个类必须声明为abstract
  4. abstract只能修饰类和方法,不能修饰属性和其他的
  5. 抽象类可以有任意成员(抽象类的本质还是类),比如非抽象方法,构造器,静态属性等等
  6. 抽象方法不能有主体,即不能实现
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
  8. 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的

抽象模板模式

视频地址:https://www.bilibili.com/video/BV1fh411y7R8?p=402


接口

注意:

  • 在jdk7.0前接口里的所有方法都没有方法体,即都是抽象方法。jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现,需要使用default关键字修饰
  • 在接口中,抽象方法可以省略abstract关键字
public interface A {
    public static void a() {
        System.out.println("静态方法");
    }

    default public void b() {
        System.out.println("默认方法");
    }
}

使用细节:

  1. 接口不能被实例化
  2. 接口中所有方法是public方法,接口中抽象方法可以不用abstract修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类同时可以实现多个接口
  6. 接口中的属性只能是final的,而且是public static final 修饰符,比如 int a = 1;实际上是 public static final int a = 1;
  7. 接口中属性的访问形式:接口名.属性名
  8. 接口不能继承其他类,但可以继承多个其他接口
  9. 接口的修饰符只能是public和默认,这点和类的修饰符是一样的

实现接口VS继承类

  • 当子类继承了父类,就自动的拥有父类的功能,如果子类需要扩展功能,可以通过实现接口的方式扩展
  • 继承的价值主要在于:解决代码的复用性和可维护性
  • 接口的价值主要在于:设计好各种规范(方法),让其它类去实现
  • 接口比继承更加灵活,继承是满足 is-a 的关系,接口需要满足 like-a 的关系
  • 接口在一定程度上实现代码解耦

小结:实现接口机制 是对 Java单继承机制 的补充


接口的多态特性

  • 多态参数:接口类型的引用变量可以指向实现了接口的类的对象
  • 多态数组:接口类型数组
  • 接口多态传递
interface IF {}
class A implements IF{}
class B implements IF{}

//多态参数:接口类型的引用变量可以指向实现了接口的类的对象
IF if01 = new A();

//多态数组:接口类型数组
IF[] arr = new IF[2];
arr[0] = new A();
arr[1] = new B();

//接口多态传递
//接口IQ继承了IG,类A实现了IQ接口
interface IG {}
interface IQ extends IG {}
class A implements IQ {}
//所以下面的是对的,相当于A类也实现了IG接口
IG ig = new A();
//验证方法:假如IG里有一个hi方法
interface IG {
    public void hi();
}
//那么当A继承IQ时会提示你需要实现hi方法

内部类

类的五大成员:属性,构造器,方法,代码块,内部类

内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

注意:内部类是学习的难点和重点,看底层源码时,有大量的内部类


内部类的分类:四种

定义在外部类局部位置上(比如方法内):(1)局部内部类(有类名) (2)匿名内部类(没有类名,重点!!!)

定义在外部类的成员位置上:(1)成员内部类(没有用static修饰) (2)静态内部类(使用static修饰)


局部内部类

  • 可以直接访问外部类的所有成员,包括私有的

  • 不能添加访问修饰符,因为它的地位就是一个局部变量(本质还是一个类),但是可以使用final修饰

  • 作用域:仅仅在定义它的方法或代码块中

  • 访问方式:外部类在方法中,可以创建内部类对象,然后再访问

  • 外部其他类不能访问局部内部类

  • 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer(100);
        outer.m1();
        System.out.println("outer的哈希值为:" + outer);
    }
}

class Outer {
    private int n = 0;
    public static int k = 10;

    public Outer(int n) {
        this.n = n;
    }

    public void m1() {
        final class Inner {
            private int n = 200;

            public void f1() {
                //Outer.this 的本质就是外部类的一个对象,哪个对象调用了m1()方法,就指向哪个对象
                System.out.println("n = " + n);
                System.out.println("n = " + Outer.this.n);//这里指向outer这个对象
                System.out.println("Outer.this的哈希值为:" + Outer.this);
            }
        }
        Inner inner = new Inner();
        inner.f1();
    }

    {
        System.out.println("hahahaha");
    }
}

匿名内部类

核心:本质是类,也是内部类,该类没有名字(有系统取的名字),同时还是个对象

基本语法:new 类或接口(参数列表){ 类体 };

原因:某个类只使用一次,后面不再使用,可以使用匿名内部类来简化开发

视频地址:https://www.bilibili.com/video/BV1fh411y7R8?p=416

  • 可以直接访问外部类的所有成员,包括私有的
  • 不能添加访问修饰符,因为它的地位就是一个局部变量(本质还是一个类),但是可以使用final修饰
  • 作用域:仅仅在定义它的方法或代码块中
  • 访问方式:外部类在方法中,可以创建内部类对象,然后再访问
  • 外部其他类不能访问匿名内部类
  • 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.m1();
    }
}

class Outer {
    private int n = 100;

    public void m1() {
        
        //先以接口为例
        //ie的编译类型是什么?IE
        //ie的运行类型是什么?就是匿名内部类 Outer$1
        /*
        * 我们看底层 会分配类名 Outer$1
        * class Outer$1 implement IE {
            @Override
            public void cry() {
                System.out.println("呜呜呜呜呜");
            }
        * }
        * */
        IE ie = new IE(){
            @Override
            public void cry() {
                System.out.println("呜呜呜呜呜");
            }
        };
        ie.cry();
        //可以验证(.getClass()方法可以返回对象的类型)
        System.out.println("ie的运行类型为:" + ie.getClass());
        
        //现在以类为例
        //son的编译类型是什么?Son
        //son的运行类型是什么?就是匿名内部类 Outer$2
        /*
        * 我们看底层 会分配类名 Outer$2
        * class Outer$2 extends Son {
            @Override
            public void eat() {
                System.out.println("你还吃,就知道吃");
            }
        * }
        * */
        //============================================
        //注意和Son son = new Son("小胖");的区别!!!!!!
        //============================================
        //另外,假如Son是抽象类,则必须重写eat()方法
        Son son = new Son("小胖"){
            @Override
            public void eat() {
                System.out.println("你还吃,就知道吃");
            }
        };
        son.eat();
        //可以验证
        System.out.println("son的运行类型为:" + son.getClass());
        
        //还可以这种写法
        Son son = new Son("小胖"){
            @Override
            public void eat() {
                System.out.println("你还吃,就知道吃");
            }
        }.eat();//注意这里!!!
    }
}

interface IE{
    public void cry();
}

class Son {
    private String name = "";

    public Son(String name) {
        this.name = name;
        System.out.println("name = " + this.name);
    }

    public void eat(){
        System.out.println("吃吃吃");
    }
}

注:所以以后看见一个类的运行类型是XXX$X,大概率就是匿名内部类


成员内部类

  • 可以直接访问外部类的所有成员,包括私有的

  • 可以添加任意访问修饰符(四种都行),因为它的地位就是一个成员

  • 作用域:和外部类的其他成员一样,为整个类体

  • 访问方式:外部类在方法中,可以创建内部类对象,然后再访问

  • 外部其他类可以访问成员内部类,有两种方式

    方式一:外部类对象名.成员内部类名 引用名 = 外部类对象名.new 成员内部类名();

    方式二:在外部类中编写一个方法,可以返回成员内部类对象(类似于单例模式的getInstance()方法)

  • 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问


静态内部类

  • 可以直接访问外部类的所有静态成员,包括私有的,但不能访问非静态成员

  • 可以添加任意访问修饰符(四种都行),因为它的地位就是一个成员

  • 作用域:和外部类的其他成员一样,为整个类体

  • 访问方式:外部类在方法中,可以创建内部类对象,然后再访问

  • 外部其他类可以访问静态内部类,有两种方式

    方式一:外部类名.静态内部类名 引用名 = new 外部类名.静态内部类名();

    方式二:在外部类中编写一个方法,可以返回静态内部类对象(可以将返回的方法也写成静态的)

  • 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

最后

以上就是落后香氛为你收集整理的Java笔记——12.面向对象(高级)12.面向对象(高级)的全部内容,希望文章能够帮你解决Java笔记——12.面向对象(高级)12.面向对象(高级)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部