• 面向对象的个人理解(从C/C++到Java)


    目录

    前言

    功能分类

    干扰的起源

    调整1

     调整2

     比较:

    总结

    用类(class)消除干扰

    Dog类

    Cat类

     入口类

     总结

    JDK中的一些实现

    尝试

    代码重用

    类型化(Type)

     继承

     总结

    访问控制

    私有(private)与公开(public)

     保护(protected)

    局部实现

    内部类

     匿名类

    局部类

     总结

    一些细节

    静态成员

    静态类

    Lamda

    总结


    前言

    功能分类

    类(class)的第一个功能是隔离,起到边界的作用,使得不同功能的代码互不干扰。

    干扰的起源

    在非面向对象的语言中,我们主要定义结构和函数来实现功能。下边用C语言来举个例子。

    某程序员写了宠物模拟器,不过开始他只有一只狗,于是他写了一个只有狗子特性的代码,代码功能就是模拟模拟狗子被撸后,会㕵㕵叫:

    注:C语言代码使用Visual Studio编写。

    1. #include
    2. #include
    3. const wchar_t* name = L"旺才";
    4. void call() {
    5. wprintf(L"%s,过来!\n",name);
    6. }
    7. void pat() {
    8. wprintf(L"汪汪\n");
    9. }
    10. void main() {
    11. setlocale(LC_ALL, "");
    12. wprintf(L"撸狗\n-----------\n");
    13. call();
    14. pat();
    15. }

    运行结果:

     此时这个程序没啥问题,接着又养了一只猫,猫跟狗差不多的功能,但我们不能重复定义call()和pat()函数,这样会有冲突,那需要对代码做些改动,可以接受两只宠物。

    这里至少有两种思路,一种是再复制狗的代码,改下名称;另一种,保持函数名称不变,添加参数进行区别。

    调整1

    对于前者,可能改成这样:

    1. #include
    2. #include
    3. const wchar_t* dog_name = L"旺才";
    4. const wchar_t* cat_name = L"花花";
    5. void dog_call() {
    6. wprintf(L"%s,过来!\n", dog_name);
    7. }
    8. void cat_call() {
    9. wprintf(L"%s,过来!\n", cat_name);
    10. }
    11. void dog_pat() {
    12. wprintf(L"汪汪\n");
    13. }
    14. void cat_pat() {
    15. wprintf(L"喵喵\n");
    16. }
    17. void main() {
    18. setlocale(LC_ALL, "");
    19. char c = '0';
    20. while (c != 'q') {
    21. if (c != '\n')
    22. wprintf(L">>\n请选择宠物:狗(d),猫(c)\n");
    23. c = getchar();
    24. if (c == '\n') continue;
    25. if (c == 'd') {
    26. wprintf(L"撸狗\n-----------\n");
    27. dog_call();
    28. dog_pat();
    29. }
    30. else if (c == 'c') {
    31. wprintf(L"撸猫\n-----------\n");
    32. cat_call();
    33. cat_pat();
    34. }
    35. else if (c != 'q') {
    36. wprintf(L"所先宠物不存在\n");
    37. }
    38. }
    39. }

     运行结果:

     调整2

    另一种改成这样:

    1. #include
    2. #include
    3. const wchar_t* dog_name = L"旺才";
    4. const wchar_t* cat_name = L"花花";
    5. void call(char pet) {
    6. if (pet == 'd')
    7. wprintf(L"%s,过来!\n", dog_name);
    8. else if (pet == 'c')
    9. wprintf(L"%s,过来!\n", cat_name);
    10. else
    11. wprintf(L"所选宠物不存在\n");
    12. }
    13. void pat(char pet) {
    14. if (pet == 'd')
    15. wprintf(L"汪汪\n");
    16. else if (pet == 'c')
    17. wprintf(L"喵喵\n");
    18. }
    19. void main() {
    20. setlocale(LC_ALL, "");
    21. char c = '0';
    22. while (c != 'q') {
    23. if (c != '\n')
    24. wprintf(L">>\n请选择宠物:狗(d),猫(c)\n");
    25. c = getchar();
    26. if (c == '\n') continue;
    27. if (c == 'd') {
    28. wprintf(L"撸狗\n-----------\n");
    29. }
    30. else if (c == 'c') {
    31. wprintf(L"撸猫\n-----------\n");
    32. }
    33. call(c);
    34. pat(c);
    35. }
    36. }

    运行结果:

     

     比较:

    前者命名冲突,代码重复。好处是比较直观,小白都能看懂。

    后者代码整洁,但交叉比较,耦合性高,牵一发可能动全身。

    总结

    不管用哪一种代码改动,多多少少都会对别的部分形成干扰,虽然有方法尽量去避免这些干扰,但往往对程序员的要求太高,反而降低了开发效率。产生干扰的主要原因是,C语言只能做到函数级别的代码分组,增加功能就要不断地增加函数,当函数的数据到达一定级别时,名称的冲突必然导致系统的不可维护。我们可以想像一下,当这个程序有了几百上千种宠物后,代码会是什么样子的?

    用类(class)消除干扰

    类实现了更大范围的代码分组,将相关的函数变成成员方法,将全局变量变成成员字段放在一个类中,与其它代码隔离开来,在小范围内可以有效地避开命名冲突的问题。就好比,行政区中全国乡镇的名称重复的很多,但在同一个区县中就不存在了。为了区分同名的乡镇,我们只要区分区县就可以了。所以前边的代码,用java的方式,我们就可以写成两个类,一个Dog类,一个Cat类,虽然他们有相同的部分,但互不干扰。

    Dog类

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog {
    6. private static String name = "旺财";
    7. public static void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. public static void pat() {
    11. System.out.println("汪汪!");
    12. }
    13. }

    Cat类

    1. /**
    2. * 猫猫
    3. *
    4. */
    5. public class Cat {
    6. private static String name = "花花";
    7. public static void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. /**
    11. * 猫会妙妙
    12. */
    13. public static void pat() {
    14. System.out.println("喵喵");
    15. }
    16. }

     入口类

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸狗\n-----------\n");
    13. Dog.call();
    14. Dog.pat();
    15. } else if (c == 'c') {
    16. System.out.println("撸猫\n-----------\n");
    17. Cat.call();
    18. Cat.pat();
    19. } else if (c != 'q') {
    20. System.out.println("所先宠物不存在\n");
    21. }
    22. }
    23. }
    24. }

     运行结果:

     总结

    JDK中的一些实现

    Math (Java SE 17 & JDK 17)declaration: module: java.base, package: java.lang, class: Mathhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Math.html System (Java SE 17 & JDK 17)declaration: module: java.base, package: java.lang, class: Systemhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html

    Executors (Java SE 17 & JDK 17)declaration: module: java.base, package: java.util.concurrent, class: Executorshttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Executors.html

    尝试

    用Java重构之后,加宠物就方便很多了,只要类不重名,其它代码随便写。比如增加一个Parrot类:

    1. /**
    2. * 鹦鹉
    3. *
    4. */
    5. public class Parrot {
    6. private static String name = "鹦哥";
    7. public static void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. public static void pat() {
    11. System.out.println("烦死了!");
    12. }
    13. }

    修改App.java

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸狗\n-----------\n");
    13. Dog.call();
    14. Dog.pat();
    15. } else if (c == 'c') {
    16. System.out.println("撸猫\n-----------\n");
    17. Cat.call();
    18. Cat.pat();
    19. } else if (c == 'p') {
    20. System.out.println("撸鹦鹉\n-----------\n");
    21. Parrot.call();
    22. Parrot.pat();
    23. } else if (c != 'q') {
    24. System.out.println("所先宠物不存在\n");
    25. }
    26. }
    27. }
    28. }

     运行结果:

    代码重用

    前边,虽然解决了代码中的某些命名冲突的问题,但有个最大的缺点,就是重复率太高,如果不看具体实现,字段、方法的名称几乎是一样的。其实这些内容是可以做到重用的,Java的代码重用除了传统的定义数据结构和通用函数外,还可以类型化(Type)及继承。

    类型化(Type)

    前边的示例中,一个类只实现了一个宠物的行为,如果又增加了一只狗狗,是不是要增加一个类,比如Dog1,其实大可不必。前边之所以只能表述一个宠物,是因为类的成员都标成了static,意思就是这个类中的代码跟面向过程没啥差别,只是调用前需要加上类名作为前辍。增加的狗狗目前来说,可能只是名字不一样,所以只要一个狗狗类的模板即可。

    Java的类除了分组的作用,本身也是可以作为一个数据类型来使用的,此时只要将类成员的static关键去掉即可。接下来改下Dog类、Cat类和Parrot类。

    Dog类

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog {
    6. private String name = "旺财";
    7. public void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. public void pat() {
    11. System.out.println("汪汪!");
    12. }
    13. }

    Cat类

    1. /**
    2. * 猫猫
    3. *
    4. */
    5. public class Cat {
    6. private String name = "花花";
    7. public void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. /**
    11. * 猫会妙妙
    12. */
    13. public void pat() {
    14. System.out.println("喵喵");
    15. }
    16. }

    1. /**
    2. * 鹦鹉
    3. *
    4. */
    5. public class Parrot {
    6. private String name = "鹦哥";
    7. public void call() {
    8. System.out.format("%s\n", name);
    9. }
    10. public void pat() {
    11. System.out.println("烦死了!");
    12. }
    13. }

     此时,各个类就成类型(Type)结构了,如果调用这些代码,就得先创建类型实例,更改App类的代码,变成这样:

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸狗\n-----------\n");
    13. Dog dog = new Dog();
    14. dog.call();
    15. dog.pat();
    16. } else if (c == 'c') {
    17. System.out.println("撸猫\n-----------\n");
    18. Cat cat = new Cat();
    19. cat.call();
    20. cat.pat();
    21. } else if (c == 'p') {
    22. System.out.println("撸鹦鹉\n-----------\n");
    23. Parrot parrot = new Parrot();
    24. parrot.call();
    25. parrot.pat();
    26. } else if (c != 'q') {
    27. System.out.println("所先宠物不存在\n");
    28. }
    29. }
    30. }
    31. }

    虽然多了点代码,但我们可以复用这个结构,只要做少量的修改,就可以创建无数个宠物。改一下Dog类,增加两个构造器,一个是默认的无参数,另一个是带参数的,这样可以创建对象的修改狗狗的名字:

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog {
    6. private String name = "旺财";
    7. public Dog() {
    8. }
    9. public Dog(String dogName) {
    10. name = dogName;
    11. }
    12. public void call() {
    13. System.out.format("%s\n", name);
    14. }
    15. public void pat() {
    16. System.out.println("汪汪!");
    17. }
    18. }

     App类

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸两条狗\n-----------\n");
    13. Dog dog = new Dog();
    14. dog.call();
    15. dog.pat();
    16. Dog dog1 = new Dog("小贝");
    17. dog1.call();
    18. dog1.pat();
    19. } else if (c == 'c') {
    20. System.out.println("撸猫\n-----------\n");
    21. Cat cat = new Cat();
    22. cat.call();
    23. cat.pat();
    24. } else if (c == 'p') {
    25. System.out.println("撸鹦鹉\n-----------\n");
    26. Parrot parrot = new Parrot();
    27. parrot.call();
    28. parrot.pat();
    29. } else if (c != 'q') {
    30. System.out.println("所先宠物不存在\n");
    31. }
    32. }
    33. }
    34. }

    运行效果:

     

     继承

    另一种代码重用的方式就是继承了,上述示例中无论狗狗、猫咪还是鹦鹉,它们行为方式是一样的,都是撸完后做出打出一行文本,无非就是内容上的差别,我们可以相同的部分提取出,单独创建一类在存放,响应内容的由具体的宠物类中实现,我们创建一个Pet类:

    1. /**
    2. * 小可爱们
    3. *
    4. */
    5. public class Pet {
    6. private String name = "无名";// 宠物都有名字
    7. public Pet() {
    8. }
    9. public Pet(String petName) {
    10. name = petName;
    11. }
    12. public void call() {
    13. System.out.format("%s\n", name);
    14. }
    15. public void pat() {
    16. System.out.println(response());
    17. }
    18. /**
    19. * 默认没反应,由具体对象实现
    20. * @return
    21. */
    22. protected String response() {
    23. return "";
    24. }
    25. }

    然后修改各个宠物类: 

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog extends Pet {
    6. public Dog() {
    7. super();
    8. }
    9. public Dog(String dogName) {
    10. super(dogName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "汪汪!";
    15. }
    16. }
    1. /**
    2. * 猫猫
    3. *
    4. */
    5. public class Cat extends Pet {
    6. public Cat() {
    7. this("花花");
    8. }
    9. public Cat(String catName) {
    10. super(catName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "喵喵!";
    15. }
    16. }

    1. /**
    2. * 鹦鹉
    3. *
    4. */
    5. public class Parrot extends Pet {
    6. public Parrot() {
    7. this("鹦哥");
    8. }
    9. public Parrot(String parrotName) {
    10. super(parrotName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "烦死了!";
    15. }
    16. }

    运行结果:

     

     总结

    为了实现代码的重用,实际还是有点代价的,代码的结构及语法都复杂了不少,但有了IDE的支撑,以后往后代码的增多,这点代价还是非常值的。接下来可以尝试增加两个宠物,一个是黄金莽,另一个是猫的细分种类咖啡猫。

    对于黄金莽,即不需要名字,也不会发声,代码可以写成这样,基本上不用写什么代码:

    1. /**
    2. * 黄金莽
    3. *
    4. */
    5. public class GoldBoa extends Pet {
    6. public GoldBoa() {
    7. super("");
    8. }
    9. }

    对于 咖啡猫,除了跟普通猫一样,还会说人话:

    1. /**
    2. * 咖啡猫
    3. *
    4. */
    5. public class Garfield extends Cat {
    6. public Garfield() {
    7. super("");
    8. }
    9. public Garfield(String name) {
    10. super(name);
    11. }
    12. public void speak() {
    13. System.out.print("我会说话!");
    14. }
    15. }

    修改一下App,创建新的宠物:

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p),黄金莽(b)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸两条狗\n-----------\n");
    13. Dog dog = new Dog();
    14. dog.call();
    15. dog.pat();
    16. Dog dog1 = new Dog("小贝");
    17. dog1.call();
    18. dog1.pat();
    19. } else if (c == 'c') {
    20. System.out.println("撸猫\n-----------\n");
    21. Cat cat = new Cat();
    22. cat.call();
    23. cat.pat();
    24. Garfield garfield = new Garfield();
    25. garfield.call();
    26. garfield.pat();
    27. garfield.speak();
    28. } else if (c == 'p') {
    29. System.out.println("撸鹦鹉\n-----------\n");
    30. Parrot parrot = new Parrot();
    31. parrot.call();
    32. parrot.pat();
    33. } else if (c == 'b') {
    34. System.out.println("撸黄金莽\n-----------\n");
    35. GoldBoa boa = new GoldBoa();
    36. boa.call();
    37. boa.pat();
    38. } else if (c != 'q') {
    39. System.out.println("所先宠物不存在\n");
    40. }
    41. }
    42. }
    43. }

     运行结果:

    访问控制

    代码进行分组后,还可以对内部成员进行访问控制。这样可以对代码进行更多的约束控制,上述示例中,每个宠物都有自己的名字,名字一旦起好,别人就不能随意发动了,所以在定义类的时候,name这个字段前边加上了private这个修饰符,说明不能被外部更改,因为加了这个修饰后,程序运行起来后,这个代码对外部的代码是不可见的。就好比路上见到一个人,你不知道这个有没有带钱包,因为钱包是私有的,一般是不让外人看到的。

    私有(private)与公开(public)

    宠物的名字虽然不可修改,但我们还是可以得到宠物的名字的,然后修改一下Pet类,增加一个获取名字的方法,这个方法是公开的,也就说谁都可以知道宠物的名字,所以前边加了一个修改符public:

    1. /**
    2. * 小可爱们
    3. *
    4. */
    5. public class Pet {
    6. private String name = "无名";// 宠物都有名字
    7. public Pet() {
    8. }
    9. public Pet(String petName) {
    10. name = petName;
    11. }
    12. public String getName() {
    13. return name;
    14. }
    15. public void call() {
    16. System.out.format("%s\n", name);
    17. }
    18. public void pat() {
    19. System.out.println(response());
    20. }
    21. /**
    22. * 默认没反应,由具体对象实现
    23. *
    24. * @return
    25. */
    26. protected String response() {
    27. return "";
    28. }
    29. }

    再改下App类:

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p),黄金莽(b)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸两条狗\n-----------\n");
    13. Dog dog = new Dog();
    14. System.out.format("宠物名字:%s\n",dog.getName());
    15. dog.call();
    16. dog.pat();
    17. Dog dog1 = new Dog("小贝");
    18. System.out.format("宠物名字:%s\n",dog1.getName());
    19. dog1.call();
    20. dog1.pat();
    21. } else if (c == 'c') {
    22. System.out.println("撸猫\n-----------\n");
    23. Cat cat = new Cat();
    24. System.out.format("宠物名字:%s\n",cat.getName());
    25. cat.call();
    26. cat.pat();
    27. Garfield garfield = new Garfield();
    28. System.out.format("宠物名字:%s\n",garfield.getName());
    29. garfield.call();
    30. garfield.pat();
    31. garfield.speak();
    32. } else if (c == 'p') {
    33. System.out.println("撸鹦鹉\n-----------\n");
    34. Parrot parrot = new Parrot();
    35. System.out.format("宠物名字:%s\n",parrot.getName());
    36. parrot.call();
    37. parrot.pat();
    38. } else if (c == 'b') {
    39. System.out.println("撸黄金莽\n-----------\n");
    40. GoldBoa boa = new GoldBoa();
    41. System.out.format("宠物名字:%s\n",boa.getName());
    42. boa.call();
    43. boa.pat();
    44. } else if (c != 'q') {
    45. System.out.println("所先宠物不存在\n");
    46. }
    47. }
    48. }
    49. }

     运行效果:

     保护(protected)

    protected这个关键字是继承时专用的。表示可以提供给子类访问,这样的话就可以将一些方法让子类调用、重写或实现。之前代码中已经在Pet类中定义了一个叫做response()的方法,这个方法在App中是不可见的,但Dog、Cat等类中是可以访问的。具体请查看之前的代码,这里不复述。

    局部实现

    内部类

    之前,我们撸宠物的时候,宠物是直接做出反应的,但有时候可能是反应迟钝的,这时可以用线程来模拟一下,线程需要写一个类实现Runnable接口,但这种反应是每个动物特有的,所以不必定义全局类,只定义一个内部类来实现,以狗子举例:

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog extends Pet {
    6. public Dog() {
    7. this("汪财");
    8. }
    9. public Dog(String dogName) {
    10. super(dogName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "汪汪!";
    15. }
    16. @Override
    17. public void pat() {
    18. Thread daze = new Thread(new DogResponse());
    19. daze.start();
    20. }
    21. class DogResponse implements Runnable {
    22. @Override
    23. public void run() {
    24. try {
    25. System.out.println("先发个呆!");
    26. Thread.sleep(3000);
    27. System.out.println(response());
    28. } catch (InterruptedException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }
    33. }

    为了方便起见,再改下App, 去掉一只狗狗:

    1. import java.io.IOException;
    2. public class App {
    3. public static void main(String[] args) throws IOException {
    4. char c = '0';
    5. while (c != 'q') {
    6. if (c != '\n' && c != '\r')
    7. System.out.println(">>\n请选择宠物:狗(d),猫(c),鹦鹉(p),黄金莽(b)\n");
    8. c = (char) System.in.read();
    9. if (c == '\n' || c == '\r')
    10. continue;
    11. if (c == 'd') {
    12. System.out.println("撸两条狗\n-----------\n");
    13. Dog dog = new Dog();
    14. System.out.format("宠物名字:%s\n",dog.getName());
    15. dog.call();
    16. dog.pat();
    17. } else if (c == 'c') {
    18. System.out.println("撸猫\n-----------\n");
    19. Cat cat = new Cat();
    20. System.out.format("宠物名字:%s\n",cat.getName());
    21. cat.call();
    22. cat.pat();
    23. Garfield garfield = new Garfield();
    24. System.out.format("宠物名字:%s\n",garfield.getName());
    25. garfield.call();
    26. garfield.pat();
    27. garfield.speak();
    28. } else if (c == 'p') {
    29. System.out.println("撸鹦鹉\n-----------\n");
    30. Parrot parrot = new Parrot();
    31. System.out.format("宠物名字:%s\n",parrot.getName());
    32. parrot.call();
    33. parrot.pat();
    34. } else if (c == 'b') {
    35. System.out.println("撸黄金莽\n-----------\n");
    36. GoldBoa boa = new GoldBoa();
    37. System.out.format("宠物名字:%s\n",boa.getName());
    38. boa.call();
    39. boa.pat();
    40. } else if (c != 'q') {
    41. System.out.println("所先宠物不存在\n");
    42. }
    43. }
    44. }
    45. }

     运行效果:

     匿名类

    如果嫌定义内部类麻烦的话,也可以使用匿名类,使代码更紧凑一点,狗狗的代码变成了这样:

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog extends Pet {
    6. public Dog() {
    7. this("汪财");
    8. }
    9. public Dog(String dogName) {
    10. super(dogName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "汪汪!";
    15. }
    16. @Override
    17. public void pat() {
    18. Thread daze = new Thread(new Runnable() {
    19. @Override
    20. public void run() {
    21. try {
    22. System.out.println("先发个呆!");
    23. Thread.sleep(3000);
    24. System.out.println("3秒后......");
    25. System.out.println(response());
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. });
    31. daze.start();
    32. }
    33. }

     运行效果同上。

    局部类

    宠物养了就是玩的,有时候会让宠物表演,当然不是真的表演,而随机做一些动物,我们可以定义一个类来存放宠物表演时的状态,但表演也是经常性的,而且仅仅是表演时用到,所以不必定义一个全局类来表示这些动作,也不用定义内部类,别的地方用不到,因而可以直接在方法内定义一个局部类,以狗狗为例:

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog extends Pet {
    6. public Dog() {
    7. this("汪财");
    8. }
    9. public Dog(String dogName) {
    10. super(dogName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "汪汪!";
    15. }
    16. public void performence() {
    17. class Actions {
    18. public int times = 0;
    19. public void bark() {
    20. System.out.println("汪汪!汪汪汪!");
    21. }
    22. public void sit() {
    23. System.out.println("坐下了!");
    24. }
    25. public void jump() {
    26. System.out.println("上屋顶了!");
    27. }
    28. }
    29. System.out.println("----------开始表演--------------");
    30. Actions actions = new Actions();
    31. while (actions.times < 5) {
    32. int order = (int) Math.ceil(Math.random() * 3);
    33. switch (order) {
    34. case 1:
    35. actions.bark();
    36. break;
    37. case 2:
    38. actions.sit();
    39. case 3:
    40. actions.jump();
    41. }
    42. actions.times++;
    43. }
    44. System.out.println("----------表演结束--------------");
    45. }
    46. @Override
    47. public void pat() {
    48. Thread daze = new Thread(new Runnable() {
    49. @Override
    50. public void run() {
    51. try {
    52. System.out.println("先发个呆!");
    53. Thread.sleep(3000);
    54. System.out.println("3秒后......");
    55. System.out.println(response());
    56. } catch (InterruptedException e) {
    57. e.printStackTrace();
    58. }
    59. }
    60. });
    61. daze.start();
    62. }
    63. }

     运行效果:

     总结

    类是用来分组代码功能的,有时候会有一些使用范围比较小的代码,而又要对其按功能分组,就可以使用上述的实现方法。

    一些细节

    静态成员

    本文刚开始的时候,定义的类成员都是加了static的,static表示程序一运行,这些代码就可以直接使用的,仅仅加上类名作前缀即可。如果不加就要先做new的这个动作,才能使用。也就是static与非static是两个不同的东西。因为先有static,所以在new出来的对象实例中使用static成员,反之则不能,很可能实例还没创建,没东西可用,即使他们在一个类里边。

    静态类

    顶层类不能加static修饰,但内部类却可以。

    Lamda

    匿名类有些情况下可以使用Lamda来表达,这样可以使代码更加紧凑。比如前边狗狗被的反应可以变成这样(Thead类的构造方法参数):

    1. /**
    2. * 狗狗
    3. *
    4. */
    5. public class Dog extends Pet {
    6. public Dog() {
    7. this("汪财");
    8. }
    9. public Dog(String dogName) {
    10. super(dogName);
    11. }
    12. @Override
    13. protected String response() {
    14. return "汪汪!";
    15. }
    16. public void performence() {
    17. class Actions {
    18. public int times = 0;
    19. public void bark() {
    20. System.out.println("汪汪!汪汪汪!");
    21. }
    22. public void sit() {
    23. System.out.println("坐下了!");
    24. }
    25. public void jump() {
    26. System.out.println("上屋顶了!");
    27. }
    28. }
    29. System.out.println("----------开始表演--------------");
    30. Actions actions = new Actions();
    31. while (actions.times < 5) {
    32. int order = (int) Math.ceil(Math.random() * 3);
    33. switch (order) {
    34. case 1:
    35. actions.bark();
    36. break;
    37. case 2:
    38. actions.sit();
    39. case 3:
    40. actions.jump();
    41. }
    42. actions.times++;
    43. }
    44. System.out.println("----------表演结束--------------");
    45. }
    46. @Override
    47. public void pat() {
    48. Thread daze = new Thread(() -> {
    49. try {
    50. System.out.println("先发个呆!");
    51. Thread.sleep(3000);
    52. System.out.println("3秒后......");
    53. System.out.println(response());
    54. } catch (InterruptedException e) {
    55. e.printStackTrace();
    56. }
    57. });
    58. daze.start();
    59. }
    60. }

    用Lamda只要写run()中的代码就可以了。

    总结

    概念这东西,很多时候是用的一些新造的词来描述的,Java是一门面向对象的语言,面向对象这东西从字面上并不是很好理解。封装是什么?继承是什么?多态是什么?并不太好用自然语言来表达,多数解释也只是说明了其意图,并不能解释是如何实现的。其实代码写多了,也就那么回事。

  • 相关阅读:
    题目0156-计算网络信号
    【机器学习-周志华】学习笔记-第一章
    Servlet基础(1)
    从Docker Hub获取镜像和创建容器
    1039 Course List for Student
    COCO数据集中图像的caption读取到txt文件
    Roson的Qt之旅#109 QML ListView的成员属性介绍
    Vue.js 通过举一反三建立企业级组件库
    SpringBoot - WebMvcConfigurer的作用是什么?
    你能懂的 Reflect 反射
  • 原文地址:https://blog.csdn.net/icoolno1/article/details/122909137