类变量和类方法
类变量(静态变量)
问题:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。
传统的解决方法:
类变量快速入门
package com.static_;
public class ChildGame {
public static void main(String[] args) {
//定义一个变量 count, 统计有多少小孩加入了游戏
int count = 0;
Child child1 = new Child("白骨精");
child1.join();
//count++;
child1.count++;
Child child2 = new Child("狐狸精");
child2.join();
//count++;
child2.count++;
Child child3 = new Child("老鼠精");
child3.join();
//count++;
child3.count++;
//===========
//类变量,可以通过类名来访问
System.out.println("共有" + Child.count + " 小孩加入了游戏...");
//下面的代码输出什么
System.out.println("child1.count=" + child1.count);//3
System.out.println("child2.count=" + child2.count);//3
System.out.println("child3.count=" + child3.count);//3
}
}
class Child{ //类
private String name;
//定义一个变量 count ,是一个类变量(静态变量) static修饰-》 静态
//该变量最大的特点就是会被Child类的所有的对象实例共享
public static int count = 0;
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + " 加入了游戏..");
}
}
类变量的内存布局
静态变量放在哪里?
说法不一,但是不管static变量在哪里,共识:(1)static变量是同一个类所有对象共享(2)static类变量,在类加载的时候就生成了
什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
如何定义类变量
如何访问类变量
package com.hspedu.static_;
public class VisitStatic {
public static void main(String[] args) {
//类名.类变量名
//说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
System.out.println(A.name);
A a = new A();
//通过对象名.类变量名
System.out.println("a.name=" + a.name);
}
}
class A {
//类变量
//类变量的访问,必须遵守相关的访问权限.
public static String name = "education";
//普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量
private int num = 10;
}
类变量使用注意事项和细节讨论
类方法(静态方法)
类方法快速入门
类方法的调用:
- 当方法使用了static修饰后,该方法就是静态方法
- 静态方法就可以访问静态属性/变量
package com.static_;
public class StaticMethod {
public static void main(String[] args) {
//创建2个学生对象,叫学费
Stu tom = new Stu("tom");
//tom.payFee(100);
Stu.payFee(100);//对不对?对
Stu mary = new Stu("mary");
//mary.payFee(200);
Stu.payFee(200);//对
//输出当前收到的总学费
Stu.showFee();//300
}
}
class Stu{
private String name;普通成员
//定义一个静态变量,来累积学生的学费
private static double fee = 0;
public Stu(String name) {
this.name = name;
}
//说明
//1. 当方法使用了static修饰后,该方法就是静态方法
//2. 静态方法就可以访问静态属性/变量
public static void payFee(double fee){
Stu.fee += fee;//累计
}
public static void showFee(){
System.out.println("总学费有:" + Stu.fee);
}
}
类方法经典使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
在实际开发中(开发自己的工具时),往往会将一些通用的方法,设计成静态方法,这样我们就不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务。
类方法使用注意事项和细节讨论
package com.hspedu.static_;
public class StaticMethodDetail {
public static void main(String[] args) {
D.hi();//ok
//非静态方法,不能通过类名调用
//D.say();, 错误,需要先创建对象,再调用
new D().say();//可以
}
}
class D {
private int n1 = 100;
private static int n2 = 200;
public void say() {//非静态方法,普通方法
}
public static void hi() {//静态方法,类方法
//类方法中不允许使用和对象有关的关键字,
//比如this和super。普通方法(成员方法)可以。
//System.out.println(this.n1);//错误
}
//类方法(静态方法)中 只能访问 静态变量 或静态方法
//口诀:静态方法只能访问静态成员.
public static void hello() {
System.out.println(n2);
System.out.println(D.n2);
//System.out.println(this.n2);不能使用
hi();//OK
//say();//错误
}
//普通成员方法,既可以访问 非静态成员,也可以访问静态成员
//小结: 非静态方法可以访问 静态成员和非静态成员
public void ok() {
//非静态成员
System.out.println(n1);
say();
//静态成员
System.out.println(n2);
hello();
}
}
课堂练习
练习1:
(count先输出后++)
练习2:
练习3:
main方法语法
main方法特别提示
package com.hspedu.main_;
public class Main01 {
//静态的变量/属性
private static String name = "韩顺平教育";
//非静态的变量/属性
private int n1 = 10000;
//静态方法
public static void hi() {
System.out.println("Main01的 hi方法");
}
//非静态方法
public void cry() {
System.out.println("Main01的 cry方法");
}
public static void main(String[] args) {
//可以直接使用 name
//1. 静态方法main 可以访问本类的静态成员
System.out.println("name=" + name);
hi();
//2. 静态方法main 不可以访问本类的非静态成员
//System.out.println("n1=" + n1);//错误
//cry();
//3. 静态方法main 要访问本类的非静态成员,需要先创建对象 , 再调用即可
Main01 main01 = new Main01();
System.out.println(main01.n1);//ok
main01.cry();
}
}
main方法(idea传递参数)
如何在idea传递参数?
代码块
代码块基本介绍
基本介绍:
代码化块又称初始化块,属于类中的成员(即是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
代码块的好处
package com.static_.CodeBlock01;
public class codeBlock {
public static void main(String[] args) {
Movie movie = new Movie("hello");
System.out.println("===============");
Movie movie2 = new Movie("fine", 100, "成龙");
}
}
class Movie {
private String name;
private double price;
private String director;
//3个构造器-》重载
//(1) 下面的三个构造器都有相同的语句
//(2) 这样代码看起来比较冗余
//(3) 这时我们可以把相同的语句,放入到一个代码块中即可
//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//(5) 代码块调用的顺序优先于构造器
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正式开始...");
};
public Movie(String name) {
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}
代码块使用注意事项和细节讨论
1)
package com.static_.CodeBlock01;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//static代码块,是在类加载时,执行的,而且只会执行一次.
DD dd = new DD();
DD dd1 = new DD();
//普通的代码块,在创建对象实例时,会被隐式的调用。
// 被创建一次,就会调用一次。
// 如果只是使用类的静态成员时,普通代码块并不会执行
System.out.println(DD.n1);//8888, 静态模块块一定会执行
}
}
class DD {
public static int n1 = 8888;//静态属性
static {//静态代码块
System.out.println("DD 的静态代码1被执行...");//
}
//普通代码块, 在new对象时,被调用,而且是每创建一个对象,就调用一次
//可理解为普通代码块是构造器的补充
{
System.out.println("DD 的普通代码块...");
}
}
2)① 创建对象实例时(new)
package com.static_.CodeBlock01;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//2)① 创建对象实例时(new)
AA aa = new AA();
}
}
class AA {
static {//静态代码块
System.out.println("AA的静态代码块1被执行...");
}
}
2)②创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
package com.static_.CodeBlock01;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//2)②创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
AA aa2 = new AA();
}
}
class BB {
//静态代码块
static {
System.out.println("BB 的静态代码1被执行...");//1
}
}
class AA extends BB{
//静态代码块
static {
System.out.println("AA的静态代码块1被执行...");
}
}
2)③使用类的静态成员时(静态属性,静态方法)
package com.static_.CodeBlock01;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//2)③使用类的静态成员时(静态属性,静态方法)
System.out.println(Cat.n1);
}
}
class Cat{
public static int n1 = 999;//静态属性
static { //静态代码块
System.out.println("Cat 的静态代码1被执行...");//
}
}
例:创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载(静态代码块也一样)
package com.static_.CodeBlock01;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
AA aa2 = new AA();
System.out.println(Cat.n1);
}
}
class Animal {
//静态代码块
static {
System.out.println("Animal的静态代码1被执行...");
}
}
class Cat extends Animal{
public static int n1 = 999;//静态属性
static { //静态代码块
System.out.println("Cat的静态代码1被执行...");
}
}
静态优先,同级看顺序,最后构造器。
package com.static_.CodeBlock01;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();
}
}
class A {
{ //普通代码块
System.out.println("A 普通代码块01");
}
private int n2 = getN2();//普通属性的初始化
static {//静态代码块
System.out.println("A静态代码块01");
}
private static int n1 = getN1();//静态属性的初始化
public static int getN1() {
System.out.println("getN1被调用...");
return 100;
}
public int getN2() { //普通方法/非静态方法
System.out.println("getN2被调用...");
return 200;
}
//无参构造器
public A() {
System.out.println("A() 构造器被调用");
}
}
package com.static_.CodeBlock01;
public class CodeBlockDetail03 {
public static void main(String[] args) {
new BBB();
// (1)AAA的普通代码块
// (2)AAA() 构造器被调用
// (3)BBB的普通代码块
// (4)BBB() 构造器被调用
}
}
class AAA { //父类是Object
{
System.out.println("AAA的普通代码块");
}
public AAA() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("AAA() 构造器被调用....");
}
}
class BBB extends AAA {
{
System.out.println("BBB的普通代码块...");
}
public BBB() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("BBB() 构造器被调 用....");
}
}
package com.static_.CodeBlock01;
public class CodeBlockDetail04 {
public static void main(String[] args) {
//(1) 首先进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
new B02();//对象
}
}
class A02 {//父类
private static int n1 = getVal01();
static {
System.out.println("A02的一个静态代码块..");//(2)
}
{
System.out.println("A02的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器
//隐藏
//super()
//普通代码和普通属性的初始化...
System.out.println("A02的构造器");//(7)
}
}
class B02 extends A02 { //B02子类
private static int n3 = getVal03();//静态变量
static {//静态代码块
System.out.println("B02的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化
System.out.println("B02的构造器");//(10)
}
}
package com.static_.CodeBlock01;
public class CodeBlockDetail04 {
public static void main(String[] args) {
new C02();
}
}
class C02 {
private int n1 = 100;
private static int n2 = 200;
private void m1() {
}
private static void m2() {
}
static {
//静态代码块,只能调用静态成员(变量和方法)
//System.out.println(n1);错误
System.out.println(n2);//ok
//m1();//错误
m2();
}
{
//普通代码块,可以使用任意成员
System.out.println(n1);
System.out.println(n2);//ok
m1();
m2();
}
}
题 1:下面的代码输出什么?
package com.static_.CodeBlock01;
public class CodeBlockExercise01 {
}
class Person {
public static int total;//静态变量
static {//静态代码块
total = 100;
System.out.println("in static block!");//(1)
}
}
class Test {
public static void main(String[] args) {
System.out.println("total = "+ Person.total);
//使用类的静态变量,类被加载,执行,且只会执行一次//100
System.out.println("total = "+ Person.total);
//100
}
}
问题2:
package com.static_.CodeBlock01;
public class CodeBlockExercise02 {
}
class Sample
{
Sample(String s)
{
System.out.println(s);
}
Sample()
{
System.out.println("Sample默认构造函数被调用");
}
}
class Test02{
Sample sam1=new Sample("sam1成员初始化");//
static Sample sam=new Sample("静态成员sam初始化 ");//
static{
System.out.println("static块执行");//
if(sam==null)System.out.println("sam is null");
}
Test02()//构造器
{
System.out.println("Test默认构造函数被调用");//
}
//主方法
public static void main(String str[])
{
Test02 a = new Test02();//无参构造器
}
}
1、静态成员sam初始化
2、static块执行
3、sam1成员初始化
4、static块执行
单例设计模式
什么是单例模式
单例模式应用实例
instance:实例
单例饿汉式
步骤[单例模式-饿汉式]
1. 将构造器私有化
2. 在类的内部直接创建对象(该对象是static)
3. 提供一个公共的static方法,返回 gf对象
package com.hspedu.single_;
public class SingleTon01 {
public static void main(String[] args) {
// GirlFriend xh = new GirlFriend("小红");
// GirlFriend xb = new GirlFriend("小白");
//通过方法可以获取对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
//System.out.println(GirlFriend.n1);//调用静态属性时会造成类的加载,
//所以虽然没有调用对象但是gf会自动创建,但是沒有使用
//...
}
}
//有一个类, GirlFriend
//只能有一个女朋友
class GirlFriend {
private String name;
//public static int n1 = 100;
//为了能够在静态方法中,返回 gf对象,需要将其修饰为static
//對象,通常是重量級的對象,饿汉式可能造成创建了对象,但是沒有使用.
private static GirlFriend gf = new GirlFriend("小红红");
//如何保障我们只能创建一个 GirlFriend 对象
//步骤[单例模式-饿汉式]
//1. 将构造器私有化
//2. 在类的内部直接创建对象(该对象是static)
//3. 提供一个公共的static方法,返回 gf对象
private GirlFriend(String name) {
System.out.println("構造器被調用.");
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
单例懒汉式
package com.static_.single_;
/**
* 演示懒汉式的单例模式
*/
public class SingleTon02 {
public static void main(String[] args) {
//new Cat("大黃");
//System.out.println(Cat.n1);
Cat instance = Cat.getInstance();
System.out.println(instance);
//再次調用getInstance
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
//希望在程序運行過程中,只能創建一個Cat對象
//使用單例模式
class Cat {
private String name;
public static int n1 = 999;
private static Cat cat ; //默認是null
//步驟
//1.仍然構造器私有化
//2.定義一個static靜態屬性對象
//3.提供一個public的static方法,可以返回一個Cat對象
//4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象
// 從而保證了單例
private Cat(String name) {
System.out.println("構造器調用...");
this.name = name;
}
public static Cat getInstance() {
if(cat == null) {//如果還沒有創建cat對象
cat = new Cat("小可愛");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
final 关键字
final基本介绍
package com.static_.final_;
public class Final01 {
public static void main(String[] args) {
E e = new E();
//e.TAX_RATE = 0.09;
}
}
//要求A类不能被其他类继承
//可以用final修饰A类
final class A{}
//class B extends A{}
class C{
//要求hi方法不能被子类重写
//可以用final修饰hi方法
public final void hi(){}
}
class D extends C{
// @Override
// public void hi() {
// System.out.println("重写了C类的hi方法...");
// }
}
//当不希望类的某个属性的值被修改,可以用final修饰
class E{
public final double TAX_RATE = 0.08;
}
//当不希望某个局部变量被修改,可以使用final修饰
class F {
public void cry() {
//这时,NUM 也称为 局部常量
final double NUM = 0.01;
//NUM = 0.9;
System.out.println("NUM=" + NUM);
}
}
final 使用注意事项和细节讨论
final 应用实例
package com.static_.final_;
public class FinalExercise01 {
public static void main(String[] args) {
Circle circle = new Circle(5.0);
System.out.println("面积=" + circle.calArea());
}
}
class Circle {
private double radius;
private final double PI;// = 3.14;
//构造器
public Circle(double radius) {
this.radius = radius;
//PI = 3.14;
}
{
PI = 3.14;
}
public double calArea() {
return PI * radius * radius;
}
}