在java类中,可用static修饰属性 ,方法,代码块,内部类。
被修饰后的成员具备以下特点:
类方法,类中使用static修饰的方法
案例:
public class Chinese {
private String name;
private int age;
public static String nation = "大中国";
public void f1() {
System.out.println("f1");
}
public static void f2() {
System.out.println("f2");
}
//自我介绍
public static void show() {
//在静态方法中,不能使用this,super
//静态方法中,只能使用静态变量
System.out.println("自我介绍" + nation);
f2();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试
package k_static;
public class Test {
public static void main(String[] args) {
Chinese malong = new Chinese();
malong.setName("马龙");
malong.setAge(32);
malong.nation = "中华人民共和国";
Chinese yaoming = new Chinese();
yaoming.setAge(34);
yaoming.setName("姚明");
yaoming.nation = "中国";
System.out.println(malong.getName());
System.out.println(malong.nation);
System.out.println(Chinese.nation);
Chinese.show();
}
}
可以看到被static修饰的nation被修改后即使我们要输出马龙这个对象的nation也是最终修改成的中国。
需要注意的是:静态变量是与类本身关联的,因此它的值在整个类中是共享的。这意味着当一个静态变量的值被修改后,它对所有的实例对象和其他使用该静态变量的地方都会生效。这也是静态变量常用于表示全局或共享状态的原因之一
public class Singleton1 {
private Singleton1() {
System.out.println("Singleton1 实例化了");
}
private static Singleton1 instance = new Singleton1(); //因为静态的特性只有一份存在方法区,所以这个对象一定只有一个
public static Singleton1 getInstance() {
return instance;
}
}
存在线程安全问题,没有静态初始化
在常见的懒汉式实现中,如果多个线程同时调用获取实例的方法,并且实例还未被创建,则可能导致多个线程同时创建实例,从而违反了单例模式的要求。
package l_singleton;
public class Singleton2 {
private Singleton2() {
System.out.println("懒汉式单例模式实例化。。。。");
}
private static Singleton2 instance = null;
public static Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
在上述代码中,如果多个线程同时通过getInstance()方法访问单例实例,当instance为null时,多个线程可能会并发地创建实例,导致返回多个不同的实例。
为了解决懒汉式的线程安全问题,可以使用加锁机制来保证在多线程环境下只创建一个实例。例如,可以使用synchronized关键字对获取实例的代码块进行同步,确保只有一个线程能够进入临界区创建实例。。以下是使用双重检查锁定(double check)实现的线程安全懒汉式示例:
public class Singleton {
private static volatile Singleton instance; // 使用volatile关键字修饰,禁止指令重排序
private Singleton() {
// 私有构造方法
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在上述代码中,通过双重检查锁定(Double-Checked Locking)的方式,在synchronized代码块内部再次检查实例是否已经被创建。这样可以保证只会在实例未被创建时才进行同步创建实例。
需要注意的是,在Java 5之后,使用volatile关键字修饰instance变量,可以禁止指令重排序,从而确保多线程环境下的正确性。
总的来说懒汉式在多线程环境下可能存在线程安全问题,可以通过加锁机制解决
对象初始化顺序
public class Parent {
int i = 0; //初始化
{
System.out.println("Parent初始化代码块1");
}
{
System.out.println("Parent初始化代码块2");
}
static int i2 = 0; //静态初始化
static {
System.out.println("Parent静态初始化代码块1");
}
static {
System.out.println("Parent静态初始化代码块2");
}
public Parent() {
System.out.println("Parent的构造函数。。。。");
}
}
public class Children extends Parent {
int i = 0; //初始化
{
System.out.println("Children初始化代码块1");
}
{
System.out.println("Children初始化代码块2");
}
static int i2 = 0; //静态初始化
static {
System.out.println("Children静态初始化代码块1");
}
static {
System.out.println("Children静态初始化代码块2");
}
public Children() {
System.out.println("Children的构造函数。。。。");
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// Class clazz = (Class)Class.forName("m_order.Children");
// Children bean = (Children) clazz.newInstance(); //反射产生对象
Children c1 = new Children();
}
结果是:
Parent静态初始化代码块1
Parent静态初始化代码块2
Children静态初始化代码块1
Children静态初始化代码块2
Parent初始化代码块1
Parent初始化代码块2
Parent的构造函数。。。。
Children初始化代码块1
Children初始化代码块2
Children的构造函数。。。。
可以看到在类加载之后就开始初始化静态代码块,然后先执行初始化父类的代码块,父类的构造函数,然后才是子类
这里可以说的是上面的代码中
使用Class.forName(“className”)方法可以根据类名获取对应的Class对象。也就是加载class 不创建对象
调用Class.newInstance()方法可以创建该类的一个实例对象。
public abstract class A {
public abstract void f1();
public void f2() {
System.out.println("f2");
}
}
public class B extends A {
@Override
public void f1() {
System.out.println("f1()");
}
//alt+enter:解决一切问题
}
public class Test {
public static void main(String[] args) {
// new A();
new B();
}
}
public interface Usb {
//接口的方法默认public abstract
public abstract void start();
void end();
//接口中不能有普通方法
// void f2() {
// }
}
public interface TypeC {
void transfer();
}
public class Printer implements Usb, TypeC {
@Override
public void start() {
System.out.println("usb 启动");
}
@Override
public void end() {
System.out.println("usb 关闭");
}
public void transfer() {
System.out.println("typeC接口传输数据");
}
}
小节:接口和抽象的区别
public abstract
ps:但在java引入新特性中
接口中使用 default关键字也可以写入具体方法
java中可以把一个类放到另一个类的内部,这个类成为内部类
内部类的分类:
语法
public class Person {
private String name;
private int age;
//内部类,如果一个类只给当前外部类使用,别的组件不用,比如Dog和Cat类只是在Person中使用
//所有的成员变量的修饰符,都可以修饰内部类
public static class Dog {
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Cat {
public void eat() {
System.out.println("猫吃鱼");
}
}
public void f1() {
class Bird {
public void eat() {
System.out.println("鸟吃虫子");
}
}
Bird b = new Bird();
b.eat();
}
}
测试
public class Test {
public static void main(String[] args) {
//创建静态成员内部类的方法
Person.Dog dog = new Person.Dog();
dog.eat();
//创建非静态成员内部类的方法
Person p = new Person();
Person.Cat cat = p.new Cat();
cat.eat();
p.f1();
}
}
public interface Usb {
//接口的方法默认public abstract
public abstract void start();
void end();
}
public interface TypeC {
void transfer();
}
测试代码
package s_anonymous;
public class Test {
public static void f1(Usb usb) {
usb.start();
usb.end();
}
public static void f2(TypeC typeC) {
typeC.transfer();
}
public static void main(String[] args) {
// //内部类
// class Test$1 implements Usb {
// @Override
// public void start() {
// System.out.println("打印机usb启动");
// }
//
// @Override
// public void end() {
// System.out.println("打印机usb停止");
// }
// }
// Test$1 usb1 = new Test$1();
//方式1
/**
* 1:多态性:编译类型是父类
* 2:new 接口(类),后面跟一个实现
* 3:有一个对象usb1,实现了Usb接口的一个没有名字的类的实例
*
* Test$1->实现了Usb接口->用这个类产生一个对象usb1
*/
Usb usb1 = new Usb() {
@Override
public void start() {
System.out.println("打印机usb启动");
}
@Override
public void end() {
System.out.println("打印机usb停止");
}
};
f1(usb1);
//方式2:匿名内部类直接作为方法参数
f2(new TypeC() {
@Override
public void transfer() {
System.out.println("苹果14 正在传输数据....");
}
});
}
}
其中在测试代码中的方法f1(Usb usb)和f2(TypeC typeC)传入的参数如果是他们的子接口,也是允许传入参数的,这里是有多态性的体现的。