1、 面向对象基本概念

一、 什么是面向对象:

  1. 面向对象是一种编程思想。
  2. 面向对象是一种思考问题的思维方式。

二、 建立面向对象思维方式:

  1. 先整体, 再局部
  2. 先抽象, 再具体
  3. 能做什么, 再怎么做

三、 如何学习面向对象:

  1. 掌握一门面向对象语言的语法
  2. 熟悉面向对象的设计原则
  3. 熟悉面向对象设计模式

2、 类与对象

一、什么是类

  1. 类是: 分类、 类别。
  2. 通过分类, 我们可以区别不同的事物种类, 在日常生活当中, 我们常常这样做。
  3. 所以, 类是一组具有相同特性(属性) 与行为(方法) 的事物集合

二、类与对象的关系

类表示一个共性的产物, 是一个综合的特征, 而对象, 是一个个性的产物, 是一个个体的特征。

类由属性和方法组成:

  1. 属性: 就相当于一个个的特征
  2. 方法: 就相当于人的一个个的行为, 例如: 说话、 吃饭、 唱歌、 睡觉

3、 类和对象的定义格式

在Java中可以使用以下的语句定义一个类:

  1. class 类名称{
  2. 属性名称;
  3. 返回值类型 方法名称(){
  4. }
  5. }

对象的定义:

一个类要想真正的进行操作, 则必须依靠对象, 对象的定义格式如下:

  1. 类名称 对象名称 = new 类名称() ;

按照以上的格式就可以产生对象了。

如果要想访问类中的属性或方法(方法的定义),

则可以依靠以下的语法形式:

访问类中的属性:

  1. 对象.属性 ;

调用类中的方法:

  1. 对象.方法() ;

在java中对象声明有两种含义

声明对象:

  1. Horse horse= null;

// 表示声明了一个对象, 但是此对象无法使用, horse没有具体的内存指向

实例化对象:

  1. horse= new Horse() ;

// 表示实例化了对象, 可以使用

通过对象调用方法:

  1. horse.eat()

匿名对象调用方法:

  1. new Horse().eat()

4、对象内存分析

  1. new 关键字表示创建一个对象
  2. new 关键字表示实例化对象
  3. new 关键字表示申请内存空间

注意: 如果使用一个没有申请内存空间的对象, 会报空指针异常:

java.lang.NullPointerException

对象在内存中的结构:

4、面向对象(上) - 图1

给对象的属性赋值:

horse.name=”小白”; horse.age=4;

4、面向对象(上) - 图2

在内存中创建多个对象:

Horse horse1=null; horse1=new Horse();

Horse horse2=null; horse2=new Horse();

4、面向对象(上) - 图3

分别给两个对象的属性赋值:

horse1.name=”小白”; horse2.name=”小黑”;

horse1.age=4; horse2.age=5;

4、面向对象(上) - 图4

声明两个对象, 一个实例化, 一个没实例化

Horse horse1=null; horse1=new Horse();

Horse horse2=null;

4、面向对象(上) - 图5

对象之间的赋值

horse1.name=”小白”; horse2=horse1;

horse1.age=4; horse2.name=”小黑”;

4、面向对象(上) - 图6

horse1.name=”小白”; horse2=horse1;

horse1.age=4; horse2.name=”黑黑”;

horse2.name=”小黑”;

4、面向对象(上) - 图7

类与对象小结

  1. new关键字: 表示向内存申请空间, 也表示实例化一个对象, 创建一个对象。
  2. 一个对象在内存中的大小, 由该对象的所有属性所占的内存大小的总和。 引用类型变量在32位系统上占4个字节, 在64位系统上占8个字节。 加上而外的对象隐性数据所占的大。
  3. 相同的类型才可以赋值
  4. 不同的引用, 指向同一个对象, 任何一个引用改变对象的值, 其它引用都会反映出来。
  5. 编程时要注意的问题, 在确定不使用对象时, 要尽早释放对象: 引用=null
  6. 当一个堆中的对象没有被任何引用变量所指向时, 该对象会被JVM 的 GC 程序认为是垃圾对象, 从而被回收。

5、 封装性

一、 封装性的概念

  1. 封装性是面向对象思想的三大特征之一。
  2. 封装就是隐藏实现细节, 仅对外提供访问接口。

封装有:

属性的封装、 方法的封装、 类的封装、 组件的封装、 模块化封装、 系统级封装…

