1.static关键字
当在定义类的时候,类中都会有相应的属性和行为。而属性和行为都是通过创建本类对象调用的。当在调用对象的某个行为时,这个行为没有访问到对象的特有数据时,调用方法而创建这个对象有些多余。可是不创建对象,行为又调用不了,这时就会想,那么我们能不能不创建对象,就可以调用行为呢?
class Person
{
private int age;
private String name;
Person(int age ,String name)
{
this.age = age;
this.name = name;
}
//说话的行为,说出自己的年龄和姓名
void speak()
{
System.out.println("name="+this.name+",age="+this.age);
}
//睡觉行为
void sleep()
{
System.out.println("睡觉ZZZzzz....");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person(23,"张三");
p.speak();
p.sleep();
}
}
2.静态方法的使用注意事项
静态由来
如果创建对象调用方法,发现这个方法中没有使用到对象中的特有数据,那么创建该对象仅仅是为了调用方法,就显得这个对象创建很多余,这时可以使用static关键字修饰这个方法。
若一个方法被static关键字修饰,则该方法属于类的方法,可以通过类名的方式直接调用。
class Person
{
private int age;
private String name;
Person(int age ,String name)
{
this.age = age;
this.name = name;
}
//说话的行为,说出自己的年龄和姓名
void speak()
{
System.out.println("name="+this.name+",age="+this.age);
}
//睡觉行为,由于sleep方法没有访问对象的特有数据,可以使用静态修饰
static void sleep()
{
System.out.println("睡觉ZZZzzz....");
}
}
class PersonDemo
{
public static void main(String[] args)
{
//sleep方法是静态方法,属于类的方法,可以使用类名直接调用
Person.sleep();
}
}
什么时候使用静态修饰方法呢?
定义功能时,如果功能不需要访问类中定义的成员变量(非静态)时,该功能就需要静态修饰
静态方法的使用注意事项
通过上面的演示,发现被静态修饰的方法中无法访问非静态的属性和方法。这是为什么呢?
- 静态是随着类的加载就加载了。也是随着类的消失而消失了。
- 静态优先于对象存在,被对象共享。
- 因为静态先存在于内存中无法访问后来的对象的中的数据,所以静态无法访问非静态。而且内部无法书写this。因为这时对象有可能不存在,this没有任何指向。
注意事项
1.静态方法不能访问非静态的成员。但是非静态可以访问静态成员的。
说明:静态的弊端在于访问出现局限性。好处是可以直接被类名调用。
2. 静态方法中不允许出现this,super关键字。
main方法其实也静态的。因为main是程序的入口,是提供给JVM使用的,当在dos中输入java XXX 时,会启动JVM,同时JVM会加载以XXX为名称的这个class文件进内存。并扫描其中有没有main方法。若有main方法JVM就会去调用这个main方法。JVM调用main方法,是不会创建对象的,没有对象怎么调用方法,这个方法只能被静态修饰。JVM通过类名调用的。
既然被静态修饰的方法中无法访问非静态的成员属性和行为,当要静态方法的确需要访问某个成员属性怎么办呢?
3.静态变量
静态不仅可以修饰方法,同时静态也可以修饰成员变量。
class Circle
{
//圆的半径
private double radius;
//圆周率,由于圆周率是固定值,因此所有对象共享这个数据
//没有必要每个对象中拥有这个数据,因此可以使用静态修饰
static double pi = 3.14;
//带参数的构造函数
Circle(double radius)
{
this.radius = radius;
}
//获取圆面积
double getArea()
{
return radius * radius * pi;
}
}
class CircleDemo
{
public static void main(String[] args)
{
System.out.println(new Circle(3).getArea());
}
}
如果pi这个变量没有被静态修饰的话,当创建Circle对象时,每个对象中都会有pi这个变量,但是pi是个固定不变的值,没有必要每个对象中拥有,这时可以将这个变量静态修饰,让所有对象共享就可以了。
4.静态变量和成员变量的区别
变量所属不同
● 静态变量所属与类,也称为类变量
● 成员变量所属于对象,也称为实例变量(实例变量)
内存中的位置
● 静态变量存储于方法区中的静态区中
● 成员变量存储于堆内存中
在内存中出现的时间
● 静态变量随着类的加载而加载,随着类的消失而消失
● 成员变量随着对象的创建而在堆内存中出现,随着对象的消失而消失
调用方式
● 静态变量可以用类直接调用,也可对象调用
● 成员变量只能被对象调用
5.静态加载的内存图解
public class Person
{
//人的姓名
private String name;
//所有对象都具有国籍属性,并且都会CN,这时可以将这个成员变量定义成静态
的
private static String country = "CN";
Person(String name)
{
this.name = name;
}
void showName()
{
System.out.println("name="+name);
}
//静态方法
static void showCountry()
{
System.out.println("Country="+country);
}
}
class StaticDemo
{
public static void main(String[] args)
{
Person p = new Person("欧鹏");
p.showName();
//showCountry是静态方法,类名直接调用
Person.showCountry();
}
}
6.代码块
静态代码块
静态代码块,其实就在代码块前面加上了静态关键字,这个代码块就称为静态代码块。
优先于主方法执行,优先于构造代码块执行,不管创建多少对象,静态代码块只执行一次,可用于给静态变量赋值; 用来给类进行初始化.
public class Person {
private String name;
private int age;
static{
System.out.println("静态代码块执行了");
}
}
静态代码块的应用场景:类不需要创建对象,但需要初始化,这时可以将部分代码存储到静态代码块中.
构造代码块
直接写在类中的代码块:优先于构造方法执行,构造代码块用于给所有对象初始化用,每创建一次对象就会执行一次构造代码块。构造函数用于给指定对象初始化。
public class Person {
private String name;
private int age;
static{
System.out.println("静态代码块执行了");
}
{
System.out.println("构造代码块执行了");
}
Person(){
System.out.println("Person无参数的构造函数执行");
}
Person(int age){
this.age = age;
System.out.println("Person(age)参数的构造函数执行");
}
}
class PersonDemo{
public static void main(String[] args)
{
Person p = new Person();
Person p1 = new Person(23);
}
}
代码块里变量的作用域:只在自己所在区域(前后的{})内有效
局部代码块
写在局部范围内的代码块。作用:就可以控制局部变量的生命周期。
class Demo
{
public static void main(String[] args)
{
{//局部代码块
int x = 5;
System.out.println(" 局部代码块..."+x);
}
System.out.println(" over...");
}
}
7.对象的创建过程
class Demo
{
static int x = 1; //静态成员变量
int y = 1; //非静态成员变量
static //静态代码块
{
System.out.println("static code...x="+x);
}
{//构造代码块
System.out.println("cons code ...y="+y);
}
Demo() //构造函数
{
System.out.println("cons function ...y="+y);
}
}
class CreateObjectTest
{
public static void main(String[] args)
{
Demo d = new Demo(); //创建Demo对象
}
}
对象加载的流程总结:
- 加载Demo.class文件进方法区,并进行空间分配。
- 如果有静态变量,先默认初始化,显示初始化。
- 如果有静态代码块,要执行,仅一次。
- 通过new在堆内存中开辟空间,并明确首地址。
- 对对象中的属性进行默认初始化。
- 调用对应的构造函数进行初始化。
- 构造函数内部。
7.1 调用父类构造函数super();
7.2 成员变量的显示初始化。
7.3 构造代码块初始化。
7.4 构造函数内容自定义内容初始化。 - 对象初始化完毕后,将地址赋值给d引用变量。
8.单例模式
设计模式最早来源于建筑领域,是一套被反复使用、多数人知晓的、经过分类、设计经验的总结。使用设计模式是为了可重用、更容易被他人理解、保证可靠性。
设计模式是解决某一种问题的一种思想,是一种行之有效的解决方式。Java中共有23中设计模式。后期会陆陆续续学习这些设计模式。
学习设计模式就是要理解这个模式主要解决什么问题?理解清楚了解决问题,再学习对应的设计模式就简单了。
单例解决的问题:保证一个类的对象在内存中的唯一性,即一个类在内存中的对象有且只有一个。
应用场景:多个程序都在操作同一个配置文件时,需要程序A操作后的结果,程序B要知道,并继续基于程序A操作后的结果进行操作。前提,数据都存储在配置文件对象中,要求程序A和程序B操作的配置文件对象是同一个对象
思路
● 针对上述的应用场景,怎么保证一个类在内存只有一个对象呢?
● 若要保证一个类在内存中对象只有一个,就要限制其他程序不能随便创建这个类的对象。
● 那如何限制其他程序不能创建这个类对象呢?
● 创建对象是要调用构造函数的,那么只要将这个类的构造函数私有了,不让其他程序访问,其他程序就无法创建对象了。但当私有构造函数后,其他程序无法创建对象。
● 但其他程序还要使用这个类对象,那如何获取对像呢?
● 可以在本类中创建自己的对象,对外提供获取本类对象的方法即可。
饿汉模式
public class Single {
//私有本类中的构造函数
private Single(){}
//创建本类对象
private static Single s = new Single();
//对外提供获取本来对象方法
public static Single getInstance(){
return s;
}
}
注意问题:
由于外界无法创建Single对象,没有对象,那就无法调用getInstance方法,这时需要将getInstance方法静态化,这样外界就可以通过类名直接调用该方法。
懒汉模式
public class Single
{
//私有构造函数
private Single(){}
//在本类中创建本类对象
private static Single instance = null;
//对外提供静态访问方法,获取本类实例对象
public static Single getInstance(){
if(instance == null ) // 这里会有线程安全问题
{
instance = new Single();
}
return instance;
}
}
class SingleDemo
{
public static void main(String[] args)
{
//获取Single类的实例对象s
Single s = Single.getInstance();
//获取Single类的实例对象s2
Single s2 = Single.getInstance();
System.out.println(s==s2); //true
}
}
编程练习题
1
2