Java学习-第一部分-第二阶段-第一节:面向对象编程(高级) ...

打印 上一主题 下一主题

主题 874|帖子 874|积分 2622

面向对象编程(高级)

笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html)
类变量和类方法(static)

类变量

类变量-提出问题
提出问题的主要目的就是让大家思考解决之道,从而引出我要讲的知识点.说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。
传统的方法来解决
使用我们现有的技术来解决这个问题,大家看看如何?
✔思路

  • 在main方法中定义一个变量count
  • 当一个小孩加入游戏后count++,最后个count 就记录有多少小孩玩游戏
    1. public class ChildGame {
    2.     public static void main(String[] args) {
    3.         //定义一个变量count,统计有多少小孩加入了游戏
    4.         int count = 0;
    5.         Child child1 = new Child("白精");
    6.         child1.join();
    7.         count++;
    8.         Child child2 = new Child("老白");
    9.         child2.join();
    10.         count++;
    11.         
    12.         Child child3 = new Child("老白");
    13.         child3.join();
    14.         child3.count++;
    15.         
    16.         System.out.println("共有" + count + "小孩加入了游戏...");
    17.     }
    18. }
    19. class Child {//类
    20.     private String name;
    21.     public Child(String name) {
    22.         this.name = name;
    23.     }
    24.     public void join() {
    25.         System.out.println(name + "加入了游戏..");
    26.     }
    27. }
    复制代码
✔问题分析:

  • count是一个独立于对象,很尴尬
  • 以后我们访问count很麻烦,没有使用到OOP
  • 3.因此,我们引出类变量/静态变量
类变量快速入门
思考:如果,设计一个int count表示总人数,我们在创建一个小孩时,就把count加1,并且 count是所有对象共享的就ok了!,我们使用类变量来解决
  1. public class ChildGame {
  2.     public static void main(String[] args) {
  3.         Child child1 = new Child("白精");
  4.         child1.join();
  5.         child1.count++;
  6.         Child child2 = new Child("老白");
  7.         child2.join();
  8.         child2.count++;
  9.         
  10.         Child child3 = new Child("老白");
  11.         child3.join();
  12.         child3.count++;
  13.         
  14.         //......
  15.         System.out.println("共有" + child1.count + "小孩加入了游戏...");
  16.         System.out.println(child1.count);//3
  17.         System.out.println(child2.count);//3
  18.         System.out.println(child3.count);//3
  19.         System.out.println(Child.count);//3
  20.     }
  21. }
  22. class Child {//类
  23.     private String name;
  24.     //定义一个变量 count,是一个类变量(静态变量)static静态
  25.     //该变量最大的特点就是会被Child类的所有的对象实例共享
  26.     public static int count = 0;
  27.     public Child(String name) {
  28.         this.name = name;
  29.     }
  30.     public void join() {
  31.         System.out.println(name + "加入了游戏..");
  32.     }
  33. }
复制代码
其中child1,child2以及child3中的count指向相同空间,被共享,为同一值
jdk8以及之前,count(也就是静态域)在方法区中。jdk8之后,jdk存放在堆中
有些书说在方法区... jdk版本有关系,记住一点: static变量是对象共享
不管static变量在哪里,共识:

  • static变量是同一个类所有对象共享
  • static类变量,在类加载的时候就生成了.
什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
如何定义类变量
定义语法:
访问修饰符 static 数据类型 变量名;[推荐]
static 访问修饰符 数据类型 变量名;
  1. class A{
  2.     public static string name = "abc";
  3.     static public int totalNum = 100;
  4. }
复制代码
如何访问类变量
类名.类变量名
或者对象名.类变量名【静态变量的访问健饰符的访问权限和范围和普通属性是一样的。】
推荐使用:类名.类变量名;
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         //类名.类变量名
  4.         // 说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
  5.         System.out.println(A.name);
  6.         A a = new A();
  7.         System.out.println("a.name=" +a.name);
  8.     }
  9. }
  10. class A {
  11.     //类变量
  12.     public static String name = "韩顺平";
  13. }
复制代码
类变量使用注意事项和细节讨论

  • 什么时候需要用类变量
    当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, fee)
  • 类变量与实例变量(普通属性)区别
    类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
  • 加上static称为类变量或静态变量,否则称为 实例变量/普通变量/非静态变量
  • 类变量可以通过类名.类变量名或者对象名.类变量名来访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【前提是 满足访问修饰符的访问权限和范围】
  • 实例变量不能通过类名.类变量名方式访问。
  • 类变量是在类加载时就初始化了,也就是说,即使你没有助建家,只安尖加郓,就可以使用类变量了。
  • 类变量的生命周期是随类的加载开始,随着类消亡而销毁。
类方法