二、 封装的好处

  1. 模块化
  2. 信息隐藏
  3. 代码重用
  4. 插件化易于调试
  5. 具有安全性

三、封装缺点:

会影响执行效率

封装之前:

  1. class Person{
  2. String name;
  3. int age;
  4. }

封装之后:

  1. class Person{
  2. //属性是成员变量
  3. private String name;
  4. private int age;
  5. //参数及方法内定义的变量是局部变量
  6. public void setName(String name){
  7. this.name = name;
  8. }
  9. public String getName(){
  10. return name;
  11. }
  12. }

成员变量和局部变量

1、 在类中的位置不同

  • 成员变量: 在类中定义
  • 局部变量: 在方法中定义或者方法的参数

2、 在内存中的位置不同

  • 成员变量: 在堆内存(成员变量属于对象, 对象进堆内存)
  • 局部变量: 在栈内存(局部变量属于方法, 方法进栈内存)

3、 生命周期不同

  • 成员变量: 随着对象的创建而存在, 随着对象的销毁而消失
  • 局部变量: 随着方法的调用而存在, 随着方法的调用完毕而消失

4、 初始化值不同

  • 成员变量: 有默认初始化值, 引用类型默认为null
  • 局部变量: 没有默认初始化值, 必须定义, 赋值, 然后才能使用

注意:

局部变量名称可以和成员变量名称一样, 在方法中使用的时候, 采用的是就近原则。


6、 构造方法

什么是构造方法

  1. 构造方法就是类构造对象时调用的方法, 用于对象的初始化工作
  2. 构造方法是实例化一个类的对象时, 也就是new 的时候, 最先调用的方法。

构造方法的定义:

构造方法是在类中定义的, 构造方法的定义格式: 方法名称与类名称相同, 无返回值类型的声明。

对象的实例化语法:

  1. Dog dog = new Dog();

//new Dog后面有个括号, 带括号表示调用了方法, 此时调用的方法就是构造方法了

构造方法重载:

无参构造方法:

  1. public Dog(){}

带一个参数的构造方法:

  1. public Dog(String name){
  2. this.name = name;
  3. }

带多个参数的构造方法:

  1. public Dog(String name,int age){
  2. this.name = name;
  3. this.age = age;
  4. }

构造方法小结

  1. 构造方法名称与类名相同, 没有返回值声明(包括 void)
  2. 构造方法用于初始化数据(属性)
  3. 每一个类中都会有一个默认的无参的构造方法
  4. 如果类中有显示的构造方法, 那么默认构造方法将无效
  5. 如果有显示的构造方法, 还想保留默认构造 方法, 需要显示的写出来。
  6. 构造方法可以有多个, 但参数不一样, 称为构造方法的重载
  7. 在构造方法中调用另一个构造方法, 使用this(…), 该句代码必须在第一句。
  8. 构造方法之间的调用, 必须要有出口。
  9. 给对象初始化数据可以使用构造方法或setter方法, 通常情况下, 两者都会保留。
  10. 一个好的编程习惯是要保留默认的构造方法。 (为了方便一些框架代码使用反射来创建对象)
  11. private Dog(){}, 构造方法私有化, 当我们的需求是为了 保正该类只有一个对象时。
    1. 什么时候一个类只需要一个对象? 比如, 工具类(没有属性的类, 只有行为) 并且该工具对象被频繁使用。
    2. 权衡只用一个对象与产生多个对象的内存使用, 来确定该类是否要定义为只需要一个对象。

7、 this关键字

