面向对象(高级)
创始人
2024-04-11 17:53:08
0

目录

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

类变量(静态变量)

内存刨析:

注意事项和使用细节:

 类方法(静态方法): 

使用场景:

注意事项和细节讨论:

2. main方法中的语法

3. 代码块

4. final关键字

5. 内部类

1. 局部内部

2. 匿名内部类

匿名内部类的使用细节:

实例内部类

静态内部类


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

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

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

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

类变量(静态变量)

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

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

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

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

例如:

public class demo {public static void main(String[] args) {new Child("小明");new Child("小红");System.out.println(Child.count);}
}
class Child {private String name;public static int count = 0;public Child(String name) {this.name = name;count++;}
}

图示:

内存刨析:

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

有的书是这样得:

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

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

注意事项和使用细节:

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

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

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

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

🐤  类变量的访问方式:

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

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

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

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

 类方法(静态方法): 

语法形式:

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

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

调用:

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

注意:

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

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

使用场景:

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

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

注意事项和细节讨论:

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

类方法中无this的参数

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

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

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

例如:

class Dog {public void say()  {//非静态}public static void hi(){//静态}
}
public class demo {public static void main(String[] args) {Dog.hi();Dog.say();//报错}
}

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

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

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

2. main方法中的语法

深入理解main方法

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

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

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

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

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

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

例如:


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


 提示: 

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

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

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

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

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

3. 代码块

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

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

语法:

修饰符{

        代码

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

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

🐔 普通代码块

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

{

        代码

}

🐔 静态代码块

此时的修饰符就是static

static{

        代码

}

🐔 构造块

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

使用场景:

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

举例:🌰

public class Student{private String name;private String gender;private int age;private double score;private static String classRoom;//实例代码块{this.name = "marry";this.age = 12;this.gender = "man";System.out.println(name+" is instance init()!");} // 静态代码块static {classRoom = "303";System.out.println("I am static init()!");}// 构造代码块public Student(){System.out.println("I am Student init()!");}public static void main(String[] args) {Student s1 = new Student();Student s2 = new Student();}
}

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

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

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

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

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

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

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

public class demo {public static void main(String[] args) {new Son();}
}
class Father {public Father() {System.out.println("我是父类的构造代码块");}{System.out.println("我是父类的代码块");}static{System.out.println("我是父类静态代码块");}
}
class Son extends Father{public Son() {System.out.println("我是子类的构造代码块");}{System.out.println("我是子类的代码块");}static{System.out.println("我是子类静态代码块");}
}

可以发现如下顺序:

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

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. 外部类在方法中,可以创建内部类对象,然后调用方法即可;如:

public class Outer {public static void main(String[] args) {Outer outer = new Outer();outer.n1();}private int n1 = 100;private void m2() {System.out.println("2");}public void n1() {final class Inner {public void f1() {System.out.println("1");m2();}}Inner inner = new Inner();inner.f1();}
}

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

2. 匿名内部类

本质:

(1)本质仍是一个类

(2)是个内部类

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

(4)同时还是个对象

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

语法:

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

        类体

};

例如:

class Outer {public static void main(String[] args) {Outer outer = new Outer();outer.method();}private int m = 10;public void method() {//需求:使用一个IA接口,并创建一个对象//传统写法:写一个类,实现该接口IA tiger = new Tiger();tiger.cry();}
}
interface IA {public void cry();
}
class Tiger implements IA {@Overridepublic void cry() {System.out.println("嗷嗷嗷嗷");}
}

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


class Outer {public static void main(String[] args) {Outer outer = new Outer();outer.method();}private int m = 10;public void method() {//需求:使用一个IA接口,并创建一个对象//传统写法:写一个类,实现该接口//现在我们不要Tiger类IA tiger = new IA() {@Overridepublic void cry() {System.out.println("嗷嗷嗷嗷");}};tiger.cry();}
}
interface IA {public void cry();
}

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

class Outer {public static void main(String[] args) {Outer outer = new Outer();outer.method();}private int m = 10;public void method() {//需求:使用一个IA接口,并创建一个对象//传统写法:写一个类,实现该接口//现在我们不要Tiger类//使用匿名内部类来简化代码//此时的编译类型是:IA接口//此时的运行类型是:匿名内部类/*底层:class XXXXXXX(类名) implements IA {@Overridepublic void cry() {System.out.println("嗷嗷嗷嗷");}};*/IA tiger = new IA() {@Overridepublic void cry() {System.out.println("嗷嗷嗷嗷");}};tiger.cry();}
}
interface IA {public void cry();
}

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

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

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

匿名内部类的使用细节:

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

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

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

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

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

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

实例内部类

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

说明:

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

例:

public class Main {public static void main(String[] args) {Outer outer = new Outer();outer.T();}
}
class Outer {private int n1 = 10;class Inner {public void say() {System.out.println("Outer 中的 n1 = " + n1);}}public void T(){Inner inner = new Inner();inner.say();}
}

 只能通过这种方式访问。

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

静态内部类

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

public class Main {public static void main(String[] args) {// 静态内部类对象创建 & 成员访问OutClass.InnerClass innerClass = new OutClass.InnerClass();innerClass.methodInner();}
}class OutClass {private int a;static int b;public void methodA() {a = 10;System.out.println(a);}public static void methodB() {System.out.println(b);} // 静态内部类:被static修饰的成员内部类static class InnerClass {public void methodInner() {// 在内部类中只能访问外部类的静态成员// a = 100; // 编译失败,因为a不是类成员变量b = 200;// methodA(); // 编译失败,因为methodB()不是类成员方法methodB();}}
}

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

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

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...