一、类变量和类方法
1.1类变量的基本概念
用我们已经学习的知识解决这个问题
package com.static_;
public class childGame {
public static void main(String[] args) {
int count=0;
Child child1 = new Child("白骨精");
child1.join();
count++;
Child child2 = new Child("狐狸精");
child2.join();
count++;
Child child3 = new Child("老鼠精");
child3.join();
count++;
System.out.println(" 共有 "+ count + " 小孩加入了游戏...");
}
}
class Child{
private String name;
public Child(String name){
this.name = name;
}
public void join(){
System.out.println(name+" 加入了游戏 ");
}
}
问题分析:
1.count是一个独立于对象(count并不属于child类)
2.右后我们访问count很麻烦,没有运用oop(面相对象)
思考:
如果设计的时候count被所有对象(child1,child2...)所共享就好了
因此java引入了static变量,static变量是同一个类所有对象共享,static类变量在类加载的时候就生成了。
package com.static_;
public class childGame {
public static void main(String[] args) {
int count=0;
Child child1 = new Child("白骨精");
child1.join();
child1.count++;
Child child2 = new Child("狐狸精");
child2.join();
child1.count++;
Child child3 = new Child("老鼠精");
child3.join();
child3.count++;
//因为此时的count变量法被三个对象共享,所以最后count的值为3
System.out.println(" 共有 "+ Child.count + " 小孩加入了游戏...");
}
}
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+" 加入了游戏 ");
}
}
1.2类变量的基础代码
package com.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="静态变量";
}
1.3类变量的相关细节
package com.static_;
public class StaticDetail {
public static void main(String[] args){
B b = new B();
//System.out.println(B.n1);无法访问(细节5 实例对象不能通过类名直接访问)
System.out.println(B.n2);
System.out.println(C.address);//(细节6 没有完成类的初始化,但是只要类加载就能使用类变量了)
}
}
class B{
public int n1 = 100;//实例变量/普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量
public static int n2 = 100;
}
class C{
public static String address = "北京";
}
1.4类方法的基本概念
类方法的使用场景:当方法中不涉及任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。(如果我们希望不创建实例(对象),也可以调用某个方法(即当做工具来使用),这时,把方法当做静态方法时非常合适。个人理解:就是c语言里面的函数)
1.5类方法的基本代码
package com.static_;
public class StaticMethod {
public static void main(String[] args) {
stu tom = new stu("tom");
tom.payFee(100);
stu mary = new stu("mary");
mary.payFee(200);
//输出当前收到的总学费
stu.showFee();
}
}
class stu{
private String name;
//定义一个静态变量,来积累学生的学费
private static double fee=0
public stu(String name) {
this.name = name;
}
//说明
//1.当方法使用了static修饰后,该方法就是静态方法
//2.静态方法就可以访问静态属性
public void payFee(double fee){
stu.fee+=fee;
}
public static void showFee(){
System.out.println("总学费有:"+stu.fee);
}
}
1.6类变量和类方法的注意事项
二、main方法
1.1mian方法的基本概念
如何使IDEA在运行主方法的时候也能传参数给args函数
1.2main方法的相关细节
(1)在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。
(2)但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
package com.main_;
public class Main01 {
//静态属性
private static String name = "一个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) {
//1.可以直接使用name 原因:静态方法可以直接访问本类中的静态成员
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();//ok
}
}
三、代码块
3.1代码块的基本概念
说明注意:
(1)修饰符可选,要写的话只能写static
(2)代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块
(3)逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
(4)“;”号可以写上也可以省略
3.2代码块的基础代码
package com.CodeBlock;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("美国队长");
Movie movie1 = new Movie("美国队长",50);
Movie movie2 = new Movie("美国队长", 50, "动作");
}
}
class Movie{
private String name;
private double price;
private String director;
//下面的三个构造器都有相同的语句
//这样代码看起来比较冗余
//这时我们可以把相同的语句,放入到下一个代码块中即可
//这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//代码块调用的顺序优先与构造器
{
System.out.println("电影屏幕打开了");
System.out.println("广告开始");
System.out.println("电影正式开始\n");
}
public Movie(String name) {
// System.out.println("电影屏幕打开了");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
// System.out.println("电影屏幕打开了");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
}
public Movie(String name, double price, String director) {
this.name = name;
this.price = price;
this.director = director;
// System.out.println("电影屏幕打开了");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
}
}
对于代码块的理解:
(1)相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化操作
(2)场景:如果多个和构造器中都有重复的语句,可以抽取到初初始化块中,提高代码的重用性
3.3代码块的相关细节
[1]static代码块也叫静态代码块,作用是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象就执行
[2]类什么时候被加载?
1.创建对象实例时(new)
package com.CodeBlock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//1.创建对象实例时(new)
AA aa = new AA();
AA aa1 = new AA();//创建了两个实例但是类只加载了一次
}
}
class AA{
static{
System.out.println("AA 的静态代码块1被执行");
}
2.创建子类对象实例,父类也会被加载(先加载父类,再加载子类)
package com.CodeBlock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//1.创建对象实例时(new)
//2.创建子类对象实例,父类也会被加载,而且,父类先被加载,子类后被加载
AA aa = new AA();
}
}
class AA extends BB{
static{
System.out.println("AA 的静态代码1被执行");
}
}
class BB{
static{
System.out.println("BB 的静态代码块1被执行");
}
}
3.使用类的静态成员时(静态属性,静态方法)
package com.CodeBlock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//1.创建对象实例时(new)
//2.创建子类对象实例,父类也会被加载,而且,父类先被加载,子类后被加载
//3.使用类的静态成员时(静态属性,静态方法)
System.out.println(Cat.n1);
}
}
class Cat{
public static int n1 = 999;//静态属性
//静态代码块
static{
System.out.println("Cat 的静态代码1被执行");
}
}
[3]普通代码块,在创建实例时,会被隐式的调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块就并不会执行
package com.CodeBlock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
System.out.println(AA.n1);//8888
//静态代码块一定会输出,但普通代码块不会输出
}
}
class AA{
public static int n1 = 8888;//静态属性
//静态代码块
static{
System.out.println("AA 的静态代码块1被执行");
}
//普通代码块,在new对象时被调用,而且是每创建一个对象就调用一次,和类加载没关系
//可以这样简单的理解,普通代码块是构造器的补充
{
System.out.println("AA 的普通代码块")
}
[4]创建一个对象时,在一个类调用顺序是:(下面的序号代表顺序)
1.调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按照他们定义的顺序调用)
package com.CodeBlock;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();//先调用getN1
}
}
class A{
//静态属性初始化
private static int n1 = getN1();
static{//静态代码块
System.out.println("A 静态代码块01");
}
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
}
把静态属性和静态代码块电话一下顺序看下输出结果
package com.CodeBlock;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();//先调用getN1,再调用静态代码块
}
}
class A{
static{//静态代码块
System.out.println("A 静态代码块01");
}
//静态属性初始化
private static int n1 = getN1();
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
}
2.调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
package com.CodeBlock;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();//(1)先调用getN1(2)调用静态代码块(3)getN2被调用(4)普通代码块01
}
}
class A{
private int n2 = getN2();
{
System.out.println("A 普通代码块01");
}
static{//静态代码块
System.out.println("A 静态代码块01");
}
public int getN2(){//普通方法/非静态
System.out.println("getN2被调用");
return 200;
}
//静态属性初始化
private static int n1 = getN1();
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
}
3.调用构造器方法
package com.CodeBlock;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();//(1)先调用getN1(2)调用静态代码块(3)getN2被调用(4)普通代码块01(5)A 构造器
}
}
class A{
private int n2 = getN2();
{
System.out.println("A 普通代码块01");
}
static{//静态代码块
System.out.println("A 静态代码块01");
}
public int getN2(){//普通方法/非静态
System.out.println("getN2被调用");
return 200;
}
//静态属性初始化
private static int n1 = getN1();
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
public A() {
System.out.println("构造器被调用");
}
}
[5]构造器的最前面其实隐含了super()和调用普通代码块,静态相关的代码块属性初始化在类加载时就执行完毕。 (这也是为什么构造器最后执行的执行的原因)
class A{
public A(){//构造器
//这里有隐藏的执行要求
super()//默认会有的super
//调用普通代码块
//调用自身
}
}
package com.CodeBlock;
public class CodeBlockDetail03 {
public static void main(String[] args){
new BBB();//(1)AAA 的普通代码块(2)AAA() 构造器被调用(3)BBB 的普通代码块(4) 构造器被调用
}
}
class AAA{
{
System.out.println("AAA 的普通块");
}
public AAA(){
System.out.println("AAA() 构造器被调用");
}
}
class BBB extends AAA{
{
System.out.println("BBB 的普通代码块");
}
public BBB(){
//(1)super,因此会先进入父类进行调用
//(2)调用本类的普通代码块
System.out.println("BBB() 构造器被调用");
}
}
[6]我们看一下创建一个子类时,他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下(这里就比较综合了,相比上一个细节加入了静态代码块和静态属性初始化):(此处的序号代表顺序)
关于这个细节的理解:
因为在创建对象时是先进行类加载再创建对象,而静态属性初始化和静态代码块是随着类加载而执行的(细节一提到过)。而加入继承的概念之后,会先进行父类的类加载再进行子类加载,在进行类加载的时候就会执行静态属性初始化和静态代码块(这就是为什么先要执行父类和子类的静态属性初始化和静态代码块原因)。普通属性初始化和普通代码的执行顺序在上一个细节提到了。
四、单例设计模式
4.1单例设计模式的基础概念
4.2饿汉式单例设计模式代码
package com.Singel_;
public class Singleton{
public static void main(String[] args) {
System.out.println(GirlFriend.n1);
//上述代码跟要创建的对象没有关系
//虽然只是调用了类的其它属性,但实际上构造器已经被调用了
//静态对象也就已经完成初始化了
}
}
//有一个类,GirlFriend
//只能有一个女朋友
class GirlFriend{
private String name;
public static int n1 = 999;
//饿汉式可能造成创建了对象没有使用的情况,造成了资源的浪费
private static GirlFriend Gf = new GirlFriend("小红红");
//如何保证我们只能创建一个GirlFriend
//1.将构造器私有化
//2.提供一个公共的static方法,返回gf对象
public GirlFriend(String name) {
System.out.println("构造器被调用");
this.name = name;
}
public static GirlFriend getInstance(){
return Gf;
}
}
运用static让属性在类加载时就完成初始化,就可以不需要new进行对象初始化了,避免了出现多个对象。 最后过调用静态方法完成输出。(注意,静态属性只能由静态方法返回
4.2懒汉式单例设计模式代码
package com.Singel_;
public class SingleTon2 {
public static void main(String[] args) {
System.out.println(Cat.n1);
//只执行上面这句话静态属性不会进行初始化
System.out.println(Cat.getInstance());
//调用getInstance才会完成初始化
}
}
//希望在程序运行过程中,只能创建一个Cat对象
class Cat{
private String name;
public static int n1 = 999;
private static Cat cat;
//1.仍然让构造器私有化
//2.定义一个static静态属性对象
//3.提供一个public的static方法,可以返回一个Cat对象
//4.懒汉式,只有用户使用getInstance时,才返回cat对象,后面再次调用时会返回上次创建的cat对象
private Cat(String name) {
this.name = name;
}
public static Cat getInstance(){
if(cat == null){
cat = new Cat("小可爱");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
4.3饿汉式和懒汉式的区别
五、final关键字
5.1final的基本介绍
package com.final_;
public class Final {
public static void main(String[] args) {
}
}
//如果我们要求A类不能被其他类继承
//可以使用final修饰A类
final class A{
}
//class B extends A {}
class C{
//如果我们要求hi不能被子类重写
//可以使用final修饰hi方法(可以继承)
public final void hi(){}
}
//class D extends C{
// public void hi(){
// System.out.println("重写C类的hi方法");
// }
//}
class E{
public final double TAX_RATE = 0.08;
}
class F{
public void cry(){
//这是NUM也是局部常量
final double NUM = 0.01;
//NUM = 0.09无法更改
System.out.println("NUM"+NUM);
}
}
5.2final的相关细节
第一部分细节:
package com.final_;
public class FinalDetail01 {
public static void main(String[] args) {
CC cc = new CC();
new EE().cal();
}
}
//细节2
class AA{
/*
1.定义时:如public final double TAX_RATE = 0.08;
2.在构造器中
3.在代码块中
*/
public final double TAX_RATE = 0.08;
public final double TAX_RATE2;
public final double TAX_RATE3;
public AA(){//在构造其中赋值
TAX_RATE2 = 1.1;
}
{//在代码块中赋值。因为代码块石构造器的一种补充机制
TAX_RATE3 = 8.8;
}
}
//细节3
class BB{
public static final double TAX_RATE = 9.99;
public static final double TAX_RATE2;
// public static final double TAX_RATE3;
static {
TAX_RATE2 = 3.3;
}
// public BB(){无法通过构造器赋值,因为静态属性初始化是在类加载时完成的,构造器是在类加载完成后执行的
// TAX_RATE3 = 8.8;
// }
}
//细节4
//final是可以实例化对象的,只是不能继承而已
final class CC{}
//细节5
//如果类不是final类,但是含有final方法。则该方法虽然不能重写诶,但是可以被继承
//即,仍然遵守继承的机制
class DD{
public final void cal(){
System.out.println("cal()方法");
}
}
class EE extends DD{
}
第二部分细节
package com.final_;
public class FinalDetail02 {
public static void main(String[] args) {
System.out.println(BBB.num);
}
}
//final 和static 往往搭配使用,效率会更高,不到导致类加载,底层编译器做了优化处理
class BBB{
public final static int num =1000;
static {
System.out.println("BBB 静态代码块被执行");
}
}
final class AAA{
//一般来说,如果过一个类已经是final类了,就没有必要再将方法修饰成final方法
//因为已经无法继承了,更谈不上重写
//public final void cry(){}
}