在Java基础中, this关键字是一个最重要的概念。 使用this关键字可以完成以下的操作:

  • 调用类中的属性
  • 调用类中的方法或构造方法
  • 表示当前对象
  1. /**
  2. this关键字
  3. 调用类中的属性
  4. · 调用类中的方法或构造方法
  5. · 表示当前对象:在方法被调用的过程中,哪个对象调用了方法,在方法内的this就表示谁
  6. 类名.this
  7. */
  8. public class Test4{
  9. public static void main(String[] args){
  10. Cat cat = new Cat();
  11. cat.setName("咪咪");
  12. cat.setAge(3);
  13. cat.eat();
  14. }
  15. }
  16. class Cat{
  17. private String name;
  18. private int age;
  19. public void setName(String name){ //name=咪咪
  20. this.name = name; //this代表的是当前对象
  21. }
  22. public String getName(){
  23. return name;
  24. }
  25. public void setAge(int age){
  26. this.age = age;
  27. }
  28. public int getAge(){
  29. return age;
  30. }
  31. public void eat(){
  32. //在方法中使用this调用类中的其它方法或属性,this可以省略,this前面可以使用当前的类名.this
  33. //在后面的内部类知识点中会涉及到
  34. System.out.println("我是"+Cat.this.getName()+",我爱吃鱼");
  35. System.out.println("我是"+getName()+",我爱吃鱼");
  36. System.out.println("我是"+this.name+",我爱吃鱼");
  37. /*
  38. 我是咪咪,我爱吃鱼
  39. 我是咪咪,我爱吃鱼
  40. 我是咪咪,我爱吃鱼
  41. */
  42. }
  43. }

8、 值传递与引用传递

示例1:值传递

  1. public class Test19 {
  2. public static void main(String[] args){
  3. int x = 10;
  4. method(x) ;
  5. System.out.println("x="+x) ; //x=10
  6. }
  7. public static void method(int mx){
  8. mx = 20;
  9. }
  10. }

示例2:引用传递

  1. public class Test19 {
  2. public static void main(String[] args){
  3. Duck d = new Duck();
  4. method(d) ;
  5. System.out.println("Duck age ="+d.age) ;//Duck age =5
  6. }
  7. public static void method(Duck duck){
  8. duck.age = 5;
  9. }
  10. }
  11. class Duck{
  12. int age = 2; //省略封装
  13. }

示例3:String传递

  1. public class Test19 {
  2. public static void main(String[] args){
  3. String name = "小飞";
  4. method(name) ;
  5. System.out.println("name="+name) ;//name=小飞
  6. }
  7. public static void method(String sname){
  8. sname = "小备";
  9. }
  10. }

示例4: String传递

  1. public class Test19 {
  2. public static void main(String[] args){
  3. Person p = new Person();
  4. method(p) ;
  5. System.out.println("person name= "+p.name) ;//person name= 备备
  6. }
  7. public static void method(Person p ){
  8. p.name = "备备";
  9. }
  10. }
  11. class Person{
  12. String name = "飞飞"; //省略封装
  13. }

9、 对象的一对一关系

两个对象之间的一对一关系:

比如:一个英雄(Hero) 对一个兵器(Weapon)

  1. /**
  2. 两个对象之间的一对一关系:
  3. 比如:一个英雄(Hero)对一个兵器(Weapon)
  4. 双向一对一
  5. 单向一对一
  6. */
  7. public class Test5{
  8. public static void main(String[] args){
  9. Hero hero = new Hero("刘备",300);
  10. Weapon weapon = new Weapon("双股剑",3);
  11. //把两个对象关联起来
  12. hero.setWeapon(weapon);
  13. weapon.setHero(hero);
  14. //通过英雄来获取他的信息
  15. String name = hero.getName();
  16. int age = hero.getAge();
  17. Weapon w = hero.getWeapon();
  18. System.out.println("我是"+name+",我"+age+"岁,我的武器是:"+w.getName()+",排行"+w.getGrade()+"级");
  19. }
  20. }
  21. //英雄类
  22. class Hero{
  23. private String name;
  24. private int age;
  25. private Weapon weapon;//一对一关系
  26. public void setWeapon(Weapon weapon){
  27. this.weapon = weapon;
  28. }
  29. public Weapon getWeapon(){
  30. return weapon;
  31. }
  32. public Hero(){}
  33. public Hero(String name,int age){
  34. this.name = name;
  35. this.age = age;
  36. }
  37. public void setName(String name){
  38. this.name = name;
  39. }
  40. public String getName(){
  41. return name;
  42. }
  43. public void setAge(int age){
  44. this.age = age;
  45. }
  46. public int getAge(){
  47. return age;
  48. }
  49. }
  50. //兵器类
  51. class Weapon{
  52. private String name;
  53. private int grade;
  54. private Hero hero;
  55. public void setHero(Hero hero){
  56. this.hero = hero;
  57. }
  58. public Hero getHero(){
  59. return hero;
  60. }
  61. public Weapon(){}
  62. public Weapon(String name,int grade){
  63. this.name = name;
  64. this.grade = grade;
  65. }
  66. public void setName(String name){
  67. this.name = name;
  68. }
  69. public String getName(){
  70. return name;
  71. }
  72. public void setGrade(int grade){
  73. this.grade = grade;
  74. }
  75. public int getGrade(){
  76. return grade;
  77. }
  78. }

