java学习笔记 head first java

2022-09-08 11:43:31 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

文章目录

  • golang to java
    • Head First Java
      • final
    • 一些与众不同的设计
      • staic初始化
      • primitive类型的包装
      • format
      • 静态的import
      • 内部类
    • 常用包、函数
      • java.util.Calendar
    • 异常处理
    • 序列化
    • File IO
      • BufferedXXX
      • 要点
    • 多线程
      • 同步操作
    • 集合与泛型
      • 对象的等价
      • 泛型
    • 包,jar存档文件和部署
      • 创建可执行的JAR
      • 把package,com结构打包进jar
    • Addtional
      • 不变性
      • 断言
      • Anonymous和Static Nested Classes
        • 静态嵌套类
        • nested和inner的差别
      • 存取权限和存取修饰符(谁可以看到什么)
      • String和StringBuffer、StringBuilder的方法
      • 多维数组
      • 枚举

golang to java

golang工程师,最近开始学习一些java

Head First Java

instanceof相当于断言

代码语言:javascript复制
Dog d = new Dog()
Object o = d
if (o instanceof Dog) { 
   
	Dog d = (Dog)o
}

interface在java和golang中基本一致,java中的interfece是一个100%抽象类,所有函数都是抽象的。必须要用implements显式指定一个接口,(可以是多个吗?可以,用逗号分隔)

代码语言:javascript复制
public interface Pet { 
   
	public abstract void beFriendly();
	public abstract void play();
}
public class Dog extends Canine implements Pet,Binterface,Cinterface { 
   
	public void beFriendly(){ 
   
	}
	public void play(){ 
   
	}
}
代码语言:javascript复制
type Pet interface { 
   
	beFriendly()
	play()
}

golang中不需要implements,java必须要implements

final

final也可用于修饰非静态变量,表达一种不能变的概念,包括实例变量,局部变量,或者方法的参数。(不变性上类似const),还可以防止方法的覆盖或创建子类(继承体系中的末端)。

一些与众不同的设计

staic初始化

加载类时,会执行static{xxx},这个可以初始化staic final变量

代码语言:javascript复制
public class Bar{ 
   
	public static final double BAR_SIGN;

	static { 
   
		// 这段程序会在类被加载时执行
		BAR_SIGN = (double)Math.random();
	} 
}

primitive类型的包装

由于object和primitive的差异,需要把primitive类型包装成object才能进入object体系。 泛型的规则,要求只能是类或者接口类,不能使primitive。 5.0之前,要手动,5.0之后,有autoboxing 可用在,参数、返回值、boolean判断、数值计算、赋值。 void takeNumber(Integer i){}可传入 int类型。 int giveNumber(){return Interger(0)}返回值可互相替代。 if (Boolean对象){}bool判断可用Boolean对象。 Integer i = Integer j 3直接用于数值计算。 Interger i = 3赋值。

注:包装完了是个object引用,需要new出来

format

String.format ("%t", today)%t表示时间。

代码语言:javascript复制
"%tc" // 完整时间
"%tr" // 只有时间
"%tA, %tB, %td" // 周月日
"%tA, %<tB %<td", today // <符号是个特殊的指示,用来告诉格式化程序重复利用之前用过的参数,不用写多个。

静态的import

import static java.lang.System.out;可以省略System。 省略包名会有混淆,在golang中一般不提倡这么用。

内部类

代码语言:javascript复制
class MyOuterClass{ 
   
	private int x;
	MyInner inner = new MyInner();
	class MyInner{ 
   
		void go(){ 
   
			x = 42; // 可以使用OuterClass内的所有
		}
	}
	public void doStuff(){ 
   
		// 从外部类以“内”的代码初始内部实例
		inner.go(); // 调用内部方法
	}
}

// 另一种,从外部类以“外”的代码初始内部实例
class Foo{ 
   
	public static void main(String[] args){ 
   
		MyOuterClass outObject = new MyOuterClass();
		MyOuterClass.MyInner innerObj = outObject.new MyInner();
		// 外部类.内部类 内部对象 = 外部对象.new 内部类构造函数; 
	}
}

注意点:

  1. 内部类的实例一定会绑在外部类的实例上。
  2. 内部类提供了在一个类中提供同一个接口实现多次的机会。
  3. 使用内部类的特征:独立、却又好像另一个类成员之一。
  4. 使用内部类代表外部类,外部类只能单继承。内部类可以实现多个接口,通过IS-A测试。

常用包、函数

