《Head First Java》笔记

01 | Java基本概念

  • Java的工作方式

    1. 编写源代码party.java
    2. 执行javac来编译源代码,产生party.class
    3. 启动JVM来运行字节码文件,JVM会将字节码转换成平台能够理解的形式来运行。 在这里插入图片描述
  • 关于while循环

    1. 不能使用如下错误代码:
      	int i = 1;
      	while(i){
      		..........
      	}
      
      while循环中只能接收boolean型结果,即接收true/false。
  • 关于类、对象、方法、实例变量

    1. 由 .java 文件编译出来的 ----------- 类
    2. 实例变量可以与其他兄弟姐妹不同 -----------对象
    3. 功能类似模板 ------------ 类
    4. 喜欢执行工作 ------------ 对象、方法
    5. 带有很多方法 ------------ 类、对象
    6. 代表“状态” ------------- 实例变量
    7. 拥有很多行为 ---------- 对象、类
    8. 待在对象中 ------------ 方法、实例变量
    9. 生存于堆上 ------------ 对象
    10. 被用来创建对象实例 --------- 类
    11. 状态可以改变 ------------- 对象、实例变量
    12. 声明方法 -------- 类
    13. 可以在运行期变化 ---------- 对象、实例变量

02 | primitive主数据类型和引用

  • primitive主数据类型用来保存基本类型的值, 对象引用保存的是对象的引用

  • primitive主数据类型的声明与赋值声明:

    1. 编译器不允许将大杯的内容放到小杯中(强制转换有风险),但反过来可以(隐含展开)。
      long y = 42;
      int x = y;//不能通过编译,编译器无法确定long的内容是否可以截成int型
      //若要强制编译器通过,可用强制转换,如下:
      int x = (int) y;
      
      long y = 42;
      short x = (short) y;  //可通过编译,但x值为-25534!!!
      
      float f = 3.14f;
      int x = (int) f;   //x值为3 
      
    2. primitive主数据类型共有8种,分别是:
      boolean char byte short int long float double
      可记忆为:
      “Be Careful,Bears shouldn't Ingest Large Furry Dogs ”(小心,熊不应该吃大型毛茸茸的狗)
      存放数值的有6种如下图:在这里插入图片描述
    3. primitive变量的默认值:
      数字的primitive(包含char)的预设为0,boolean的预设为false,对象引用则为null;如: int–0;float–0.0;boolean–false。
    int x = 234;
    byte b = 89;
    boolean ifFun = true;
    double d = 34.98;
    char c = 'f';
    long big = 3456789;
    float f = 32.5f; //不带f默认是double类型
    
  • Java保留字中易遗忘的:native、strictfp、transient、assert、enum、const

  • 变量命名方法:

    1. 必须以字母、下划线_、或$ 开头,不能用数字开头
    2. 除首字母外,后面可用数字
    3. 要避开上述的java保留字
  • primitive主数据类型变量与引用变量: 在这里插入图片描述

    1. 那么问题来了,如果如上图所言,那是否意味着所有的对象引用都具有相同的大小,而不管它实际上所引用的对象大小?
      答:是的,对于任意一个Java虚拟机来说,所有的引用大小都一样,但不同的虚拟机间可能会以不同的方式来表示引用,因此某个虚拟机的引用大小可能会大于或小于另一个虚拟机的引用。
  • 数组也是对象

  • 没有引用到任何对象的引用变量的值为null值。

03 | 方法操作实例变量

  • 类所描述的是对象知道什么(变量)与执行什么(方法)
  • java是通过值传递的(拷贝传递),特别注意,在方法中改变拷贝后的值,对拷贝前的值是没有影响的。(java所传递的所有东西都是值!!!)在这里插入图片描述
  • 实例变量与局部变量的区别
    1. 实例变量声明在类中
    2. 局部变量声明在方法中
    3. 局部变量在使用前必须初始化,因为它没有默认值,否则无法编译
  • 变量的比较(==与equals) 在这里插入图片描述
  • 对号入座:实例变量、参数、return、getter、setter、封装、public、private、按值传递、方法
    1. 一个类可以带很多个------实例变量、方法、getter、setter
    2. 一种方法只能带有一个-------return
    3. 可以被隐含的转换------return、参数
    4. 喜欢private的实例变量--------封装
    5. 制作一个拷贝-------按值传递
    6. 只有setter才能更新-------实例变量
    7. 根据定义返回-------getter
    8. 不可以以实例变量来运用------public
    9. 可以有许多个参数 -------- 方法
    10. 被定义成采用一个参数 ------ setter
    11. 帮忙创建封装 ------- getter、setter、public、private
    12. 总是单飞 ---------- return

