• 面向对象(高级)


    目录

    1. 类变量和类方法(静态变量)

    类变量(静态变量)

    内存刨析:

    注意事项和使用细节:

     类方法(静态方法): 

    使用场景:

    注意事项和细节讨论:

    2. main方法中的语法

    3. 代码块

    4. final关键字

    5. 内部类

    1. 局部内部

    2. 匿名内部类

    匿名内部类的使用细节:

    实例内部类

    静态内部类


    每个人都可以活得更精彩,不要让惰性毁了你,坚持学习。

    随着我们对面向对象的不断了解,我们已经学过很多语法和思想了,虽然不是很好理解,但是也没有难到令人放弃,随着不断地练习,总有一天自己会瞬间通透的;更难学的还在后面。不要觉得Java很难,多给自己点信心,加油。

    那好,聊到这里我们继续学习更难的知识点。

    1. 类变量和类方法(静态变量)

    类变量(静态变量)

    引出: 假设有一群小孩在堆雪人,不断地有小孩要加入;问;某个时刻共有多少个小孩?

    思路: main中定义count,每个小孩加入后count++。

    问题分析: count是个独立对象,访问起来很麻烦,并且没有用到面向对象(OOP)

    问题解决: 在类中引入一个类变量(静态变量)static修饰 ,该变量最大的特点就是被该程序中的所有对象共有。

    例如:

    1. public class demo {
    2. public static void main(String[] args) {
    3. new Child("小明");
    4. new Child("小红");
    5. System.out.println(Child.count);
    6. }
    7. }
    8. class Child {
    9. private String name;
    10. public static int count = 0;
    11. public Child(String name) {
    12. this.name = name;
    13. count++;
    14. }
    15. }

    图示:

    内存刨析:

    随着版本的不同,静态变量存放的位置不同。

    有的书是这样得:

     而有的书是说,static得信息存在静态域中:

    无论放在哪里,对我们的使用并不影响。

    注意事项和使用细节:

    🐤  何时使用:需要所有对象共享一个变量

    🐤  类变量和实例变量(普通变量)的区别:

    类变量是该类所有对象所共享对的,而实例变量是个类对象独有的。

    🐤  加上static成为类变量或静态变量,否则称之为实例对象、非静态变量或普通变量。

    🐤  类变量的访问方式:

    1. 在同一个类中访问:直接引用变量名即可

    2. 在不同类中访问:必须通过类名.类变量名字;如:Child.count

    原因:类变量是随着类的加载而加载的,所以在类的加载时就可以访问到类变量信息。

    🐤  类变量的周期从类的开始到类的结束。

     类方法(静态方法): 

    语法形式:

    访问修饰符        static        返回类型        方法名() {     }   (推荐)      或者

    static        访问修饰符        返回类型        方法名() {     }   (不推荐)

    调用:

    语法:类名.方法名 或者 对象名.方法名 (前提是满足访问修饰符的访问权限和范围)

    注意:

    非静态的可以调用静态的,但静态的只能调用静态的不可以调用非静态的。

    在程序运行时,类先加载,静态此时也就加载,在对象创建后才有了非静态;所以时先有静态后有非静态。

    使用场景:

    当方法不涉及到任何和对象相关的成员时,则可以将其设为静态方法,以便提升效率。

    比如:工具类中的Utils、Math工具、Array类。

    注意事项和细节讨论:

    🐤 类方法和普通方法都是随着类的加载而加载的,将结构信息储存在方法区:

    类方法中无this的参数

    而普通方法隐藏着this的参数

    🐤类方法可以通过类名调用,也可以通过方法名调用

    🐤普通方法和对象有关,需要通过对象名调用,比如:对象名.方法名(参数)【dog.eat()】 不能通过类名调用

    例如:

    1. class Dog {
    2. public void say() {
    3. //非静态
    4. }
    5. public static void hi(){
    6. //静态
    7. }
    8. }
    9. public class demo {
    10. public static void main(String[] args) {
    11. Dog.hi();
    12. Dog.say();//报错
    13. }
    14. }

    🐤 类方法中不允许使用与对象有关的关键字,例如:this和super

    🐤 类方法只能访问静态变量或静态方法,而普通成员中既可以访问静态也可以访问非静态

    总结:静态可以访问静态的,非静态可以访问静态和非静态的。

    2. main方法中的语法

    深入理解main方法

    1. mian方法被Java虚拟机调用

    2. Java虚拟机调用main方法访问权限必须是public

    3. Java虚拟机在执行main方法时不需要创建对象,所以i方法必须是static的

    4.该方法接受String类型的数组传参,该数组中保存执行Java命令时传递所给类的参数:

    也就是我们main方法中的这个:

    String[] args  值是由执行程序时传进去的参数形成的一个数组。
    

    例如:

    1. public class Main {
    2. private static String name = "小明";
    3. private int age = 10;
    4. public static void main(String[] args) {
    5. System.out.println(name);
    6. hi();
    7. System.out.println(age);//错位,main中可以访问静态的,但是不可以访问非静态的
    8. }
    9. public static void hi() {
    10. System.out.println("hello");
    11. }
    12. }


     提示: 

    🐔: 在main方法中,我们可以直接调用main方法所在类的静态方法或静态属性

    🐔: 但是,不能直接调用非静态的成员变量;想要调用,只能通过关键字 new 出实例对象,通过对象去访问。

    如何在idea上实现main的动态传值:

    由于我的是社区版的我就把b站老韩的视频链接放下来:

    0384_韩顺平Java_main动态传值_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1fh411y7R8?p=385&vd_source=93857e709631270a9e771d5aaee79ecb

    3. 代码块

    基本介绍:代码块又称初始化块,属于类中的成员【即类的一部分】,类似于方法,讲语法封装在方法体中,用 { } 包围起来。

    但和方法不同,无名,无参,无返回值,只是在类加载时,或创建对象时隐式调用。

    语法:

    修饰符{

            代码

    };//“ ;”可写可不写

    根据代码块定义的位置以及关键字,又可分为以下四种:
    普通代码块
    构造块
    静态块
    同步代码块(后续讲解多线程部分再谈)

    🐔 普通代码块

    此时的修饰符就是default默认不写;

    {

            代码

    }

    🐔 静态代码块

    此时的修饰符就是static

    static{

            代码

    }

    🐔 构造块

    定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量

    使用场景:

    如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的复用性。

    举例:🌰

    1. public class Student{
    2. private String name;
    3. private String gender;
    4. private int age;
    5. private double score;
    6. private static String classRoom;
    7. //实例代码块
    8. {
    9. this.name = "marry";
    10. this.age = 12;
    11. this.gender = "man";
    12. System.out.println(name+" is instance init()!");
    13. }
    14. // 静态代码块
    15. static {
    16. classRoom = "303";
    17. System.out.println("I am static init()!");
    18. }
    19. // 构造代码块
    20. public Student(){
    21. System.out.println("I am Student init()!");
    22. }
    23. public static void main(String[] args) {
    24. Student s1 = new Student();
    25. Student s2 = new Student();
    26. }
    27. }

    从这个例子我们可以看出如下总结:

    🐔创建对象时,会有如下顺序:

    执行静态代码块 ——> 执行普通代码块 ——> 执行构造代码块

    🐔 静态代码块仅执行一次,静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的

    🐔如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)

    🐔实例代码块只有在创建对象时才会执行

    如果我们在此基础上加上继承,结构又会如何,执行顺序又会如何?

    1. public class demo {
    2. public static void main(String[] args) {
    3. new Son();
    4. }
    5. }
    6. class Father {
    7. public Father() {
    8. System.out.println("我是父类的构造代码块");
    9. }
    10. {
    11. System.out.println("我是父类的代码块");
    12. }
    13. static{
    14. System.out.println("我是父类静态代码块");
    15. }
    16. }
    17. class Son extends Father{
    18. public Son() {
    19. System.out.println("我是子类的构造代码块");
    20. }
    21. {
    22. System.out.println("我是子类的代码块");
    23. }
    24. static{
    25. System.out.println("我是子类静态代码块");
    26. }
    27. }

    可以发现如下顺序:

     执行父类静态代码块 ——> 执行子类静态代码块 ——> 执行父类普通代码块 ——> 执行父类构造代码块——> 执行普通代码块 ——> 执行构造代码块

    4. final关键字

    final关键字可以修饰类、属性、方法、局部变量。

    使用到final关键字的情况:

    🐔 当不希望类被继承:

    final class A { } //无法被继承

    🐔 当不希望父类的某个方法被子类重写时

    🐔 当不希望某个类中某个值被修改

    public  final int SIZE = 5 ;

    🐔当不希望某个局部变量被修改

    final double TAX_RATE = 0.08 ;

    注意事项和使用细节:

    1. final修饰的属性又叫常量,一般用 XX_XX_XX命名

    2. final修饰的属性在定义时必须初始化,可选在如下位置:

    (1)如:public  final int SIZE = 5 ;

    (2)构造器中;

    (3)在代码块中;

    3. 如果final修饰的属性是静态的,则被初始化的位置只能是

    (1)定义时

    (2)在静态代码块中,不能在构造器中赋值,因为实例对象才能调用构造器

    4. final类不能被继承,但是可以实例化

    5. final不能修饰构造方法

    6. final和static往往搭配使用,效率更高,不会导致类加载,底层的编译器进行了优化处理。

    5. 内部类

    内部类是本章的重难点,在以后看源码的时候,会看见大量的内部类。

    基本介绍:当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

    基本语法:

     class OutClass { //外部类
        class InnerClass{ //内部类
        }
    }

    class other { //外部其他类

    }

    特点: 既可以访问私有属性,又可以体现类与类之间的包含关系。

    内部类的分类
    定义在外部类局部位置上(比如方法内);

    (1)局部内部类(有类名)

    (2)匿名内部类(没有类名)

    定义在外部类的成员位置上:

    (1)实例内部类(没有static)

    (2)静态内部类(使用static修饰)

    1. 局部内部

    说明:

    1. 可以访问外部类的所有成员;

    2. 不能添加访问修饰符,因为它的地位相当与局部变量,但是可以用final修饰

    3. 作用域:仅仅只定义在它的方法或者代码段中

    4. 在访问成员时直接调用即可

    5. 外部类在方法中,可以创建内部类对象,然后调用方法即可;如:

    1. public class Outer {
    2. public static void main(String[] args) {
    3. Outer outer = new Outer();
    4. outer.n1();
    5. }
    6. private int n1 = 100;
    7. private void m2() {
    8. System.out.println("2");
    9. }
    10. public void n1() {
    11. final class Inner {
    12. public void f1() {
    13. System.out.println("1");
    14. m2();
    15. }
    16. }
    17. Inner inner = new Inner();
    18. inner.f1();
    19. }
    20. }

     6. 如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.this.成员名

    2. 匿名内部类

    本质:

    (1)本质仍是一个类

    (2)是个内部类

    (3)从宏观上来来书评该类没有名字,但是在底层上被赋予了一个名字

    (4)同时还是个对象

    说明:匿名内部类定义在外部类的局部位置,譬如方法体,但是没有名字

    语法:

    new 类或者接口(参数列表){

            类体

    };

    例如:

    1. class Outer {
    2. public static void main(String[] args) {
    3. Outer outer = new Outer();
    4. outer.method();
    5. }
    6. private int m = 10;
    7. public void method() {
    8. //需求:使用一个IA接口,并创建一个对象
    9. //传统写法:写一个类,实现该接口
    10. IA tiger = new Tiger();
    11. tiger.cry();
    12. }
    13. }
    14. interface IA {
    15. public void cry();
    16. }
    17. class Tiger implements IA {
    18. @Override
    19. public void cry() {
    20. System.out.println("嗷嗷嗷嗷");
    21. }
    22. }

     但是如果我只用一次,后面就不再使用,这里单独写一个Tiger时不时太浪费了,所以我们可以试着用匿名内部类写。

    1. class Outer {
    2. public static void main(String[] args) {
    3. Outer outer = new Outer();
    4. outer.method();
    5. }
    6. private int m = 10;
    7. public void method() {
    8. //需求:使用一个IA接口,并创建一个对象
    9. //传统写法:写一个类,实现该接口
    10. //现在我们不要Tiger类
    11. IA tiger = new IA() {
    12. @Override
    13. public void cry() {
    14. System.out.println("嗷嗷嗷嗷");
    15. }
    16. };
    17. tiger.cry();
    18. }
    19. }
    20. interface IA {
    21. public void cry();
    22. }

    本来我们的接口是不可以直接实例化对象的,但这里就相当于重写了cry()方法,并没有实例化它的对象。

    1. class Outer {
    2. public static void main(String[] args) {
    3. Outer outer = new Outer();
    4. outer.method();
    5. }
    6. private int m = 10;
    7. public void method() {
    8. //需求:使用一个IA接口,并创建一个对象
    9. //传统写法:写一个类,实现该接口
    10. //现在我们不要Tiger类
    11. //使用匿名内部类来简化代码
    12. //此时的编译类型是:IA接口
    13. //此时的运行类型是:匿名内部类
    14. /*
    15. 底层:
    16. class XXXXXXX(类名) implements IA {
    17. @Override
    18. public void cry() {
    19. System.out.println("嗷嗷嗷嗷");
    20. }
    21. };
    22. */
    23. IA tiger = new IA() {
    24. @Override
    25. public void cry() {
    26. System.out.println("嗷嗷嗷嗷");
    27. }
    28. };
    29. tiger.cry();
    30. }
    31. }
    32. interface IA {
    33. public void cry();
    34. }

     现在我们来看看它在底层中具体的名字:

    System.out.println(tiger.getClass());我们用getClass()方法来获取名字。

     它的名字就是: class 包名.外部类名$1

    匿名内部类的使用细节:

    1.匿名内部类既是一个类的定义,又是一个对象;从语法上看它既有类的特性又有对象的特征。

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

    3. 不可以添加修饰符,因为他就是个局部变量

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

    5.外部类不可以访问匿名内部类(因为他是个局部变量)

    6.如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.this.成员名

    实例内部类

    在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类
    即未被static修饰的成员内部类

    说明:

    1. 可以访问外部类的所有成员,包括私有的。

    例:

    1. public class Main {
    2. public static void main(String[] args) {
    3. Outer outer = new Outer();
    4. outer.T();
    5. }
    6. }
    7. class Outer {
    8. private int n1 = 10;
    9. class Inner {
    10. public void say() {
    11. System.out.println("Outer 中的 n1 = " + n1);
    12. }
    13. }
    14. public void T(){
    15. Inner inner = new Inner();
    16. inner.say();
    17. }
    18. }

     只能通过这种方式访问。

    2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
    3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:【外部类名称.this.同名成员 来访问】
    4. 实例内部类对象必须在先有外部类对象前提下才能创建
    5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
    6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

    静态内部类

    被static修饰的内部成员类称为静态内部类

    1. public class Main {
    2. public static void main(String[] args) {
    3. // 静态内部类对象创建 & 成员访问
    4. OutClass.InnerClass innerClass = new OutClass.InnerClass();
    5. innerClass.methodInner();
    6. }
    7. }
    8. class OutClass {
    9. private int a;
    10. static int b;
    11. public void methodA() {
    12. a = 10;
    13. System.out.println(a);
    14. }
    15. public static void methodB() {
    16. System.out.println(b);
    17. } // 静态内部类:被static修饰的成员内部类
    18. static class InnerClass {
    19. public void methodInner() {
    20. // 在内部类中只能访问外部类的静态成员
    21. // a = 100; // 编译失败,因为a不是类成员变量
    22. b = 200;
    23. // methodA(); // 编译失败,因为methodB()不是类成员方法
    24. methodB();
    25. }
    26. }
    27. }

    【注意事项】
    1. 在静态内部类中只能访问外部类中的静态成员
    2. 创建静态内部类对象时,不需要先创建外部类对象
    3.作用域:同其他成员,为整个类体

    4.如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.成员名

  • 相关阅读:
    [代码已开源]集群聊天服务器与客户端开发
    APP自动化测试,Appium+PO模式+Pytest框架实战—项目案例
    vue中v-for循环数组使用方法中splice删除数组元素(每次都删掉点击的下面的一项)
    模拟经营微信小游戏-休闲餐厅上线了
    33张Java高级进阶技术思维导图,白嫖大佬梳理的技术要点!只需看重点,学习效率提升300%(建议收藏)
    day006
    cp -r, 保留拷贝的文件原本的权限和属性
    NumPy创建数组的方法
    Docker遇到的一些问题和感想
    C++ STL中的Multimap概述
  • 原文地址:https://blog.csdn.net/weixin_67807492/article/details/127976524