代码语言:javascript复制
String.format("...",...); // 格式化字符串
String doubleString = Double.toString(d); // double to string
import java.util.Calendar // 操作日期,除了Date里的,时间计算,roll, set,等等
import java.util.Date // 当前日期
Calendar cal = Calendar.getInstance(); // 静态方法返回一个
Calendar对象,不可直接 new Calendar();

java.util.Calendar

代码语言:javascript复制
add(int field,int amount)
get(int field)
getInstance()
getTimeInMillis()
roll()
set()
set()
setTimeInMillis()

异常处理

代码语言:javascript复制
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();
	}
}

throws 表示方法会抛出什么类型的错误。

RuntimeException被称为不检查异常,可以抛出和catch但是没有这个必要,编译器也不管。 任何继承过它的都会被编译器忽略。 try catch是处理真正的异常,而不是程序的逻辑错误。catch要做的是恢复的尝试,或者至少优雅的列出错误信息。

  • 可能会抛出异常的方法必须声明成 throws Exception。
  • 如果程序调用了会throws Exception的方法,那一定要try catch,告诉编译器注意到了
  • 如果不处理异常,还是可以正式地将异常ducking来通过编译

finally表示无论如何都要做的事,无论try,catch,都会进finally。如果try catch中有return,依然会执行finally

代码语言:javascript复制
try{ 
   
} catch (AException ex) { 
   
} catch (BException ex){ 
   
}finally { 
   
}

Exception也是继承体系中的,可以多态,父类引用子类实例。 throws 父类,可以抛出它的所有子类。 catch父类,可以接它所有子类,但是不应该这么做。应该具体异常具体一个catch。 duck掉的意思是不处理exception,交给调用栈的上一层去处理。

代码语言:javascript复制
// duck the exception
void foo() throws ClothingException { 
    // throws 出去,交给上层处理
	laundry.doLaundry()
}

异常处理规则:

  1. catch与finally不能没有try
  2. try一定要有catch或finally
  3. try与catch之间不能有程序
  4. 只带有finally的try必须要声明异常!!就是throws XXException对于try{}finally{}

序列化

把Object可以完整的保存下来,包括Object中的对其他Object的引用,序列化过程必须全部正确,如果有局部不正确,那整体也会出错。 Object -> ObjectOutputStream -> FileOutputStream -> 文件

  • 标记为transient可不被序列化。
  • 父类不序列化,子类可以标记为可序列化。
  • 如果两个对象引用了同一个对象,那么序列化时候也只会有一份(比较聪明)。

反序列化时。

  1. 对象从stream中读出来
  2. jvm通过存储信息判断出对象的class类型
  3. jvm尝试寻找和加载对象的类。如果jvm找不到,就会抛出exception。
  4. 新的对象会被放在堆上,不会调用构造函数。
  5. 如果对象在继承树上有个不可序列化的祖先类,则该不可序列化类以及之上的类的构造函数就会执行,一旦构造函数连锁启动后将无法停止。也就是说从第一个不可序列化的父类开始,全部都会重新初始状态。
  6. 对象的实例变量会被还原成序列化时的状态值。transient变量会被赋值null的对象,或者primitive的默认0值。

文件-> FileInputStream -> InputOutputStream -> Object newFileInputStream(“xxx”) newObjectInputStream(fs) Obj = (Type)is.readObject()

读取对象的顺序必须与写入的顺序相同。

写入文件和写对象差不多。

代码语言:javascript复制
try{ 
   
	FileWrite writer = new FileWriter("Foo.txt");
	writer.write("hello foo!");
	writer.close();
}catch ()...

File IO

import java.io.*; class名java.io.File: File表示磁盘上的文件,不是文件的内容。

代码语言:javascript复制
File.mkdir(); // 建目录
File.isDirectory(); // 列出目录的内容
File.getAbsolutePath(); // 列出绝对路径
File.delete(); // 删除

BufferedXXX

缓冲区,可以和FileWriter连接,提高性能。 构造函数 : BufferedWriter(FileWriter)

要点

  • 用FileWriter这个连接串流来写入文本文件。
  • 将FileWriter连接到BufferedWriter可以提升效率。
  • File对象代表文件的路径,而不是文件本身。
  • 可以用File对象来创建、浏览、删除目录
  • 用到String文件名的串流,大部分都可以用File对象来代替String
  • 用FileReader来读取文本文件
  • 将FileReader链接到BufferedReader可以提升效率。

多线程

java.lang.Thread

  • java每个线程有独立的执行空间
  • java.lang.Thread的对象表示线程
  • Thread需要任务,任务实现Runnable接口
  • Runnable接口只有一个方法,就是void run()
  • run()是线程入口
  • Runnable作为Thread的构造函数参数,构建Thread
  • Thread在调用start之前处于新建立状态
  • start之后会建立出新的执行空间,被等待执行
  • java虚拟机有调度器
  • 线程会block
  • 不会保证调度时间合顺序