04 | 编写程序

  • Math.random返回一个介于0到小于1之间的数,如下产生0~4之间的整数
    int randomNum = (int) (Math.random()*5)
    

05 | 认识Java的api

  • ArrayList与数组的比较
    编译器能够自动地将primitive主数据类型包装成Object存放在ArrayList中。
  • 除了java.lang这个包里面的类,其他要用到的类都要指定完整名称或者使用import来导入。

06 | 继承与多态

  • 继承可传递,即狼is a犬类动物,犬类动物is a 动物,则可推出,狼is a 动物。

  • 继承下来的方法可以被覆盖,但实例变量不能被覆盖。

  • 如何预防某个类被作出子类:

    1. 存取控制。就算不能标记为private,也可以不标记为public。非公有的类智能被同一个包的类作出子类。
    2. 使用final。表示不能被继承。
    3. 让类只拥有private的构造器。
  • 重写与重载:

    1. 相同点:方法名称相同。(本书认为重载与多态毫无关系P191)

    2. 不同点:位置、参数列表、权限修饰符、返回值类型、抛出异常不同。
      重载(overload):发生在同一个类中,方法名相同,参数列表不同,与权限修饰、返回值类型、抛出异常无关。

      重写(override): 发生在继承类中,方法名和参数列表相同,权限修饰符大于等于父类、返回值类型小于等于父类、抛出异常小于等于父类。

07 | 接口与多态

  • object类是具体的,那怎么会允许有人去创建Object的对象呢?这不就跟Animal对象一样不合理吗?
    有时候可能需要一个通用的对象,一个轻量化的对象,它最常见的用途是在线程的同步化上面。

  • 使用Object类型的多态应用:

    ArrayList<Object> myDogArrayList = new ArrayList<Object>();
    Dog adog = new Dog();
    myDogArrayList.add(adog);
    
    //当把dog对象取出,并赋值给Dog的引用将无法通过编译
    Dog d = myDogArrayList.get(0);
    //上述代码不能通过编译的原因,对ArrayList<Object>调用get()方法会返回Object类型,编译器无法确认它是Dog
    //注意!任何从ArrayList<Object>取出的东西都会被当做Object类型的引用而不管它原来是什么。
    
  • 多态:在这里插入图片描述

  • 对象类型转换:
    Object引用变量在没有类型转换的情况下不能复制给其他的类型,若堆上的对象类型与索要转换的类型不兼容,则此转换会在执行期发生异常。例如:

    Dog d = (Dog) x.getObject(aDog);
    
    //如下代码可编译通过
    Object o = al.get(index);
    Dog d = (Dog) o;
    d.bark();
    

    在这里插入图片描述

  • 为什么不能多继承? 会出现“致命方块”的问题
    在这里插入图片描述

  • 如果想要创建出一个具体的子类,但不打算完全地覆盖掉原来的方法,只想加入额外的动作,该如何?
    用super关键字,调用父类的方法。

    abstract class Report{
    	void runReport(){......}
    	void printReport(){......}
    }
    
    class BuzzReport extends Report{
    	void runReport(){
    		super.runReport();
    		buzzCompliance();
    		printReport();
    	}
    	void printReport(){......}
    }
    

08 | 构造器与垃圾收集器

  • 内存中的两种区域:

    1. 堆(heap):对象的生存空间
    2. 栈(stack):方法调用、变量的生存空间
  • 实例变量 OR 局部变量

    1. 实例变量:是被声明在类而不是方法中,存在与所属的对象中。位于堆上。如实例变量是个对对象的引用,则对象与引用都在堆上。primitive默认值:0/0.0/false,引用默认值:null。
      public class duck{
      	int size;		
      }
      
    2. 局部变量:声明在方法中,生命周期只限于方法被放在站上的这段时间(方法调用至执行完毕为止)
      //x,i,b都为局部变量
      public void foo(int x){
      	int i = x + 3;
      	boolean b = true;
      }
      
  • 方法会被堆在一起:
    在这里插入图片描述

  • 有关对象局部变量:

    1. 非primitive的变量只是保存对象的引用,而不是对象本身。
    2. 对象本身只会存在于堆上。
      在这里插入图片描述
  • 实例变量存在于对象所属的堆空间上:

    1. 若实例变量是primitive类型:则根据具体的int、long等类型留空间;
    2. 若实例变量是对象:java只会保留引用变量的空间而不是对象本身的空间。如果只声明变量而没有创建对象:
      在这里插入图片描述
      直到引用变量被赋值一个新的Antenna对象才会在堆上占有空间:
      在这里插入图片描述