10、 static关键字

static关键字的作用:

  1. 使用static关键字修饰一个属性
    声明为static的变量实质上就是全局变量
  2. 使用static关键字修饰一个方法
    通常, 在一个类中定义一个方法为static, 那就是说, 无需本类的对象即可调用此方法
  3. 使用static关键字修饰一个类(内部类)

class文件加载到内存的结构图:

4、面向对象(上) - 图8

声明为static的方法有以下几条限制:

  • 它们仅能调用其他的static 方法。
  • 它们只能访问static数据。
  • 它们不能以任何方式引用this 或super。

什么时候使用static?

所有对象共同的属性或方法, 那么我们可以定义为静态的。

  1. /**
  2. static 关键字
  3. 1、静态变量或方法不属于对象,依赖类
  4. 2、静态变量是全局变量,生命周期从类被加载后一直到程序结束
  5. 3、静态变量只有存一份,在静态方法区中存储
  6. 4、静态变量是本类所有对象共享一份
  7. 5、建议不要使用对象名去调用静态数据,直接使用类名调用
  8. 6、static修饰一个方法,那么该方法属于类,不属于对象,直接用类名调用
  9. 7、静态方法不能访问非静态属性和方法,只能访问静态。
  10. */
  11. public class Test6{
  12. public static void main(String[] args){
  13. System.out.println(args.length);
  14. for(String s: args){
  15. System.out.println(s);
  16. }
  17. //Role beibei = new Role("刘备","蜀国");
  18. //Role yunchang = new Role("云长","蜀国");
  19. //Role feifei = new Role("张飞","蜀国");
  20. Role beibei = new Role("刘备");
  21. Role yunchang = new Role("云长");
  22. Role feifei = new Role("张飞");
  23. System.out.println(beibei.getInfo());
  24. System.out.println(yunchang.getInfo());
  25. System.out.println(feifei.getInfo());
  26. System.out.println(beibei.country);
  27. System.out.println(yunchang.country);
  28. System.out.println(feifei.country);
  29. beibei.country = "魏国";//尽量不用对象调用静态变量
  30. System.out.println("------------------");
  31. System.out.println(beibei.country);
  32. System.out.println(yunchang.country);
  33. System.out.println(feifei.country);
  34. System.out.println("------------------");
  35. //用类名调用静态变量
  36. System.out.println(Role.country);
  37. System.out.println("------------------");
  38. Role.setCountry("吴国");
  39. System.out.println(beibei.country);
  40. }
  41. }
  42. //角色
  43. class Role{
  44. private String name;
  45. static String country = "蜀国";//静态变量(全局变量)
  46. public Role(String name,String country){
  47. this.name = name;
  48. this.country = country;
  49. }
  50. public Role(String name){
  51. this.name = name;
  52. }
  53. public void setName(String name){
  54. this.name = name;
  55. }
  56. public String getName(){
  57. return name;
  58. }
  59. //静态方法不能访问非静态的数据
  60. public static void setCountry(String country){
  61. Role.country = country;
  62. }
  63. /*
  64. public void setCountry(String country){
  65. this.country = country;
  66. }
  67. public String getCountry(){
  68. return country;
  69. }
  70. */
  71. public String getInfo(){
  72. return "name="+name+",country="+country;
  73. }
  74. }
  75. /*
  76. 0
  77. name=刘备,country=蜀国
  78. name=云长,country=蜀国
  79. name=张飞,country=蜀国
  80. 蜀国
  81. 蜀国
  82. 蜀国
  83. ------------------
  84. 魏国
  85. 魏国
  86. 魏国
  87. ------------------
  88. 魏国
  89. ------------------
  90. 吴国
  91. */

11、 main方法分析

主方法:

  1. public static void main(String[] args){
  2. //代码块
  3. }
  • public: 公有的, 最大的访问权限
  • static: 静态的, 无需创建对象
  • void:: 表示没有返回值, 无需向JVM返回结果
  • main: 方法名, 固定的方法名
  • String[] args: 表示参数为字符串数组, 可以在调用方法时传入参数

12、代码块

1、 普通代码块