Thread.setName(String)可以帮线程取名字。用Thread.currentThread().getName()可以取出名字。

同步操作

synchronized修饰方法,使得它每次只能被单一的线程存取。

集合与泛型

Collections Framewo 集合框架,能够支持绝大多数数据结构。

代码语言:javascript复制
public static <T extends Comparable<? super T>> void sort(List<T> list)

public interface Comparable<T>
{ 
   
	int compareTo(Object b);
}

Interface Comparator<T>
{ 
   
	int	compare(T o1, T o2);
}

T extends Comparable 表示T必须继承/实现 Comparable, ? super T表示Comparable的类型参数必须是T或者T的父型。 extends在泛型里,可以是implements或者extends,即继承或者实现都可以。 sort也可以有两个参数的版本,第二个参数必须实现Comparator,比较两个Type。 Collection里面有三种,List,Set,Map。

代码语言:javascript复制
Collection(itf)
	Set(itf)
		SortedSet(itf)
			TreeSet // 排序的Set
		LinkedHashSet // 带插入顺序的Set
		HashSet // 不带顺序的Set
	List(itf)
		ArrayList
		LikedList
		Vector

Map(itf)
	SortedMap(itf)
		TreeMap
	HashMap
	LinkedHashMap
	Hashtable

可以 HashSet .addAll(ArrayList), 一把全加进去。

对象的等价

引用相等性: 不同堆上的hashCode()不一样,内存不一样。可以用==比较。

对象相等性。 需要覆盖 hashCode()和equals()。

hashCode()先比较,不一样的,一定不一样,如果一样,那么一定是同一个对象。 在hash相同时,还不能确定对象一定想同,还需要用equals比较。

hashcode不一样是对象不一样的充分条件,hashcode不一样,equals为false是对象不一样的充分必要条件。

那么对象一样的 充分必要条件是 hashcode一样 或者 equals为true。

  1. 若equals被覆盖,那么hashCode一定也要被覆盖,否则比较没有意义,因为会先比较hashCode。foo.hashCode() == bar.hashCode() || foo.equals(bar)
  2. hashCode() 默认是取heap上的地址相关。
  3. equals()默认是执行==。如果equals没被覆盖,两个对象永远不会被视为相同的。
  4. equals()必须与hashcode == hashcode等值(推论)
代码语言:javascript复制
	public class BookCompare implements Comparator<Book>{ 
   
			public int compare(Book a, Book b){ 
   
				return a.title.compareTo(b.title);
			}
	}

	BookCompare bCompare = new BookCompare()
	TreeSet<Book> tree = new TreeSet<Book>(bCompare);
	// 使用Comparator作为构造参数

也可以Book impletements Comparable来new TreeSet<Book>

总结:

  1. 要使用TreeSet,要么集合中的元素,实现Comparable接口
  2. 要么使用重装,取用Comparator参数的构造函数来创建TreeSet

HashMap<Key, Value> 用put和get。

泛型

父类数组可以接受子类数组作为入参。

代码语言:javascript复制
public void takeAnimals(Animal[] animals);
{ 
   
	takeAnimals(dogs);// dog数组每个元素都继承Animal
}

但是ArrayList<Animal>不可以接受ArrayList<Dog>,会编译期失败。

对于数组来说会运行期失败。

泛型的万用字符。使用带有<?>的声明时,编译器不会让你加入任何东西到集合中!

代码语言:javascript复制
// ? 继承或实现Animal的T
public void takeAnimals(ArrayList<? extends Animal> animals){ 
   
	for (Animal a : animals){ 
   
		a.eat(); // 不会报错
	}
	animals.add(new Cat()); // 会编译器报错
}

相同功能的另一种语法。

  1. 返回类型前声明。可以只声明一次。后面都用T。 public <T extends Animal> void takeThing(ArrayList<T>) list)
  2. 用问号。每个地方都要声明 ? public void takeThing(ArrayList<? extends Animal>) list)

包,jar存档文件和部署

代码语言:javascript复制
cd MyProject/source
javac -d ../classes

javac -d 可指定class的目录,一般时source classes的形式。 从classes目录执行。

JAR:Java ARchive。是个pkzip格式文件,把一组类文件包装起来,只需交付一个JAR文件。

可执行的JAR代表用户不需要把文件抽出来就能运行。秘诀在于manifest文件,会带有JAR的信息,告诉JVM哪个类有main()方法。