类方法基本介绍
类方法也叫静态方法。
形式如下:
访问修饰符 static 数据返回类型 方法名(){ }【推荐】
static 访问修饰符 数据返回类型 方法名(){ }
类方法的调用:
使用方式:类名.类方法名 或者 对象名.类方法名【前提是满足访问修饰符的访问权限和范围】
类方法应用案例
请大家看一个静态方式小案例。(统计学费总和)
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         //创建2个学生对象,叫学费
  4.         Stu tom = new Stu("tom");
  5.         tom.payFee(100);
  6.         Stu mary = new Stu("mary");
  7.         mary.payFee(200);
  8.         
  9.         //输出当前收到的总学费
  10.         Stu.showFee();//300
  11.     }
  12. }
  13. class Stu {
  14.     private String name;//普通成员
  15.     // 定义一个静态变量,来累积学生的学费
  16.     private static double fee = 0;
  17.     public Stu(String name) {
  18.         this.name = name;
  19.     }
  20.     //说明
  21.     //1.当方法使用了static修饰后,该方法就是静态方法
  22.     //2.静态方法就可以访问静态属性/变量
  23.     public static void payFee(double fee) {
  24.         Stu.fee += fee;//累积到
  25.         
  26.     }
  27.     public static void showFee() {
  28.         System.out.println("总学费有:" + Stu.fee);
  29.     }
  30. }
复制代码
类方法经典的使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。(不需要额外创建对象)
比如:工具类中的方法utils
Math类、Arrays类、Collections集合类看下源码:
小结
在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等..
举例
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         System.out.println(MyTools.calSum(10,30));
  4.     }
  5. }
  6. class MyTools {
  7.     //开发自己的工具类时,可以将方法做成静态的,方便调用class MyTools {
  8. //求出两个数的和
  9.     public static double calSum(double n1, double n2) {
  10.         return n1 + n2;
  11.     }
  12. }
复制代码
类方法使用注意事项和细节讨论

  • 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数
    普通方法中隐含着this的参数
  • 类方法可以通过类名调用,也可以通过对象名调用。
  • 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调
    用。
  • 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。[举例]
  • 类方法(静态方法)中只能访问静态变量或静态方法。【如何理解】
  • 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。
小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
理解main方法语法

●深入理解main方法
解释main方法的形式:public static void main(String[] args){}

  • main方法时虚拟机调用
  • java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
  • java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
  • 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数,案例演示,接收参数.
  • java 执行的程序 参数1 参数2 参数3
特别提示:

  • 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。
  • 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
代码块

