head first java读书笔记

head first java读书笔记

1. 基本信息

  • 页数:689
  • 阅读起止日期:20170104-20170215

2. 标签

  • Java入门

3. 价值

  • 8分

4. 主题

使用面向对象的思路介绍Java的基础知识,从对象的基本概念、变量、方法,到函数库,集成与多态,静态,再到GUI,序列化,网络,数据结构,最后介绍发布和远程调用。

5. 纲要

  1. Java的基本介绍-第1章
  2. 对象的基本介绍(变量与方法)-第2-5章
  3. 函数库-第6章
  4. 对象的深入-继承、多态、构造函数-第7-9章
  5. 静态的变量或方法-第10章
  6. 异常处理-第11章
  7. GUI-第12-13章
  8. 序列化与IO-第14章
  9. 网络与线程-第15章
  10. 常用数据结构-第16章
  11. 包与发布程序-第17章
  12. 远程过程调用、servlets等-第18章
  13. 附录

6. 点评

本书最大的启发是建立面向对象的基本思想,万物皆在对象中,到底是如何组成和实现的。

7. 摘录

7.1 Java的基本介绍

  1. Java程序包括:源代码、编译器、输出(class文件)、JVM;
  2. 最常用的java

     public class ClassName {
         public static void main(String[] args){
             System.out.println("Hello World!");
         }
     }
  3. Java不能像C一样使用整型做测试条件
  4. (int)(Math.random()*length)来出现随机数;

7.2 对象的基本介绍

  1. 对象本身已知的事物被称为实例变量,对象可以执行的动作被称为方法;
  2. 类是对象的蓝图;
  3. main的两种用途:测试真正的类,启动你的java应用程序;
  4. Java的程序在执行期是一组会互相交谈的对象;
  5. 变量有两种类型primitive主数据类型和引用;
  6. primitive主数据类型:boolean, char(0~65535), byte(-128~127), short(-32768~32767), int, long, float, double。
  7. 没有对象变量,只有引用到对象的变量;引用变量更像是对象的遥控器;
  8. 没有引用到任何对象的引用变量的值为null;
  9. 如果堆上的对象没有任何引用变量,便会被回收;
  10. 数组就像杯架,数组变量就是数组对象的遥控器;
  11. 将实例变量标记为private,将getter和setter标记为public;
  12. 实例变量永远都会有默认值,引用变量的默认值是null;局部变量没有默认值。
  13. 程序设计的方法:找出类应该做的事情,列出实例变量和方法,编写伪码,编写测试用的程序,实现类,测试方法,排错或重新设计。
  14. 伪码描述要做什么事情,而不是如何做。
  15. Interger.parseInt()只会在所给的String为数字时有用。

7.3 API

  1. ArrayList listName = new ArrayList();
  2. Java的API中,类是包含在包中的。使用时,必须import 类全名或直接打出去全名。除非是来自于java.lang这个包中,比如System, String, Math等。

7.4 对象深入

  1. extends表示继承,继承搜索方法时,会从底层向祖先搜索;
  2. 继承是是一个,实例变量是有一个;
  3. public会被继承,private不会被继承。
  4. IS-A是单方向的;
  5. 继承的意义有二:避免了重复的代码;定义出共同的协议;
  6. 多态为声明父类的对象引用指向子类的对象;
  7. 通过多态,可以写出即使引进新型子类时,也不必修改的程序;
  8. 继承虽然没有层次的限制,但一般不会超过2层;
  9. 标识出final的类可以确保方法都是自己需要的版本,final也可以防止特定的方法被覆盖,只要加在方法前;
  10. 覆盖父类方法需要满足两个条件:参数必须要一样,且返回类型要兼容;不能降低方法的存取权限;
  11. abstract表示抽象,抽象类不会被初始化;
  12. abstract可以放在方法前,直接以分号结尾。抽象方法只能放在抽象类中,所有的抽象方法在子类中必须被实现;
  13. Java中,所有的类都继承自object。object有equals,getClass,hashCode,toString等方法。
  14. 接口是为了解决多重继承的问题出现的,它没有实例变量。像是一个100%的纯抽象类,使用interface定义,所有的方法都是抽象的。其他类用implements来实现接口的方法。类是可以实现多个借口的。
  15. 如果新的类无法对其他类通过IS-A测试,就设计不继承的类;
  16. 只有在需要某类的特殊化版本时,已覆盖或增加新的方法来继承现有的类;
  17. 当你需要定义一群子类的模板,又不想初始化此模板时,使用抽象类;
  18. 如果定义出类可以扮演的角色,使用接口;
  19. 使用super关键字执行父类的方法;
  20. 抽象类可以带有抽象和非抽象的方法;
  21. 方法调用和局部变量在栈空间,所有的对象在堆空间;
  22. 构造函数没有类型,也不会被继承。通常一定要有没有参数的构造函数。如果有了一个有参数的构造函数,则没参数的构造函数必须自己写才会有,否则创建会出错。
  23. 通常状况下,类的构造函数是public。
  24. 构造函数的调用链是自顶向下的,即先运行父类,再运行子类。
  25. 子类的构造函数如果想调用父类,使用super();通常状况下,super会被编译器默认调用没有参数的版本,显示调用的话,必须要把super();放到第一行。
  26. 可以使用this(args)调用重载版的其他的构造函数,例如无参数的构造函数,加个默认值调用有参数的构造函数。this(Color.Red)