创建可执行的JAR

  1. 确定所有类文件都在classes目录下
  2. 创建manifest.txt(中文:货单)来描述哪个类带有main()方法。其中内容为 Main-Class MyApp 换行
  3. 使用jar -cvmf mainfest.txt app1.jar *.class打包成app1.jar

大部分完全在本机执行的java应用程序都是以可执行JAR来部署的。

执行JAR。

代码语言:javascript复制
cd MyProject/classes
java -jar app1.jar

JVM要找到JAR,所以必须在classpath下,让jar曝光的最好方式就是放在工作目录下。

java -jar 告诉java虚拟机所给的是JAR,JVM检查JAR的Manifest寻找入口,没有入口就会发生运行时异常。

验证JAR打包的内容: jar -tf packEx.jar-tf表示table file(tf)。 META-INF/MANIFEST.MF是入口指示

代码语言:javascript复制
$ jar -tf packEx.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/headfirstjava/
com/headfirstjava/MyDrawPanel.class
com/headfirstjava/PackageExercise.class

解压JAR打包的内容: jar -xf packEx.jar-xf表示eXtract file 会把-tf的内容解压出来,并创建相应的目录。

把classes打包成包可以避免命名冲突。 类必须在完全对应于包结构的目录中才能包进包! 最好使用Domain作为前缀,这样不仅可以避免命名冲突,也可以显示一些额外的信息。 反向使用网址domain,这样只担心同公司的人的冲突。

代码语言:javascript复制
import com.headfirstjava.projects.Chart

如上,com.headfirstjava是domain名称反过来。projects是工程名。类名Chart第一个字母是大写的。

