大家好,又见面了,我是你们的朋友全栈君。
文章目录
golang to java
golang工程师,最近开始学习一些java
Head First Java
instanceof
相当于断言
Dog d = new Dog()
Object o = d
if (o instanceof Dog) {
Dog d = (Dog)o
}
interface
在java和golang中基本一致,java中的interfece
是一个100%抽象类,所有函数都是抽象的。必须要用implements
显式指定一个接口,(可以是多个吗?可以,用逗号分隔)
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(){
}
}
type Pet interface {
beFriendly()
play()
}
golang中不需要implements,java必须要implements
final
final也可用于修饰非静态变量,表达一种不能变的概念,包括实例变量,局部变量,或者方法的参数。(不变性上类似const),还可以防止方法的覆盖或创建子类(继承体系中的末端)。
一些与众不同的设计
staic初始化
加载类时,会执行static{xxx}
,这个可以初始化staic final
变量
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表示时间。
"%tc" // 完整时间
"%tr" // 只有时间
"%tA, %tB, %td" // 周月日
"%tA, %<tB %<td", today // <符号是个特殊的指示,用来告诉格式化程序重复利用之前用过的参数,不用写多个。
静态的import
import static java.lang.System.out;
可以省略System
。
省略包名会有混淆,在golang中一般不提倡这么用。
内部类
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 内部类构造函数;
}
}
注意点:
- 内部类的实例一定会绑在外部类的实例上。
- 内部类提供了在一个类中提供同一个接口实现多次的机会。
- 使用内部类的特征:独立、却又好像另一个类成员之一。
- 使用内部类代表外部类,外部类只能单继承。内部类可以实现多个接口,通过IS-A测试。
常用包、函数
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
add(int field,int amount)
get(int field)
getInstance()
getTimeInMillis()
roll()
set()
set()
setTimeInMillis()
异常处理
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
try{
} catch (AException ex) {
} catch (BException ex){
}finally {
}
Exception也是继承体系中的,可以多态,父类引用子类实例。
throws 父类,可以抛出它的所有子类。
catch父类,可以接它所有子类,但是不应该这么做。应该具体异常具体一个catch。
duck掉的意思是不处理exception,交给调用栈的上一层去处理。
// duck the exception
void foo() throws ClothingException {
// throws 出去,交给上层处理
laundry.doLaundry()
}
异常处理规则:
- catch与finally不能没有try
- try一定要有catch或finally
- try与catch之间不能有程序
- 只带有finally的try必须要声明异常!!就是throws XXException对于try{}finally{}
序列化
把Object可以完整的保存下来,包括Object中的对其他Object的引用,序列化过程必须全部正确,如果有局部不正确,那整体也会出错。
Object -> ObjectOutputStream -> FileOutputStream -> 文件
- 标记为transient可不被序列化。
- 父类不序列化,子类可以标记为可序列化。
- 如果两个对象引用了同一个对象,那么序列化时候也只会有一份(比较聪明)。
反序列化时。
- 对象从stream中读出来
- jvm通过存储信息判断出对象的class类型
- jvm尝试寻找和加载对象的类。如果jvm找不到,就会抛出exception。
- 新的对象会被放在堆上,不会调用构造函数。
- 如果对象在继承树上有个不可序列化的祖先类,则该不可序列化类以及之上的类的构造函数就会执行,一旦构造函数连锁启动后将无法停止。也就是说从第一个不可序列化的父类开始,全部都会重新初始状态。
- 对象的实例变量会被还原成序列化时的状态值。transient变量会被赋值null的对象,或者primitive的默认0值。
文件-> FileInputStream -> InputOutputStream -> Object
newFileInputStream(“xxx”)
newObjectInputStream(fs)
Obj = (Type)is.readObject()
读取对象的顺序必须与写入的顺序相同。
写入文件和写对象差不多。
try{
FileWrite writer = new FileWriter("Foo.txt");
writer.write("hello foo!");
writer.close();
}catch ()...
File IO
import java.io.*;
class名java.io.File:
File表示磁盘上的文件,不是文件的内容。
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 集合框架,能够支持绝大多数数据结构。
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。
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。
- 若equals被覆盖,那么hashCode一定也要被覆盖,否则比较没有意义,因为会先比较hashCode。
foo.hashCode() == bar.hashCode() || foo.equals(bar)
- hashCode() 默认是取heap上的地址相关。
- equals()默认是执行==。如果equals没被覆盖,两个对象永远不会被视为相同的。
- equals()必须与hashcode == hashcode等值(推论)
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>
总结:
- 要使用TreeSet,要么集合中的元素,实现Comparable接口
- 要么使用重装,取用Comparator参数的构造函数来创建TreeSet
HashMap<Key, Value> 用put和get。
泛型
父类数组可以接受子类数组作为入参。
public void takeAnimals(Animal[] animals);
{
takeAnimals(dogs);// dog数组每个元素都继承Animal
}
但是ArrayList<Animal>不可以接受ArrayList<Dog>,会编译期失败。
对于数组来说会运行期失败。
泛型的万用字符。使用带有<?>的声明时,编译器不会让你加入任何东西到集合中!
// ? 继承或实现Animal的T
public void takeAnimals(ArrayList<? extends Animal> animals){
for (Animal a : animals){
a.eat(); // 不会报错
}
animals.add(new Cat()); // 会编译器报错
}
相同功能的另一种语法。
- 在返回类型前声明。可以只声明一次。后面都用T。
public <T extends Animal> void takeThing(ArrayList<T>) list)
- 用问号。每个地方都要声明 ?
public void takeThing(ArrayList<? extends Animal>) list)
包,jar存档文件和部署
cd MyProject/source
javac -d ../classes
javac -d 可指定class的目录,一般时source+classes的形式。
从classes目录执行。
JAR:Java ARchive。是个pkzip格式文件,把一组类文件包装起来,只需交付一个JAR文件。
可执行的JAR代表用户不需要把文件抽出来就能运行。秘诀在于manifest文件,会带有JAR的信息,告诉JVM哪个类有main()方法。
创建可执行的JAR
- 确定所有类文件都在classes目录下
- 创建manifest.txt(中文:货单)来描述哪个类带有main()方法。其中内容为
Main-Class MyApp 换行 - 使用
jar -cvmf mainfest.txt app1.jar *.class
打包成app1.jar
大部分完全在本机执行的java应用程序都是以可执行JAR来部署的。
执行JAR。
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是入口指示
$ 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,这样只担心同公司的人的冲突。
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 classMain-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()。
使用方法:
assert(height>0); // false,抛出AssertionError
assert(height>0): "height = "+height+"weight = "+weight;
冒号后面的指令,可以是任何解出非null值的合法Java语句,千万不要在assert中改变对象状态!不然打开assertion执行时可能会改变程序的行为。
编译没有变化。
执行时 java -ea TestDriveGame
Enable Assertion
Anonymous和Static Nested Classes
匿名和静态嵌套类。
静态嵌套类
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出来一个对象。
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的方法
三者通用的方法:
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); // 替换
多维数组
数组也是对象。
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
修饰符 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{实例化枚举实例}。
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"); // 可以这么调用
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/156415.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...