直接写在方法中的代码块就是普通代码块

示例:

  1. public class Demo1{
  2. public static void main(String []args){
  3. {// 普通代码块
  4. String info = “局部变量-1 ;
  5. System.out.println(info) ;
  6. }
  7. String info = “局部变量-2 ;
  8. System.out.println(info) ;
  9. }
  10. }

2、 构造块是在类中定义的代码块

示例:

  1. class Demo{
  2. { // 构造块
  3. System.out.println("构造块") ;
  4. }
  5. public Demo(){
  6. System.out.println("构造方法。 ") ;
  7. }
  8. }

3、 在类中使用static声明的代码块称为静态代码块

示例:

  1. class Demo{
  2. {
  3. System.out.println("构造块") ;
  4. }
  5. static{
  6. System.out.println("静态代码块") ;
  7. }
  8. public Demo(){
  9. System.out.println("构造方法。 ") ;
  10. }
  11. }

4、 同步代码块(多线程中讲解)


13、 单例设计模式

单例设计模式: 保证一个类仅有一个实例, 并提供一个访问它的全局访问点。

  1. 构造方法私有化
  2. 声明一个本类对象
  3. 给外部提供一个静态方法获取对象实例

在项目中为什么要使用单例,单例有什么好处?

  1. 在设计一些工具类的时候(通常工具类,只有功能方法,没有属性)
  2. 工具类可能会被频繁调用
  3. 目的是为了节省重复创建对象所带来的内存消耗,从而来提高效率

两种实现方式:

  1. 饿汉式
  2. 懒汉式
  1. public class Test19 {
  2. public static void main(String[] args){
  3. HungryMan hungryMan=HungryMan.getInstance();
  4. System.out.println(hungryMan.getClass().toString());//class HungryMan
  5. LazyMan lazyMan=LazyMan.getInstance();
  6. System.out.println(lazyMan.getClass().toString());//class LazyMan
  7. }
  8. }
  9. //单例模式,构造方法私有化
  10. //饿汉式单例:一上来就构造方法,缺点是容易占用过多内存
  11. class HungryMan{
  12. private HungryMan(){}
  13. private static HungryMan hungryMan = new HungryMan();
  14. public static HungryMan getInstance(){
  15. return hungryMan;
  16. }
  17. }
  18. //懒汉式单例:需要的时候再构造
  19. class LazyMan{
  20. private LazyMan(){}
  21. private static LazyMan lazyMan;
  22. public static LazyMan getInstance(){
  23. if(lazyMan==null){
  24. lazyMan=new LazyMan();
  25. }
  26. return lazyMan;
  27. }
  28. }
  29. //双重检测锁模式 懒汉单例
  30. class DCLLazyMan{
  31. private DCLLazyMan(){}
  32. private volatile static DCLLazyMan lazyMan;
  33. public static DCLLazyMan getInstance(){
  34. if(lazyMan==null){
  35. synchronized (DCLLazyMan.class){
  36. if(lazyMan==null){
  37. lazyMan=new DCLLazyMan(); //不是一个原子性操作
  38. /*
  39. 1、会分配一个内存空间
  40. 2、执行构造方法,初始化对象
  41. 3、把这个对象指向这个空间
  42. */
  43. }
  44. }
  45. }
  46. return lazyMan;
  47. }
  48. }
  49. //静态内部类实现
  50. class Holder{
  51. private Holder(){}
  52. public static Holder getInstance(){
  53. return InnerClass.holder;
  54. }
  55. public static class InnerClass{
  56. private static final Holder holder=new Holder();
  57. }
  58. }

14、 对象数组与管理

对象数组就是数组里的每个元素都是类的对象, 赋值时先定义对象, 然后将对象直接赋给数组。

示例:

  1. Chicken[] cs= new Chicken[10];

使用对象数组实现多个Chicken的管理。

数组的动态管理,里面用到了计数器:count,很有意思