编译package并运行试验:

  • Step1: 目录结构: source/com/headfirstjava classes
  • Step2: 在headfirstjava中写类的代码,并在首行加入 package com.headfirstjava;
  • Step3: 在source目录下,执行 javac -d ../classes com/headfirstjava/*.java -d 后参数表示classes的输出目录,会自动建立对应的目录 执行后目录结构: source/com/headfirstjava classes/com/headfirstjava
  • Step 4: cd 到 classes目录下,执行java com.headfirstjava.PackageExercise。 jvm会看得懂,并找寻当前目录下的com目录,其下应该有headfirstjava目录,那里应该能找到Class。class在其他位置都无法运行!

注意!一旦类被包进包中,就不能用简写名称调用,必须执行main()所属的完整类名,包括包结构。com/headfirstjava/类名 的每一层都要匹配上。

把package,com结构打包进jar

  • Step1: 确定所有类文件在class目录下正确对应的包结构中。
  • Step2: 创建manifest.txt,指定main class Main-Class: com.headfirstjava.PackageExercise,需要把com.xxx加上。
  • Step3: 执行jar工具,创建带目录结构和manifest的JAR文件。 jar -cvmf manifest.txt packEx.jar com,其中只需要com目录就够了,其下整个包的类都会被包进JAR。

总结: 编译:source目录下,javac javac -d ../classes 执行:classes目录下,java com.xx.Class 打包:classes目录下,新建manifest, jar -cvmf manifest.txt packEx.jar com 执行:classes目录下,java -jar packEx.jar

Addtional

不变性

String具有不变性。 创建新的String时,JVM会把它放到StringPool中,如果有相同的String,JVM不会重复创建,只会引用。String不可修改,所以在for循环中建立10个String,有9个是在浪费空间。

包装类有不变性。 Integer两个用途。1. primitive主类型包装成对象。2. 使用静态的工具方法parseInt()。 Integer iWrap = new Integer(42); 它的值永远是42,没setter方法。

如何节省内存?使用StringBuilder!

断言

没打开断言时,JVM会忽略。使用断言替代println()。 使用方法:

代码语言:javascript复制
assert(height>0); // false,抛出AssertionError
assert(height>0): "height = " height "weight = " weight;

冒号后面的指令,可以是任何解出非null值的合法Java语句,千万不要在assert中改变对象状态!不然打开assertion执行时可能会改变程序的行为。

编译没有变化。 执行时 java -ea TestDriveGameEnable Assertion

Anonymous和Static Nested Classes

匿名和静态嵌套类。

静态嵌套类
代码语言:javascript复制
public class FooOuter{ 
   
	// 静态的嵌套类
	static class BarInner{ 
   
		void sayIt(){ 
   
			System.out.println("method of a static inner class");
		}
	}
}

class Test{ 
   
	public static void main(String[] args){ 
   
		// 因为是static的,所以不需要外层实例,只需要外层类名
		FooOuter.BarInner foo = new FooOuter.BarInner();
		foo.sayIt();
	}
}

静态嵌套像一般非嵌套,他们未与外层对象产生特殊关联。但因为还是外层的一个成员,所以能够存取任何外层的static的私有成员。

nested和inner的差别

除了内嵌的类,还可以匿名类直接创建对象,就在对象new出来的地方把类定义了。这个类称为匿名类。 例如:直接以interfaceActionListener占位“类名”的地方new出来一个对象。

代码语言:javascript复制
public static void main(){ 
   
	JButton button = new JButtion();
	button.addActionListener(new ActionListener(){ 
   
		public void actionPerformed(ActionEvent ev){ 
   
			System.exit(0);
		}
	});
}

存取权限和存取修饰符(谁可以看到什么)

public:完全开放 private:只对类内开放 protected:对子类及包内开放,只能用在继承上。 (啥修饰符都不加即default)default:对包内开放。同一包内default。

String和StringBuffer、StringBuilder的方法

代码语言:javascript复制
三者通用的方法:
char charAt(int index); // index位置的字符
int length(); // 字符长度
String subString(int start, int end); // 字串
String toString(); // Object的String表示值

连接字符串:
String concat(string); // String使用
String append(String); // StringBxxxx使用

only String的方法:
String replace(char old, char new); // 替换
String subString(int begin, int end); // 字串
char[] toCharArray(); // 转char数组
String toLowerCase(); // 
String toUpperCase(); // 
String trim(); // 删后面空格
String valueOf(char[]); // 转成String
String valueOf(int i); // 转成String,其他primitive主数据亦可

only StringBuffer or StringBuilder的方法:
StringBxxxx delete(int start,int end);
StringBxxxx insert(int offset, any primitive or a char[]);
StringBxxxx replace(int start, int end, String s);
StringBxxxx reverse(); // 反转
void setCharAt(int index, char ch); // 替换

多维数组

数组也是对象。

代码语言:javascript复制
int[][] a2d = new int[4][2]; // 二维数组
实际上是5个数组,一个int[4][],和4个int[2]。
操作数组:
1) 存取第三个数组的第二个元素:int x = a2d[2][1];
2) 对某个子数组创建引用: int[] copy = a2d[1];
3) 初始化2*3数组:int[][] x = { 
   { 
   2,3,4},{ 
   7, 8, 9}};
4) 创建非常规二维数组:
	int[][] y = new int[2][]; // 长度2的第一层
	y[0] = new int[3]; // 3个
	y[1] = new int[5]; // 5

枚举

枚举类型 Enum

代码语言:javascript复制
修饰符 enum关键字 类名 { 
   枚举名1, 枚举名2, ...};
public enum Members { 
   A, B, C};
public Members selectedBandMember;
selectedBandMember只能是A, B, C中的一个
enum会继承java.lang.Enum,创建enum时,其实是隐含地继承java.lang.Enum来创建新的类。
可以使用==或.equals(),或者switch-case中。

可以在enum中加入构造函数、方法、变量和特定常量内容(class body),不常见,但是可行。因为 Enum实际上是继承java.lang.Enum类,是个final类。,编译器会为我们添加静态的values()方法,所以可以用 Members.values() 返回 Members[]。 编译器会添加valueOf(String s)方法。静态初始static{实例化枚举实例}。

代码语言:javascript复制
public class HfjEnum{ 
   
	enum Names{ 
   
		// 枚举名(构造函数参数)
		JERRY("lead guitar"){ 
   
			// 为每个实例提供特定的行为
			@Override // 复写
			public String sings(){ 
   
				return "plaintively";
			}
			@Override
		    <T> T doAction(T t) { 
   
		        //your implementation
		    }
		},
		BOBBY("rhythm guitar"){ 
   
			@Override // 复写
			public String sings(){ 
   
				return "hoarsely";
			}
			@Override
		    <T> T doAction(T t) { 
   
		        //your implementation
		    }
		},
		PHIL("bass"){ 
   
			@Override // 复写抽象函数
		    <T> T doAction(T t) { 
   
		        //your implementation
		    }
		}; // 分号结尾

		private String instrument; // Enum的私有变量
		Names(String instrument){ 
   
			// enum的构造函数,传入参数见上面JERRY,BOBBY括号内的。
		}
		public String getInstrument(){ 
    // enum的方法
			return this.instrument; // 返回私有变量
		}
		public String sings(){ 
   
			return "occasionally";
		}
		abstract<T> T doAction (T t); // 可以用泛型
	}
}
String string = Names.JERRY.<String>doAction("hello"); // 可以这么调用

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/156415.html原文链接:https://javaforall.cn

0 人点赞