前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java学习笔记 head first java

java学习笔记 head first java

作者头像
全栈程序员站长
发布2022-09-08 11:43:31
5860
发布2022-09-08 11:43:31
举报
文章被收录于专栏:全栈程序员必看

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

文章目录

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 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
          • 存取权限和存取修饰符(谁可以看到什么)
          • String和StringBuffer、StringBuilder的方法
          • 多维数组
          • 枚举
      相关产品与服务
      文件存储
      文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档