但要实现单纯的增加和删除,可以查看上一节。

  1. /**
  2. 对象数组与管理
  3. 使用对象数组实现多个Chicken的管理。
  4. 动态数组:
  5. 1、数组是一种线性数据结构
  6. 2、数组不适合作删除插入等操作,适合添加,查找,遍历
  7. */
  8. import java.util.Arrays;
  9. public class Test9{
  10. public static void main(String[] args){
  11. ChickenManager cm = new ChickenManager(5);
  12. //添加
  13. cm.add(new Chicken(1,"小小",10));
  14. cm.add(new Chicken(2,"小二",8));
  15. cm.add(new Chicken(3,"小三",6));
  16. cm.add(new Chicken(4,"小红",2));
  17. cm.add(new Chicken(5,"小奋",4));
  18. cm.add(new Chicken(6,"小斗",1));
  19. System.out.println("数组的长度是:"+cm.length());
  20. System.out.println("-------printAll----------");
  21. cm.printAll();
  22. System.out.println("-------find----------");
  23. Chicken c = cm.find(6);
  24. c.print();
  25. System.out.println("-------update----------");
  26. cm.update(new Chicken(1,"下蛋公鸡",20));
  27. cm.printAll();
  28. }
  29. }
  30. //小鸡管理类
  31. class ChickenManager{
  32. private Chicken[] cs = null;
  33. private int count = 0;//记录当前数组的元素个数(下标)
  34. public ChickenManager(int size){
  35. if(size>0){
  36. cs = new Chicken[size];
  37. }else{
  38. cs = new Chicken[5];
  39. }
  40. }
  41. public int length(){
  42. return cs.length;
  43. }
  44. //添加:实现动态数组
  45. public void add(Chicken c){
  46. if(count>=cs.length){//数组已满,需要扩充
  47. //算法1:扩充原来数组大小的一半 cs.length*3/2+1
  48. //算法2:扩充原来数组的一倍 cs.length*2
  49. int newLen = cs.length*2;
  50. cs = Arrays.copyOf(cs,newLen);
  51. }
  52. cs[count] = c;
  53. count++;
  54. }
  55. //删除
  56. public void delete(int id){
  57. for(int i=0;i<count;i++){
  58. if(cs[i].getId()==id){
  59. //找到了要删除的对象,把该对象之后的对象向前移动一位
  60. for(int j=i;j<count-1;j++){
  61. cs[j] = cs[j+1];
  62. }
  63. //把最后一个对象赋值为空(删除)
  64. cs[count-1] = null;
  65. count--;//下标减一
  66. break;
  67. }
  68. }
  69. }
  70. //更新
  71. public void update(Chicken c){
  72. Chicken temp = find(c.getId());
  73. if(temp!=null){
  74. temp.setName(c.getName());
  75. temp.setAge(c.getAge());
  76. }
  77. }
  78. //查找
  79. public Chicken find(int id){
  80. for(int i=0;i<count;i++){
  81. if(cs[i].getId()==id){
  82. return cs[i];
  83. }
  84. }
  85. return null;
  86. }
  87. //输出所有
  88. public void printAll(){
  89. for(int i=0;i<count;i++){
  90. cs[i].print();
  91. }
  92. }
  93. }
  94. //小鸡类(数据对象) value object (VO)
  95. class Chicken{
  96. private int id;
  97. private String name;
  98. private int age;
  99. public Chicken(){}//一般情况下最好保留默认的构造方法
  100. public Chicken(int id,String name,int age){
  101. this.id = id;
  102. this.name = name;
  103. this.age = age;
  104. }
  105. public void setId(int id){
  106. this.id = id;
  107. }
  108. public int getId(){
  109. return id;
  110. }
  111. public void setName(String name){
  112. this.name = name;
  113. }
  114. public String getName(){
  115. return name;
  116. }
  117. public void setAge(int age){
  118. this.age = age;
  119. }
  120. public int getAge(){
  121. return age;
  122. }
  123. public void print(){
  124. System.out.println("id="+id+",name="+name+",age="+age);
  125. }
  126. }
  127. /*
  128. 数组的长度是:10
  129. -------printAll----------
  130. id=1,name=小小,age=10
  131. id=2,name=小二,age=8
  132. id=3,name=小三,age=6
  133. id=4,name=小红,age=2
  134. id=5,name=小奋,age=4
  135. id=6,name=小斗,age=1
  136. -------find----------
  137. id=6,name=小斗,age=1
  138. -------update----------
  139. id=1,name=下蛋公鸡,age=20
  140. id=2,name=小二,age=8
  141. id=3,name=小三,age=6
  142. id=4,name=小红,age=2
  143. id=5,name=小奋,age=4
  144. id=6,name=小斗,age=1
  145. Process finished with exit code 0
  146. */