static关键字
在Java中,static
关键字有多种用途,主要包括以下几个方面:
-
静态变量:定义类级别的变量,也称为类变量。静态变量在类被加载到JVM时创建,并且只有一个副本,无论创建多少个该类的实例。
所有对象共享同一个静态变量
。public class MyClass { public static int staticVar; // static修饰的静态变量 }
-
静态方法:静态方法属于类本身,而不是类的某个对象。
静态方法只能直接访问类的静态成员,不能访问非静态成员
。可以通过类名直接调用(推荐),也可以通过对象调用。常用于工具类的方法,如Math类中的Math.max()。public class MyClass { public static void staticMethod() { System.out.println("This is a static method."); } } MyClass.staticMethod() // 调用
-
静态块:
静态代码块在类被加载时执行
,且只执行一次。常用于初始化静态变量。这通常发生在以下情况之一: -
某个对象被创建时(即调用构造函数之前)。
-
某个静态方法或静态变量被调用或访问时。
-
使用Class.forName()等反射机制显式加载类时。
public class MyClass { static { // 静态块代码 System.out.println("Static block is executed."); } }
-
静态内部类:定义在另一个类内部的静态类。静态内部类不需要外部类的实例即可被访问。
public class OuterClass { public static class StaticInnerClass { // 静态内部类的代码 } }
-
静态导入:从某个类中导入静态成员到当前类中,这样在当前类中就可以直接使用这些静态成员而不必使用类名。
import static java.lang.Math.PI; import static java.lang.Math.sqrt; public class MyClass { public double calculateCircleArea(double radius) { return PI * sqrt(radius); } }
-
静态导包:类似于静态导入,但是针对整个包。
import static java.lang.*; public class MyClass { public void myMethod() { System.out.println("Hello, World!"); } }
使用static
关键字时需要注意,过度使用静态成员可能会导致代码难以维护和测试,因为静态成员的生命周期与应用程序的生命周期相同,且所有实例共享同一个静态变量,这可能会导致状态管理变得复杂。同时,静态方法无法访问实例变量和实例方法
,这限制了它们的灵活性。
static的注意事项
● 静态方法只能访问静态变量和静态方法
● 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
● 静态方法中是没有this关键字
工具类
构造代码块
构造代码块:
- 1.写在成员位置的代码块
- 2.作用:可以把多个构造方法中重复的代码抽取出来
- 3.执行机制:在
创建类对象的时候会先执行构造代码块再执行构造方法
public class Student {
private String name;
private int age;
{
System.out.println("开始创建对象");
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
构造代码块也可以在构造器里调用this()来代替
继承
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承
,来优化代码
- java中
不允许一个子类继承多个父类,但是可以多层继承
(C++就可以多继承,C#也支持单继承)。 - Java中所有的类都直接或者间接继承于Object类
- 子类只能访问父类中非私有的成员(用private修饰的就不能访问)
- 父类的构造方法不能被子类继承
- 子类的全部构造器的第一行默认都是
super()(写不写都有),它会先调用父类的无参构造器再执行自己的构造器
- 如果父类有无参构造方法,那么父类就
默认有无参构造器(就是有隐式的无参构造器)
,子类就也可以没有构造方法
并成功创建对象。 - 如果父类没有无参构造方法,
子类必须定义构造方法,并通过 super 调用父类的有参构造器
,才能成功创建对象。
使用公共的getter和setter方法访问父类的私有属性
在Java中,使用公共的getter和setter方法访问父类的私有属性是一种常见的做法,以保持封装性并允许对属性的访问和修改。这种方式允许父类控制其私有属性的访问,同时提供一种安全的方式来获取和设置这些属性的值。
以下是一个示例,展示如何在父类中定义私有属性,并提供公共的getter和setter方法,然后在子类中使用这些方法来访问和修改父类的私有属性:
class Parent {
private int privateVar;
// 公共的getter方法
public int getPrivateVar() {
return privateVar;
}
// 公共的setter方法
public void setPrivateVar(int privateVar) {
this.privateVar = privateVar;
}
}
class Child extends Parent {
public void accessParentProperty() {
// 使用getter方法访问父类的私有属性
int value = getPrivateVar();
System.out.println("Accessed parent's private variable: " + value);
// 使用setter方法修改父类的私有属性
setPrivateVar(100);
System.out.println("Modified parent's private variable to: 100");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.accessParentProperty();
}
}
在这个例子中:
-
父类(
Parent
)定义了一个私有属性privateVar
,并提供了公共的getter和setter方法来访问和修改这个属性。 -
子类(
Child
)继承了父类,并在其方法accessParentProperty
中使用父类的getter和setter方法来访问和修改父类的私有属性。 -
主类(
Main
)创建了子类的实例,并调用accessParentProperty
方法来演示如何访问和修改父类的私有属性。
这种方式的优点是:
- 封装性:父类可以控制其私有属性的访问,防止外部直接访问和修改。
- 灵活性:如果需要对属性的访问或修改添加额外的逻辑,可以在getter和setter方法中实现。
- 继承性:子类可以通过继承父类的getter和setter方法来访问和修改父类的私有属性,而不需要知道属性的具体实现。
这种方法是Java中处理私有属性访问的标准做法。
方法重写
应用场景:当父类中方法,不能满足子类现在的需求时,我们就需要把这个方法进行重写。
注意
- 子类中重写的方法上面需要加上@override
- 如果方法重写了,那么就会覆盖掉父类方法,下一个类继承的是从写的方法。
- 父类的静态方法不能被子类重写,
但是可以被子类里的同名静态方法隐覆盖
属性隐藏
class Animal {
String name = "动物";
}
class Dog extends Animal {
String name = "狗";
void showNames() {
System.out.println("Dog's name: " + name); // 输出"狗"
System.out.println("Animal's name: " + super.name); // 输出"动物"
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.showNames();
}
}
在上面这段代码中,Dog继承了Animal,两个类里面都定义了name属性,但是Dog类里面的定义name覆盖了继承父类的name,所以Dog类里面调用name是"狗"。
this和super关键字
- 在静态方法中,super关键字是不允许使用。
- super关键字不能用来调用父类中的静态方法和变量,如果要调用就 :父类名.方法&变量。
多态
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态
。
多态的具体代码体现
People p1= new Student();
p1.run();
People p2 = new Teacher();
p2.run();
调用成员变量:编译看左边,运行也看左边
调用成员方法:编译看左边,运行也看右边
编译看左边:javac编译代码时,会看左边的父类中有没有这个变量(或方法),如果有,编译有,如果没有编译失败。
public class Test {
public static void main(String[] args) {
//创建对象(多态方式)
Animal a = new Dog();
System.out.println(a.name); //动物
a.show(); // Dog --- show方法
}
}
class Animal {
String name = "动物";
public void show() {
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal {
String name = "狗";
@Override
public void show() {
System.out.println("Dog --- show方法");
}
}
多态的前提
- 有继承/实现关系;
- 存在父类引用子类对象;
- 存在方法重写。
多态的一个注意事项
多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。
使用多态的好处
-
在多态形式下,右边对象是解耦合的,更便于扩展和维护。
-
定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。
子类的弊端
多态下会产生的一个问题:多态下不能调用子类的特有功能
解决方法:强制类型转换
包和类
导包:用import
使用其他类的规则
- 使用同一个包中的类时,不需要导包。
- 使用java.lang(java的标准包)包中的类时,不需要导包。
- Java的标准包其他情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名。
final
跳转文章链接
权限修饰符
权限修饰符的使用规则实际开发中,一般只用private和public
- 成员变量私有
- 方法公开
- 特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。
抽象类
抽象方法:将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样所以,在父类中不能确定具体的方法体该方法就可以定义为抽象方法。抽象方法在子类中必须强制重写,除非子类也是抽象类
。
抽象类:有抽象方法的类一定要定义为抽象类
抽象类的注意事项
- 抽象类不能实例化。
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 可以有构造方法, 但不能创建对象。
- 抽象类的子类要么重写抽象类中的所有抽象方法,要么也是一个抽象类。
- 抽象类里可以有成员变量、方法、构造器
- abstract 关键字只能用于修饰类、方法和接口,而不能用于修饰成员变量
模板方法
模板方法是父类(或抽象类)中能继承给子类且子类不能重写的方法,一般用public final在父类(或抽象类)中修饰该方法。
从而解决方法中存在重复代码的问题。
接口
接口(Interface)是一种完全抽象的结构,它允许你定义方法,但是不实现它们。接口可以被类实现(implement),实现接口的类必须提供接口中所有方法的具体实现。
使用注意
- 接口不能创建对象。
- 接口是用来被类实现的,实现接口的类称为实现类。
- 一个类可以实现多个接口,实现类实现多个接口,
就必须重写全部接口的全部抽象方法
,否则实现类就要定义为抽象类。
接口中成员的特点
- 成员变量只能是常量默认修饰符:public static final
- 接口里的方法没有修饰符修饰只能是抽象方法,默认修饰符:public abstract
- 接口里可以有抽象方法、默认方法、静态方法
- 接口里的
静态方法不能被重写(Override),但是可以被子类里同名的静态方法隐藏覆盖
。
接口和类之间的关系
- 类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承
- 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系:继承关系,可以单继承,也可以多继承
接口里的默认方法(default void method())
- 默认方法可以在接口中提供具体的实现代码,而
其他类可以选择重写这个方法
,重写的话就要去掉default关键字。 - 类必须显式地重写该方法,以解决冲突。
- 修饰符前面可以省略public,但是default不能。
- 默认方法可以调用接口中的其他抽象方法或默认方法。
接口里的静态方法
- 接口中的静态方法不能被实现该接口的类重写,因为静态方法是与接口本身相关联的。
- 静态方法可以通过接口名直接调用,而无须实例化接口的实现类。
接口多态
interface Animal {
void makeSound(); // 抽象方法
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
// Animal接口不能实例化
// Animal animal = new Animal(); // 这将导致编译错误
// 实例化具体实现类
Animal myDog = new Dog(); // 创建Dog类的实例
Animal myCat = new Cat(); // 创建Cat类的实例
myDog.makeSound(); // 输出: Woof!
myCat.makeSound(); // 输出: Meow!
}
}
接口实例化:
Animal myDog = new Dog();
接口和抽象类使用上的区别
接口: 用于定义一组方法的外部约定,一个类可以实现多个接口,强调的是“行为”。
抽象类: 用于提供一个模板,可以有部分实现,强调“共性”与“结构”,是一个类的蓝图。
如果希望实现多个不相关类之间的共同功能,接口是最佳选择;如果希望共享部分实现和状态,使用抽象类更为合适。
内部类
内部类表示的事物是外部类的一部分(单独出现没有任何意义)
内部类的访问特点
- 内部类
可以直接访问外部类的成员,包括私有外部类
- 访问内部类的成员,必须创建对象
成员内部类
成员内部类定义在一个类的内部。成员内部类具有一些独特的特性,使其在某些场景中非常有用。以下是成员内部类的一些关键点:
特性
-
与外部类的关系:
- 成员内部类是外部类的一部分,可以直接访问外部类的所有成员(包括私有成员)。
-
实例化:
- 要创建成员内部类的实例,必须
首先创建外部类的实例
。语法如下:OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass();
- 要创建成员内部类的实例,必须
-
作用域:
- 成员内部类可以访问外部类的所有成员,包括静态和非静态成员。
-
多重实例:
- 每个成员内部类的实例都与其外部类的实例关联,因此可以创建多个成员内部类的实例,且每个实例都与不同的外部类实例关联。
使用场景
成员内部类通常在以下情境中使用:
- 当需要访问外部类的私有数据或方法时,使用内部类可以简化代码的结构。
- 当内部类的功能与外部类高度相关联时,将其作为内部类可以提高代码的封装性和可读性。
示例代码
以下是一个简单的成员内部类示例:
public class OuterClass {
private String outerField = "Hello from OuterClass";
public class InnerClass {
public void display() {
// 访问外部类的私有成员
System.out.println(outerField);
}
}
public void createInnerClass() {
InnerClass inner = new InnerClass();
inner.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.createInnerClass();
}
}
在这个例子中,InnerClass
是 OuterClass
的成员内部类。InnerClass
能够直接访问 OuterClass
的私有字段 outerField
,并在 display
方法中打印出来。
public class Outer {
private int a = 10;
class Inner {
private int a = 20;
public void show() {
int a = 30;
// 调用外部类的属性
System.out.println(Outer.this.a); // 10
System.out.println(this.a); // 20
System.out.println(a); // 30
}
}
}
静态内部类
在外部类中,可以定义一个静态内部类。它是外部类的一个静态成员,使用 static 关键字来定义。
静态内部类适用于以下场景:
- 当静态内部类不依赖于外部类的实例时,使用静态内部类能使代码更加清晰。
- 当需要组织类的逻辑,但不需要外部类的状态时,静态内部类是一个不错的选择。
静态内部类只能访问外部类的静态变量和静态方法,如果要访问外部的非静态成员就要创建外部类的对象。
// 创建静态内部类的对象
// 只要是静态的东西,都可以用类名直接获取
Outer.Inner oi = new Outer.Inner();
oi.show1();
局部内部类
- 将内部类定义在方法里面就叫局部内部类,类似于方法里面的局部变量。
- 外界是无法直接使用,需要在方法内部创建对象并使用。
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
匿名内部类
匿名内部类: 隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
匿名内部类的格式
new 类名或接口名() {
重写方法;
};
格式的细节
包含了继承或实现、方法重写、创建对象。
整体就是一个类的子类对象或接口的实现类对象。
使用场景
当方法的参数是接口或类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码。
具体实现
interface Greeting {
void greet();
}
public class Main {
public static void main(String[] args) {
// 接口多态,实例化具体实现类
Greeting greeting = new Greeting() {
@Override
public void greet() {
System.out.println("Hello from an anonymous inner class!");
}
};
greeting.greet();
}
}