●基本介绍
代码化块又称为初始化块,属于类中的成员[即 是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
●基本语法
[修饰符]{
​    代码
};
注意:

  • 修饰符可选,要写的话,也只能写static
  • 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块。
  • 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
  • ;号可以写上,也可以省略。
实例
  1. package com.hspedu.p386;
  2. public class codeBlock01 {
  3.     public static void main(String[] args) {
  4.         Movie movie = new Movie("你好,李焕英");
  5.     }
  6. }
  7. class Movie {
  8.     private String name;
  9.     private double price;
  10.     private String director;
  11.     //3个构造器-》重载
  12.     //老韩解读
  13.     //(1)下面的三个构造器都有相同的语句
  14.     //(2)这样代码看起来比较冗余
  15.     //(3)这时我们可以把相同的语句,放入到一个代码块中,即可
  16.     //(4)这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
  17.     //(5)代码块调用的顺序优先于构造器。。
  18.     {
  19.         System.out.println("电影屏幕打开...");
  20.         System.out.println("广告开始...");
  21.         System.out.println("电影正是开始...");
  22.     }
  23.     public Movie(String name) {
  24.         System.out.println("Hovie(Strihg name)被调用。.。");
  25.         this.name = name;
  26.     }
  27.     public Movie(String name, double price) {
  28.         this.name = name;
  29.         this.price = price;
  30.     }
  31.     public Movie(String name, double price, String director) {
  32.         this.name = name;
  33.         this.price = price;
  34.         this.director = director;
  35.     }
  36. }
复制代码
代码块使用注意事项和细节讨论

  • static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。
  • 类什么时候被加载
    ①创建对象实例时(new)
    ②创建子类对象实例,父类也会被加载
    ③使用类的静态成员时(静态属性,静态方法)
    案例演示: A类 extends B类 的静态块
  • 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
    如果只是使用类的静态成员时,普通代码块并不会执行。
小结:

  • static代码块是类加载时,执行,只会执行一次
  • 普通代码块是在创建对象时调用的,创建一次,调用一次
  • 类加载的3种情况,需要记住.
    1. public class Test {
    2.     public static void main(String[] args) {
    3. //        new A();
    4. //        new A();
    5. //
    6. //        System.out.println("------------------");
    7. //        new B();
    8. //        System.out.println("------------------");
    9. //        System.out.println(A.a);
    10.         System.out.println(B.b);
    11.     }
    12. }
    13. class A {
    14.     public static int a=10;
    15.     static {//只在加载类时运行
    16.         System.out.println("111111");
    17.     }
    18.     {//普通代码块,在new对象时,被调用,而且是每创建一个对象,就调用一次
    19.      //可以这样简单的,理解普通代码块是构造器的补充
    20.         System.out.println("普通");
    21.     }
    22. }
    23. class B extends A{
    24.     static {
    25.         System.out.println("2222222");
    26.     }
    27. }
    复制代码

  • 创建一个对象时,在一个类调用顺序是:(重点,难点)∶
    ①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
    ②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
    ③调用构造方法。

  • 构造器的最前面其实隐含了super()和调用普通代码块,新写一个类,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
    1. class A2 {
    2.     public void A() { //构造器
    3.         //这里有隐藏的执行要求
    4.         //(1) super():这个知识点,在前面讲解继承的时候,老师说
    5.         // (2)调用普通代码块的
    6.         System.out.println("ok");
    7.     }
    8. }
    复制代码
  • 我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
    ①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    ②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    ③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    ④父类的构造方法
    ⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    ⑥子类的构造方法//面试题
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         //老师说明
  4.         //(1)进行类的加载
  5.         //1.1 先加载父类A02 1.2 再加载Bo2//(2)创建对象
  6.         new B02();//对象
  7.     }
  8. }
  9. class A02 {//父类
  10.     private static int n1 = getVal01();
  11.     static {
  12.         System.out.println("A02的一个静态代码块..");//2
  13.     }
  14.     {
  15.         System.out.println("A02的第一个普通代码块..");//5
  16.     }
  17.     public int n3 = getVal02();
  18.     public static int getVal01() {
  19.         System.out.println("getVal01");//1
  20.         return 10;
  21.     }
  22.     public int getVal02() {
  23.         System.out.println("getVal02");//6
  24.         return 10;
  25.     }
  26.     public A02() {
  27.         System.out.println("A02的构造器");//7
  28.     }
  29. }
  30. class B02 extends A02 { //
  31.     private static int n3 = getVal03();
  32.     static {
  33.         System.out.println("BO2的一个静态代码块..");//4
  34.     }
  35.     public int n5 = getVal04();
  36.     {
  37.         System.out.println("B02的第一个普通代码块..");//9
  38.     }
  39.     public static int getVal03() {
  40.         System.out.println("getVal03");//3
  41.         return 10;
  42.     }
  43.     public int getVal04() {
  44.         System.out.println("getVal04");//8
  45.         return 10;
  46.     }
  47.     public B02() {
  48.         System.out.println("BO2的构造器");//10
  49.     }
  50. }
复制代码

  • 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
单例设计模式

什么是单例模式

  • 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
  • 单例模式有两种方式: 1)饿汉式 2)懒汉式
单例模式应用实例
演示饿汉式和懒汉式单例模式的实现。步骤如下:

  • 构造器私有化=》防止直接new
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法。getInstance
  • 代码实现
饿汉式
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         GirlFriend instance = GirlFriend.getInstance();
  4.         System.out.println(instance);
  5.         GirlFriend instance2 = GirlFriend.getInstance();
  6.         System.out.println(instance2);
  7.         System.out.println(instance == instance2);//T
  8.     }
  9. }
  10. class GirlFriend {
  11.     private String name;
  12.     //为了能够在静态方法中,返回gf对象,需要将其修饰为static
  13.     private static GirlFriend gf = new GirlFriend("小红红");
  14.     //如何保障我们只能创建一个 GirlFriend对象
  15.     //步骤
  16.     //步骤[单例模式-饿汉式]
  17.     //1。将构造器私有化
  18.     // 2.在类的内部直接创建
  19.     //3.提供一个公共的static方法,返回 gf对象
  20.     private GirlFriend(String name) {
  21.         this.name = name;
  22.     }
  23.     public static GirlFriend getInstance() {
  24.         return gf;
  25.     }
  26. }
复制代码
懒汉式
  1. package com.hspedu.test;
  2. public class Test {
  3.     public static void main(String[] args) {
  4.         //new Cat("大黄");
  5.         Cat instance = Cat.getInstance();
  6.         System.out.println(instance);
  7.     }
  8. }
  9. class Cat {
  10.     private String name;
  11.     private static Cat cat;//步骤
  12.     //1.仍然构造器私有化
  13.     //2.定义一个static属性对象
  14.     //3.提供一个public的static方法,可以返回一个Cat对象
  15.     private Cat(String name) {
  16.         this.name = name;
  17.     }
  18.     public static Cat getInstance() {
  19.         if (cat == null) {//如果没有创建cat封象
  20.             cat = new Cat("小可爱");
  21.         }
  22.         return cat;
  23.     }
  24. }