构造函数
  • 下列代码,看起来是在调用Duck方法,实际上是在调用Duck的构造函数来创建对象。 java Duck myDuck = new Duck();

  • 构造函数不是方法,唯一能够调用构造函数的方法:新建一个类。初始化对象时会执行的程序代码在构造函数里。

  • 如何分辨构造函数和方法?构造函数没有返回值类型。

  • 构造函数不会被继承。

  • 如果已经写了一个有参数的构造函数,并且需要一个没有参数的构造函数则必须自己动手写。完全没写构造函数时编译器才会帮你写一个。

  • 如果类有一个以上的构造函数,则参数一定要不同。

  • 重载: 多个构造函数且参数都不相同(参数的类型 and 顺序

  • 构造函数可以是公有、私有或不指定。

  • 在创建新对象时,所有继承下来的构造函数都会执行。且构造函数在执行的时候首先去执行它的父类的构造函数。

  • 举个栗子:

    在这里插入图片描述

  • 如何调用父类的构造函数? 调用super,且必须在第一行调用

    1. 没编写构造函数:自动调用super();
    2. 有构造函数但没调用super():编译器会帮你对每个重载版本的构造函数加上:super(),编译器只会自动加无参数版本。
      public class Duck{
      	int size;
      	public Duck(int newSize){
      		super();  //调用父类构造函数
      		size = newSize;
      	}
      }
      
  • 某个构造函数调用同一个类的另一个构造函数:(使用this)

    1. this()只能用在构造函数中,且只能是第一行语句。
    2. 因为super()和this()都要求在第一行,因此二者不可兼得。
    3. 举个栗子:
      class Mini extends Car{
      	Color color;
      	public Mini(){
      		this(Color.Red);
      	}
      	public Mini(Color c){
      		super("Mini");
      		color = c;
      	}
      	======以下为错误示范==============
      	public Mini(int size){
      		this(Color.Red);
      		super(size);
      	}
      } 
      
  • 对象的生命周期:

    1. 先看个栗子:
      public void doStuff(){
      	boolean b = true;
      	go(4);
      }
      public void go(int x){
      	int z = x + 24;
      	crazy();
      }
      public void crazy(){
      	char c = 'a';
      }
      
      在这里插入图片描述
  • 释放对象的引用(变为可回收垃圾):

    1. 引用永久性的离开了它的范围:
      void go(){
      	//z会在方法结束时消失
      	Life z = new Life();
      }
      
    2. 引用被赋值到其他的对象上
      	Life z = new Life();
      	z = new Life();
      
    3. 直接将引用设定为null
      Life z = new Life();
      z = null;
      

09 | 数字与静态

  • 静态的方法不能调用非静态的变量:静态方法是通过类的名称来调用的,它不知道有哪些实例变量。

  • 静态的方法不能调用非静态的方法。

  • 非静态方法可以读取到静态的变量。

  • 静态变量是共享的,同一类所有的实例共享一份静态变量。而实例变量是每个实例一个。

  • 静态变量类被加载时初始化,且静态变量在该类的任何静态方法执行之前就初始化。

  • 静态的final变量是常数。

    1. 如下代码:
      public static final double PI = 3.141592653589793;
      
      public: 可供各方读取
      static: 不需要Math的实例
      final: 圆周率是不变的
    2. 常数final变量的初始化:
      //方法一:
      public class Foo{
      	public static final int X = 25;
      }
      
      //方法二:
      public class Bar{
      	public static final Double X ;
      
      	static{
      		X =double)Math.random();
      	}
      }
      
  • final:

    1. final的变量:不能改变它的值
    2. final的方法:不能覆盖掉该方法
    3. final的类:不能继承该类
  • Math的方法:

    1. Math.random(): 返回介于0.0~1.0之间的双精度浮点数(随机数
    2. Math.abs(): 返回双精度浮点数类型参数的绝对值
    3. Math.round(): 返回四舍五入之后的整型或长整型值
    4. Math.min(): 返回最小值
    5. Math.max(): 返回最大值
  • primitive主数据类型的包装:

    1. 当你需要以对象方式来处理primitive数据时,就将其包装起来。
    2. 装包:
      int i = 288;
      Integer i = new Integer(i);
      
    3. 拆包:
      int j = i.intValue();
      
    4. 编译器的自动拆包装包:
      	ArrayList<Integer> ln = new ArrayList<Interger>();
      	
      	//自动装包
      	ln.add(3);
      	
      	//自动拆包
      	int num = ln.get(0);
      
    5. 举个栗子:
      public class TestBox{
      	Integer i;
      	int j;
      	public static void main (String[] args){
      		Testbox t = new Testbox();
      		t.go();
      	}
      	public void go(){
      		j=i;
      		System.out.println(j);
      		System.out.println(i);
      		
      	}
      }
      
      运行失败:空指针异常。
  • 操作日期:

    1. 要取得当前的日期时间就用Date,其余功能可以从calendar上面找。
    2. 创建calendar实例:
      Calendar cal = Calendar.getInstance();
      
    3. Calendar API的方法
      add(int field, int amount) :加减时间值
      get(int field):取出指定字段的值
      getInstance(): 返回calendar,可指定地区
      roll(int field, boolean up): 加减时间值,不进位
      set(int field, int value): 设定指定字段的值
      set(year, month,day, hour, minute) : 设定完整的时间
      setTimeInMillins(long millis) : 以毫秒指定时间
      
  • 做个练习:
    在这里插入图片描述
    正确答案:x x √ x √ x √ √ x x x √ x x

10 | 异常处理

  • 受检查与不受检查的异常:
    在这里插入图片描述
  • 编译器不会注意RuntimeException类型的异常,RuntimeException不需要声明或被包在try/catch块中。(要包也行)
  • try/catch:
    在这里插入图片描述
  • finally:无论如何都要执行。如果try或者catch块有return指令,finally还是会执行,但是流程会跳到finally然后再回到return指令。
  • 一个方法可以抛出多个异常。有多个异常时可直接声明他们的父类,若需单独处理每个异常则要分开catch,并且从小到大catch(把最小的异常写在最上面)。
  • Integer.parseInt()方法也有异常,但是不用管。原因:它继承了RuntimeException,大部分这类错误是由于程序逻辑问题,而不是无法预测或防止的问题。
  • 如果不想处理异常,可以把他duck(抛掉throws),如果连main()也duck的话可以编译成功,但运行时虚拟机会中断。
    在这里插入图片描述
  • 异常处理规则:
    1. catch和finally不能没有try;
    2. try一定要有catch或者finally;
    3. try和catch之间不能有程序;
    4. 没有catch时方法必须声明异常。
      void go() throws FooException{
      	try{
      		x.doStuff();
      	}finally{}
      }
      
    5. finally不能在没有try块的情形下出现

11 | 内部类

  • 内部类:嵌套在一个类内部的类。
  • 内部类可以使用外部所有的方法与变量,就算是私用的也一样。
  • 内部类可以从外部类中初始化内部类,此内部对象会绑在外部对象上。

12 | 序列化和文件的输入/输出

1.序列化与反序列化
  • 序列化(Serializable):将对象状态进行存储,将内存中的东西保存到文件中(转换为字节序列的过程)。
    //1.创建出FileOutputStream, 如果文件不存在会被自动创建
    FileOutputStream fileStream = new FileOutputStream("MyGame.car");
    
    //2.创建ObjectOutputStream
    ObjectOutputStream os = new ObjectOutputStream(fileStream);
    
    //3.写入对象
    os.writeObject(characterOne);
    os.writeObject(characterTwo);
    os.writeObject(characterThree);
    
    //4.关闭ObjectOutputStream
    os.close();
    
  • 反序列化(Deserialization):把字节序列恢复为Java对象的过程。
    //1.创建出FileInputStream, 如果文件不存在会抛出异常
    FileInputStream fileStream = new FileInputStream("MyGame.car");
    
    //2.创建ObjectInputStream
    ObjectInputStream os = new ObjectInputStream(fileStream);
    
    //3.读取对象
    Object one = os.readObject();
    Object two = os.readObject();
    Object three = os.readObject();
    
    //4.转换对象类型
    GameCharacter elf = (GameCharacter) one;
    GameCharacter troll = (GameCharacter) two;
    GameCharacter magician = (GameCharacter) three;
    	
    //5.关闭ObjectInputStream
    os.close();
    
  • 如何序列化?实现Serializable接口。 Serializable接口没有方法需要实现,唯一目的是声明该类能够被序列化。
  • 序列化程序会将对象版图上的所有东西存储起来。被对象的实例变量所引用的所有对象都会被序列化。若一个类中包含对另一个类的引用,则另一个类也要实现Serializable接口,否则序列化失败。因此序列化是全有或者全无的。
  • 如果某实例变量不能或不应该被序列化,就把他标记为transient(瞬时)的(返回null)。
  • 不能序列化的父类可以有可序列化的子类;可序列化的父类的子类会自动可序列化而不用具体声明。
  • 静态变量不会被序列化,因为static代表的是每个类一个,当对象被还原时,静态变量会维持类中原本的样子,而不是存储时的样子。
  • 读取对象的顺序必须与写入对象的顺序相同。
  • readObject()的返回类型是Object,因此反序列化回来的对象还需要转换成原来的原型。
  • tip:每当对象被序列化时,该对象会被盖上一个类的版本识别ID(serialVersionUID)。
    在这里插入图片描述
    在这里插入图片描述
2. File对象
  • file对象代表文件的路径而不是文件本身。
  • 可以对File对象做的事:
    在这里插入图片描述
3.缓冲区
  • 使用缓冲区效果更好:
    BufferedWriter writer = new BufferedWriter(new FileWriter(afile));
    
  • BufferedWriter可以暂存一堆数据,到满的时候再实际写入磁盘,这样就可以减少对磁盘操作的次数。
  • 强制缓冲区立即写入:write.flush();
4.文件的读取
  • 代码如下:
    在这里插入图片描述

13 | 集合与泛型

0.整体结构
  • 注意:map并没有继承collection。
    在这里插入图片描述
1. Set:注重独一无二
  • TreeSet:有序,元素唯一,使用该集合存储自定义对象必须实现Comparable接口或者实现Comparator的比较器将其加入TreeSet构造函数的参数列表。调用无参构造函数:用compareTo()来排序。
  • HashSet:不保证元素有序性,元素唯一,快速寻找相符元素。
2.Map:用key来搜索的专家
  • LinkedHashMap:类似HashMap,但可以记住元素的插入顺序,也可设定成元素上次存取的先后顺序来排序。
  • HashMap:key-value结构,key不可重复。
3.List:对付顺序的好帮手
  • LinkedList:针对频繁在元素之间插入或删除元素所涉及的高效率集合。(实际还是ArrayList比较实用)
4.排序
  • Collection.sort()会把list中的String按照字母排序。
  • 那么问题来了,如果不是String是其他对象呢?如何排序?查阅API文档会发现,sort()大量用到了泛型。您肯定要问泛型了吧?那我们来说说泛型。
5.泛型
  • 泛型更安全:(几乎只有集合才会真的需要泛型)
    在这里插入图片描述

  • 运用泛型的方法:
    在这里插入图片描述

  • 以泛型的观点来说,extend代表extend或implement。

  • 要实现对对象的排序,就必须实现comparable。只有在实现comparable的情况下才能把ArrayList<对象>传给sort()方法。没有为什么,这个方法就是这么声明的。

  • 而Comparable只有一个方法需要实现:CompareTo(T o)。此方法的对象必须要辨别在排序位置上他自己是高于、低于或相等于所传入的对象。如果我有自己的排序规则那么CompareTo方法就不是那么适用了。

  • 因此,为了满足我们变态的多样性需求,我们可以使用自制的Comparator

  • 总结:排序规则如下:

    1. 实现Comparable接口。调用sort(List mylist)方法上的**compareTo()**方法来决定顺序。
    2. 无需实现Comparable接口。调用sort(List mylist,Comparator c)方法,如此,会使用Comparator的**compare()**方法。

    即:如果传Comparator给sort()方法,则排序由Comparator来决定。

  • 做个习题:
    在这里插入图片描述

  • tip:如果将方法声明成取用ArrayList, 它只会取用ArrayList参数,ArrayList和ArrayList都不行。

6.对象的等价
  • 如果俩对象相等要满足的条件:

    1. a.equals(b) ==>true
    2. a.hashCode() == b.hashCode()
  • HashSet()如何检查重复?
    当把对象加入HashSet时,会使用hashcode来判断对象加入的位置,如果有相同的hashcode(),则会继续判断equals()方法来检查是否真的相同。

  • 两个对象的hashcode()相等,他们不一定相等;但若两个对象相等,则hashcode一定是相等的。原因:hashcode使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。因此,hashcode只能用来缩小寻找成本,最后还是要用equals()才能认定是否是相同对象。

  • equals()的默认行为是测试两个引用是否对heap上的同一个对象(即==)。

  • tip:

    1. 数组的类型检查:运行期间
    2. 集合的类型检查:编译期间
7.万用字符:
  • ArrayList<? extends 父类>:在使用带有<?>的声明时,只能操作元素,不能新增集合元素。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值