7.5 静态的变量或方法

  1. 静态方法不需要创建实例变量就可以调用,像Math.abs(-1);
  2. 如果类只有静态方法,即要限制非抽象类初始化,可以将构造函数设置为private;
  3. 静态方法不能调用非静态的变量或方法,例如在main中调用;但可以调用静态变量或方法。
  4. 类中静态变量的值,对所有实例都相同,均是共享的;即实例变量每个实例一个,静态变量每个类一个。
  5. public static final double PI=3.1415public表示可供各方读取,static表示不用创建实例即可使用,final表示不变;静态final变量默认取名大写,且必须初始化。
  6. final的变量代表不能改变,final的方法不能被覆盖,final的类不能被继承;
  7. 使用类似ArrayList时,要用到对主类型数据的封装。因为ArrayList设计成只接受类和对象。Java5.0之前,int不能直接加入ArrayList,5.0之后可以了,因为加入了autoboxing。Integer iWarp = new Integer(i); int unWrapped = iWrap.intValue();利用上述解包装;
  8. 主数据类型的静态方法:Integer.parseInt("2"), new Boolean("true").booleanValue();Double.toString(d);
  9. 格式化输出String.format("%,d",1000);
  10. 日期的格式化输出:%tc 完整的日期和时间,%tr是时间,%tA %tB %td 分别是星期,月份和日期。如果不想重复输入参数,可以使用String.format("%tA, %
  11. java.util.Calendar对象来操作日期

    Calendar cal = Calendar.getInstance();
    cal.set(2017,1,6,15,40);
    cal.getTimeInMillis();
    cal.HOUR_OF_DAY
    cal.add(cal.DATE, 30);//月份滚动
    cal.roll(cal.DATE, 30);//月份不动
    cal.set(cal.DATE, 1);
    //重要的方法
    add(int field, int amount)
    get(int field)
    getInstance()
    getTimeInMillis()
    roll(int field, int amount)
    set(int field, int amount)
    set(year,month,day,hour,minute)
    setTimeInMillis(long millis)
    //关键字段
    DATE / DAY_OF_MONTH
    HOUR / HOUR_OF_DAY
    MILLISECOND
    MINUTE
    MONTH
    YEAR
    ZONE_OFFSET
  12. import static java.lang.System.out;静态的import可以到方法,然后直接调用out即可,不建议使用;

7.6 异常处理

  1. Java通过throws语句来告诉你所有的异常行为;把有风险的程序放在try块中,用catch块摆放异常的处理程序;异常时Exception类型的对象;
  2. 会抛出异常的方法必须要声明它有可能会这么做。方法可以抓住其他异常,异常总会丢回给调用方;

     public void takeRisk() throws BadException{
         if (abandonAllHope){
             throw new BadException();
         }
     }
     public void crossFingers(){
         try{
             anObject.takeRisk();
         }catch (BadException ex){
             System.out.println("Aaargh");
             ex.printStackTrace();
         }
     }
  3. try/catch块用来处理真正的异常,而不是程序的逻辑错误。
  4. 开发与测试期间发生RuntimeException是不会受编译器关于是否声明它会抛出RuntimeException的检查的,也不会管调用方是否认识到该异常;
  5. 无论成功或者失败都要运行的放在finally中;即使try和catch都有return也一样。
  6. throws可以抛出多个异常。throws Exception1, Exception2;catch也可以用类似switch语句一样来分别处理,但是要从小到大;
  7. 异常也可以是多态的,可以用所抛出的异常父型来catch异常。即可以用Exception ex来catch异常,但是不推荐这么做;
  8. duck异常表示,当调用有异常的方法,也声明会抛出异常时,可以不把此方法的调用放在try/catch块中,但是不推荐这么做。

7.7 GUI相关

  1. GUI的流程是创建frame,创建widget,加在widget,显示出来。
  2. 监听和事件源之间的沟通通过程序代码调用button.addActionListener(this)来向按钮注册。按钮会在事件发生时,调用注册该接口的方法actionPerformed(theEvent)

     import javax.swing.*;
     import java.awt.event.*;
    
     public class SimpleGui1B implements ActionListener{
         JButton button;
    
         public static void main(String[] args){
             SimpleGui1B gui = new SimpleGui1B();
             gui.go();
         }
         public void go(){
             JFrame frame = new JFrame();
             button = new JButton("click me");
    
             button.addAcitonListener(this);
    
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
             frame.getContentPane().add(button);
    
             frame.setSize(300,300);
             frame.setVisible(true);
         }
    
         public void actionPerformed(ActionEvent event){
             button.setText("I've been clicked!");
         }
     }
  3. frame默认有东西南北中五个位置来放置widget;
  4. 当有多个widget需要监听事件时,用内部类来解决;

     public void go(){
         //...
         labelButton.addActionListener(new LaberlListener());
         colorButton.addActionListener(new ColorListener());
         label = new JLabel("I'm a label")
         //...
     }
     class LaberListener implements ActionListener {
         public void actionPerformed(ActionEvent event){
             labnel.setText("Ouch!")
         }
     }
  5. 不同的事件有不同的对应回调函数,ActionListener对应的回调函数是actionPerformed,通过addActionListener添加。
  6. 每个背景组件都可以有自定义规则的布局管理器。BorderLayout表示五个区域,是frame默认的,FlowLayout表示从左至右,有必要时换行,是panel默认的;BoxLayout以垂直排列;

     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
     panel.add(button);
     frame.getContentPane().add(BorderLayout.North, panel);
  7. JTextField文本框组件,JTextArea可滚动的文本框组件,JCheckBox组件,JList组件均为常用组件。

7.8 序列化与IO

  1. 储存对象的状态可以有两种办法,序列化(自己的Java程序读),纯文本文件(公共格式,其他程序可读)。
  2. 序列化写入的步骤:创建FileOutputStream对应一个文件xxx.ser,有FileStream创建ObjectOutputStream,由ObjectOutputStream写入object,关闭ObjectOutputStream。其中需要注意,能够写入ObjectOutputStream的object必须implements Serializable,但是不需要实现任何方法。

     FileOutputStream fileStream = new FileOutputStream("MyGame.ser");
     ObjectOutputStream os = new ObjectOutputStream(fileStream);
     os.writeObject(characterOne);
     os.close();
  3. 当一个类可以序列化时,必须所有的实例变量都能被序列化,如果某个变量不需要序列化,需要标记transient,例如transient String currentID;
  4. 解序列化步骤,创建FileInputStream读取一个文件,创建ObjectInputStream,读取对象,转换对象类型,关闭。

     FileInputStream fileStream = new FileInputStream("MyGame.ser");
     ObjectInputStream os = new ObjectInputStream(fileStream);
     Object one = os.readObject();
     GameCharacter elf = (GameCharacter) one;
     os.close()
  5. 读写文本文件。注意可以使用缓冲区的方法来减少磁盘IO。使用writer.flush()强制缓冲区内容写入磁盘。

     import java.io.*;
     class WriteAFile{
         public static void main(String[] args){
             try {
                 FileWriter writer = new FileWriter("Foo.txt");
                 writer.write("hello, foo!");
                 //BufferedWriter bWriter = new BufferedWriter(writer);
                 //bWriter.flush();
                 writer.close();
                 File myFile = new File("Foo.txt");
                 FileReader fileReader = new FileReader(myFile);
    
                 BufferedReader reader = new BufferedReader(fileReader);
                 String line = null;
                 while((line = reader.readLine()) != null){
                     System.out.println(line);
                 }
                 reader.close();
             } catch(IOException ex){
                 ex.printStackTrace();
             }
         }
     }
  6. java.io.File类的操作

     //创建出File对象
     File f = new File("MyCode.txt");
     //建目录
     File dir = new File("Chapter_8");
     dir.mkdir()
     //列出目录内容
     if (dir.isDirectory()){
         String[] dirContents = dit.list();
     }
     //取得绝对路径
     dit.getAbsolutePath();
     //删除文件
     boolean isDeleted = f.delete()
  7. 每个对象被序列化的同时,都会带上一个类的版本的识别ID,即serialVersionUID。因此要注意版本。

7.9 网络与线程

  1. 网络接收消息的步骤:建立socket连接,输入到底层的InputStreamReader上,转换到缓冲区字符BufferedReader,读取数据;

     Socket chatSocket = new Socket("127.0.0.1", 5000);
     InputStreamReader stream = new InputStreamReader(chatSocket.getInputStream());
     BufferedReader reader = new BufferedReader(stream);
     String message = reader.readline();
     reader.close();
  2. 写消息的流程是:建立socket,getOutputSream并建立PrintWriter,写入数据;

     Socket chatSocket = new Socket("127.0.0.1", 5000);
     PrintWriter writer = new PringWriter(chatSocket.getOutputStream());
     writer.println("haha");
     writer.close();
  3. 服务器端建立服务的流程为:建立ServerSocket,等待客户端连接,客户连接后,调用accept方法建立新的socket。

     ServerSocket serverSock = new ServerSocket(5000);
     while(true){
         Socket sock = serverSock.accept();
         PrintWriter writer = new PrintWriter(sock.getOutputStream());
         writer.println("haha");
         writer.close();
     }
  4. Thread类用于创建线程,它有void join();void start();static void sleep();等方法。
  5. 启动新线程的流程是:建立Runnable对象,建立Thread对象,并赋值Runnable任务;启动Thread。
  6. Runnable是一个接口,它只有一个方法public void run()。它就是线程要执行的工作。

     public class MyRunnable implements Runnable {
         public void run(){
             go();
         }
         public void go(){
             System.out.println("top o' the stack");
         }
     }
    
     class ThreadTestDrive{
         public static void main(String[] args){
             Runnable threadJob = new MyRunnable();
             Thread myThread = new Thread(threadJob);
             myThread.start();
             System.out.println("back in main");
         }
     }
  7. 新建线程线程有新建(new),可执行(start),执行中,三个状态,可执行和执行中两个状态是来回切换的,由JVM调度决定,是随机不可控的。最多只能靠sleep来影响最小保证时间。
  8. 线程不可重复启动。
  9. 线程的名字可以通过myThread.setName("Solon's Thread")来设定,并可通过调用Thread.currentThread().getName()来获取。
  10. 线程会产生并发性问题(concurrency),并发性问题会引发竞争状态(race condition),竞争状态会引发数据损毁。举个例子,两个线程共用一个余额,每次用钱时,先检查余额,再扣钱。这样会由于竞争原因出现负数。所以需要一把锁,来保证一个方法一次只能被一个线程调用,即用synchronized关键字。

    private synchronized void makeWithDrawa(int amount){
        //...
    }
  11. 要注意锁是锁在对象上的,只有对象包含同步化方法时才起作用。对象就算有多个同步化方法,也还是只有一个锁。
  12. 同步化可以只修饰几行,这样可以减小原子操作的范围,提高效率。

    private void makeWithDrawa(int amount){
        //...
        synchronized(this){
            //...
        }
    }
  13. 两个线程和两个对象就可以引起死锁,各自占有一个资源,又需要调用彼此的资源。
  14. 静态的方法是运行在类上的,当要对静态的方法做同步化是,会使用类本身的锁。

7.10 常用数据结构与泛型

  1. TreeSet一有序状态保持并可防止重复;
  2. HashMap-KV来存储;
  3. LinkedList-针对经常插入和删除的集合;没有ArrayList常用;
  4. HashSet-防重复的集合,快速查找;
  5. LinkedHashMap-类似HashMap,但可记住元素的插入顺序,可按照存取顺序来排序;
  6. 可用java.util下的Collections.sort(List list)来给ArrayList排序,此函数会直接改变list的顺序。
  7. 不用泛型时,任何object对象都可以加入ArrayList,造成混乱。使用泛型,只有任一单一类型可加入;
  8. 泛型有三件事是重要的:

     new ArrayList(); //创建实例
     ArrayList songList = new ArrayList(); //声明指定泛型类型的变量
     void foo(ArrayList list); //声明或调用指定泛型的方法
  9. 在说明文件中,一般用E表示指定类型;

     public class ArrayList extends AbstractList implements List ...{
         public boolean add(E o)
     }
  10. 运用泛型的方法可以使用未定义在类声明的类型参数。

    public  void takeThing(ArrayList list)
    //与后续的万用字符相同
    public void takeThing(ArrayList)
    //与下面不一样,下面的只能使用Animal,不能使用其子类
    public void takeThing(ArrayList list) 
  11. 以泛型的观点来说extends可以代表extend或implement,表示是一个。
  12. 如果要对自定义类实现排序,有两种方法,一是实现该类的Comparable接口,这个接口有compareTo方法。可以调用已有的类型来辅助实现该方法;二是增加Comparator类的参数来比较。
  13. 可以调用HashSet的addAll方法,来生成ArrayList对应的队列;
  14. 同样如果要使用HashSet去除重复,针对自定义类,要覆盖equals方法和hashCode方法。它的比较过程是先比较hashCode(),如果相同,再比较equals。

    class Song implements Comparable{
        ...
        public boolean equals(Object aSong){
            Song s = (Song)aSong;
            return getTitle().equals(s.getTitle());
        }
        public int hashCode(){
            return title.hashCode();
        }
    }
  15. HashMap适合用KV场景存储数据

    HashMap scores = new HashMap();
    scores.put("Bert", 43);
    System.out.println(scores.get("Bert"));
  16. 泛型参数和数组参数的区别

    public void takeAnimals1(ArrayList animals)
    public void takeAnimals2(Animal[] animals)
    //takeAnimals1(new ArrayList dogs)会出错,编译错误
    //takeAnimals2(new Dog[] dogs)不会出错
    //前者可以防止在函数中进行Dog的特有相关操作,后者在执行期如果运行相关操作,会抛出异常
  17. 为了解决上述前者的问题,可以使用万用字符

    public void takeAnimals3(ArrayList animals)
    //为了防止此时,将ArrayList中加入Cat,当用万用字符时,不能对队列做加入操作

7.11 包与发布程序

  1. 可以使用-d class_path来将源代码与类文件相分离,实现对源码的保护;

     javac -d ../classes *.java
  2. jar类似tar命令,它有自己的规则。首先要确定所有的类文件都在classes目录下,其次要有manifest.txt文件来描述哪个类带有main()方法,最后执行命令打jar包

     cat manifest.txt
     Main-Class:MyApp
     cd MiniProject/classes
     jar -cvmf manifest.txt app1.jar *.class
  3. 如果jar包指定了manifest,则可以执行java -jar app1.jar
  4. 除非类是包的一部分,否则Java虚拟机不会深入其他目录去找。
  5. 用包防止类名冲突,可以把类放到各个包里。就像java.util.ArrayList。
  6. 而为了防止包命名冲突,一般反向使用domain作为包名称。

     com.headfirstbooks.Book
     com.headfirstjava.projects.Chart
  7. 选定包名称后,需要再类的源代码的第一行将类加入包中package com.headfirstjava;。然后要设定对应的目录结构。
  8. 一般Java程序的路径为项目根目录下有source和classes两个文件,source目录下为包路径和源代码,这样用-d ../classes编译后,-d会在classes目录下建立对应的目录和class文件。
  9. 按照上述约定时,manifest文件也放在class目录下,一般为Main-Class:com.headfirstjava.PackageExcise
  10. 在classes目录下执行jar语句打包,这里只要指定com路径就行。

     jar -cvmf manifest.txt packEx.jar com
  11. 使用jar -xf packEx.jar解压后会发现有META-INF目录,其下有MANIFEST.MF文件,即为写入的对应文件。
  12. JWS即Java Web Start,用户能通过网页上的某个连接来启动Java程序,一旦程序下载后,下次就能独立于浏览器来运行,它是通过网络来发布程序的一种手段。jnlp文件是个描述应用程序可执行JAR文件的XML文件。
  13. 制作可执行jar程序;编写jnlp文件;两者均放入Web服务器;Web服务器设定新类型application/x-java-jnlp-file;设定网页链接到jnlp文件Launch My App

7.12 远程过程调用、servlets等

  1. 远程过程调用最早是为了利用Server端的强大处理能力。RMI全称Remote Method Invocation;
  2. RMI需要服务器与服务器helper和客户端与客户端helper。helper假装成服务,但其实只是真实服务的代理;
  3. 使用RMI时要确定协议,两端都是Java可使用JRMP协议,IIOP可以调用Java对象或其他类型的远程方法。
  4. 在RMI中客户端的helper称为stub,客户端的helper成为skeleton
  5. 创建远程服务的流程是创建接口(extends Remote),实现类(extends UnicastRemoteObject impements MyRemote),rmic产生stub与skeleton(rmic MyRemoteImpl),启动RMI registry(rmiregistry),启动远程服务(Naming.rebind("Remote Hello", serviceMyRemote))。
  6. 客户端查询RMIregistry((MyRemote)Naming.lookup("rmi://127.0.0.1/Remote Hello")),RMIregistry返回stub对象,客户端调用stub上的方法;
  7. 注意点:启动远程服务前启动registry;参数和返回类型可序列化;
  8. servlet相当于Java的CGI,可以处理用户提交的请求,并将结果返回给浏览器的网页;
  9. 创建并执行serverlet的步骤:找到网页服务器可以存放servlet的地方;取得servlet.jar并添加到classpath上(其不是标准函数库的一部分);extends HttpServlet来编写servlet的类;编写html来调用servlet;给服务器配置html和servlet;
  10. servlet一般通过覆盖HttpServlet的doGet和doPost来创建;它输出带有完整标识的HTML网页;
  11. EJB是Enterprise JavaBeans,它具有一组光靠RMI不会有的服务,比如交易管理、安全性、并发性、数据库和网络功能等;EJB服务器作用于RMI调用和服务层之间。service helper调用EJB object,object调用Enterprise bean,后者再调用DB等操作;
  12. Jini也使用RMI,但具有自适应探索(接收服务注册和客户端调用查询)和自恢复网络(用心跳检查服务)的功能;

    7.13 附录

  13. 按位非~,按位与&,按位或|,按位异或^;
  14. 右移>>,无符号右移>>>;
  15. String具有不变性,放在String pool中,不受Garbage Collector管理。
  16. 包装类没有setter,其具有不变性。例如new Integer(42),永远都是42。
  17. 断言可以用来测试,它比println的好处是没有特别设定的话,它会自动被JVM忽略,也可专门打开断言调试程序。

     assert (height > 0) : "height = " + height + " weight = " + weight;
     //冒号后面的语句可以是任何返回非null值得语句,但一定要注意不要在这里改变对象的状态
     //断言的编译和普通编译没差别,执行时有差异。
     javac TestDriveGame.java
     java -ea TestDriveGame
  18. new Foo().go()是一个调用go方法,但又不需要维持对Foo引用的方式;
  19. 静态嵌套类,和普通类很像,可通过new Outer.Inner()来创建。可存取外层静态私有变量;
  20. 非静态的嵌套类,通常被称为内部类。
  21. 匿名内部类。

     //button.addActionListener(quitListener); //通常是传递一个内部类的实例
     //虽然ActionListener是个接口,而且我们不能声明一个接口的实例,但匿名内部类是个例外
     button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ev){
             System.exit(0);
         }
     });
  22. public任何程序代码都可公开存取;default只在统一包中的默认事物能够存取;protected可允许不在同一包的子类,继承该部分;
  23. 对于常用的可变String,一般使用StringBuilder,Thread安全环境中,采用StringBuffer;
  24. 二维数组是指数组的数组,new int [4][2]其实是创建一个包含四个元素的数组,每个元素是一个长度为2的数组。
  25. 枚举相对于普通常量的优势是限定了范围,其实际是继承了java.lang.Enum。声明时使用public enum Members {JERRY, BOBBY, PHIL};
  26. 每个枚举都内置values()可以用于遍历操作;
  27. 可以在enum中加入构造函数,方法、变量和特定常量的内容;

8. 相关

暂无

转载于:https://www.cnblogs.com/solontan/p/6414597.html

你可能感兴趣的:(head first java读书笔记)