复制代码
饿汉式VS懒汉式

  • 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
  • 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善)
  • 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
    在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。
final关键字

●基本介绍
final中文意思:最后的,最终的.
final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求,就会使用到final:

  • 当不希望类被继承时,可以用final修饰.
    1. final class A{ }
    2. class B extends A {}//会报错
    复制代码
  • 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【案例演示:访问修饰符 final 返回类型方法名】
    1. class c {
    2.         //如果我们要求hi不能被子类重写
    3.         //可以使用final修饰hi方法
    4.         public final void hi() {}
    5. }
    6. class D extends C {
    7.     @0verride
    8.         public void hi {//报错
    9.                 System.out.println("重写了C类的hi方法..");
    10.         }
    11. }
    复制代码
  • 当不希望类的的某个属性的值被修改,可以用final修饰.【案例演示: public final double TAX_RATE=0.08】
  • 当不希望某个局部变量被修改,可以使用final修饰【案例演示: final double TAX_RATE=0.08)
    1. class F {
    2.         public void cry() {
    3.                 //这时,NUM也称为局部常量
    4.             final double NUM = 0.01;
    5.         NUM= 0.9;//报错
    6.                 System.out.println("NUM=" + NUM);
    7.         }
    8. }
    复制代码
final使用注意事项和细节讨论

  • final修饰的属性又叫常量,一般用XX_XX_XX 来命名
  • final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
    ①定义时:如public final double TAX_RATE=0.08;
    ②在构造器中
    ③在代码块中。
  • 如果final修饰的属性是静态的,则初始化的位置只能是
    ①定义时
    ②在静态代码块赋值,不能在构造器中赋值。
  • final类不能继承,但是可以实例化对象。
  • 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
  • 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(多此一举)
  • final不能修饰构造方法(即构造器)
  • final和static往往搭配使用,效率更高,底层编译器做了优化处理。
  • 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
抽象类

当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
  1. abstract class Animal {
  2.     private String name;
  3.     public Animal(String name) {
  4.         this.name = name;
  5.     }
  6.     //思考:这里eat这里你实现了,其实没有什么意义//即:父类方法不确定性的问题
  7.     //===>考虑将该方法设计为抽象(abstract)方法//===>所谓抽象方法就是没有实现的方法
  8.     //===>所谓没有实现就是指,没有方法体
  9.     //===>当一个类中存在抽象方法时,需要将该类声明为abstract类
  10.    
  11. //    public void eat() {
  12. //        System.out.println("这是一个动物,但是不知道吃什么..");
  13. //    }
  14.     public abstract void eat() ;
  15. }
复制代码
抽象类的介绍

  • 用abstract关键字来修饰一个类时,这个类就叫抽象类
    访问修饰符 abstract 类名{
    }
  • 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
    访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
  • 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
  • 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多
抽象类使用的注意事项和细节讨论

  • 抽象类不能被实例化
    1. public class AbstractDetail01 {
    2.     public static void main(String[] args) {
    3.                 //抽象类,不能被实例化
    4.         new A();//报错
    5.     }
    6. }
    7. abstract class A {
    8. }
    复制代码
  • 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  • 一旦类包含了abstract方法,则这个类必须声明为abstract
    1. class B {//报错
    2.         public abstract void hi();
    3. }
    复制代码
  • abstract只能修饰类和方法,不能修饰属性和其它的。
  • 抽象类可以有任意成员【抽象类还是类】,比如:非抽象方法、构造器、静态属性等等
  • 抽象方法不能有主体,即不能实现.如图所示
    abstract void aaa(){......}//报错,不能存在“{}”
  • 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。[举例 A类,B类,C类]
    1. //如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
    2. abstract class E {
    3.     public abstract void hi();
    4. }
    5. abstract class F extends E {
    6. }
    7. class G extends E {
    8.     @Override
    9.     public void hi() { //这里相等于G子类实现了父类E的抽象方法,所谓实现方法,就是有方法体
    10.     }
    11. }
    复制代码
  • 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
抽象类最佳实践-模板设计模式

最佳实践
需求

  • 有多个类,完成不同的任务job
  • 要求统计得到各自完成任务的时间
  • 请编程实现
感情的自然流露

  • 先用最容易想到的方法
  • 分析问题,提出使用模板设计模式
[code]public class Test {        public static void main(String[] args) {                new A().job();                new B().job();        }}//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类class A {        public void job() {        //得到开始的时间        long start = System.currentTimeMillis();        long num = 0;        for (long i = 1; i
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

花瓣小跑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表