写在前面
这篇blog大佬们看着乐一下就好了哈。
本想全部整理一遍的,可是事情太多了,现在只能整理部分的点了,那些没有总结点的章节只能说明我还没遇到过错的或者说我不想滥竽充数抄书本什么的把,我想记录的是一些自己的理解和实操过的结论,整理得挺匆忙,非常欢迎纠错或补充。这篇blog针对的是清华大学出版社的《Java程序设计基础(第6版)》,前面两章没什么容易错的点把,直接从第三章开始把。奉上亲手拍的锦鲤,应该比ycy好用哈哈,大家冲鸭!
第三章 java语言基础
1.Scanner读取的next方法有nextByte(),nextInt(),nextDouble(),nextFloat(),nextLong(),nextShort(),next(),nextLine()8种。
(1) 没有nextChar()。
(2) 要加括号!
(3) nextLine能读空格,next不可以。
第五章 数组与字符串
1.str.matches(regx)表示str是否符合regx这个正则表达式。
2. 声明数组的时候注意new右边是[]而不是()。
int[] arr=new [10];
3.常量池,字符串池,字符串常量池的引用机制。
(1) 常量池:区别于变量,引用存储在栈中,对象存储在堆中,java中常量(666,“yyds”这种就是常量)存储在方法区中,方法去中存放常量的这一部分空间叫做常量池。
reference1 Java堆.栈和常量池
(2) 字符串池:就是常量池中存储字符串池的部分。因为String本身时一个对象,且使用频繁,如果每次使用都用new去开辟堆内存空间的话太耗时,所以开辟了另外一个空间和引用机制,方便使用。
reference2 字符串常量池
reference3 What is the difference between “text” and new String(“text”)?
(3) 字符串常量池的引用机制:理解都写在注释里边的,想了解清楚的话仔细看看把。
public class StringPool {
public static void main(String[] args) {
String str1 = "abc";
/*
* str1的创建过程,首先jvm会从常量池中查找是
* 否已经有对象的值是abc了,发现没有,所以会新
* 建一个对象存储在方法区中并把这个地址返回给str1引用。
*/
String str2 = "abc";
/*
* str2的创建过程,首先jvm会从常量池中查找,
* 发现存在值为"abc"的对象,发现存在所以会把这
* 个值对应的引用返回给str2
*/
String str3 = new String("ghi");
/*
* str3的创建过程,首先jvm会从常量池中查找,
* 发现不存在值为"ghi"的对象,所以会在常量池
* 中新建一个值为ghi的对象,并把这个对象的引用返回,
* 然后jvm再根据返回的这个引用所对应的值进一步复制一份,
* 在堆内存中分配空间构建一个新对象,值为ghi。综上,真
* 没必要用这个方法创建String对象。
*/
String str4=str3.intern();
/*
* str4的创建过程,首先jvm会从常量池中查找,
* 发现存在值为str3(ghi)的对象,发现存在,所以会把这
* 个值对应的引用返回给str4
*/
if(str1==str2){
System.out.println("str1 and str2 are the same.");
}else System.out.println("str1 and str2 are different.");
if(str3==str4){
System.out.println("str3 and str4 are the same.");
}else System.out.println("str3 is in heap while the str4 is in string constant pool.");
}
}
/* Output:
* str1 and str2 are the same.
* str3 is in heap while the str4 is in string constant pool.
*/
4.数组赋初值用{},Array的length没有(),String有括号()。
int[][] arr = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
System.out.println(arr.length); // 输出:3
String str = "1234";
System.out.println(str.length()); // 输出:4
第六章 类与对象
1.java"源文件中可以包含多个类,但是只能有一个类用public修饰,并且该类名与文件名一致。
2.Java 中的数据类型分为基本数据类型和复杂数据类型。int 是前者而integer 是后者(也就是一个类);因此在类进行初始化时int的变量初始为0.而Integer的变量则初始化为null。
reference java int与integer的区别
3.String属于引用的一种,而引用的初始值是null。
4. 数据域封装的意思是加了个private修饰符。同时需要在定义这个类的时候要加上setValue,getValue的方法或类似的方法。
5. 访问封装的数据域的方法命名要看被访问数据的类型,如果是int,char,String这种一般用getXXX,如果是boolean这种,一般用isXXX。
6. 形参一般是函数头中的声明的变量,实参一般指主函数(或其他传入其他函数)的值。
7. 注意static修饰的变量被所有实例对象共享,也就是说所有的实例(包括但不限于对象)都可以通过类名.变量名访问,改变它的值,相当于全局变量。
8. 使用方法(非主方法)只能在方法里边使用(包括主方法,构造方法)。
9. default,默认方法,缺省 都是一个东西。
第七章 java语言类的特性
1.成员变量,类变量,局部变量区别
(1) 局部变量:在方法中定义的变量,声明和初始化在方法中实现,方法结束后销毁。
(2) 成员变量:定义在类中,方法体之外,必须要通过创建对象实例化之后才能调用。
(3) 类变量:成员变量的基础上加上static修饰。可以直接在类中调用不用实例化对象。
2. 构造方法没有返回值。
3. 类的成员变量可以不用初始化,程序会给变量赋以默认的初始值,但是局部变量可以则一定要初始化变量。
4. 被static修饰的变量是可以再次被修改的,只有像fianl修饰的才是不可修改的。
reference 被static修饰的成员变量可以被修改吗
5.调用构造方法不代表创建对象,new才是创建的对象的,后面接的构造方法名只是用来初始化。因此每次实例化子类之前jvm都会调用父类的构造方法进行参数初始化方便子类使用。所以又推出一个结论,如果子类想要使用父类的数据域或者方法,必须先要构造一个子类对象,因为父类的构造方法要声明在子类的构造方法里边的一行或默认。
6.无参的构造方法并不是说一定不用去定义,如果定了有参的构造方法,同时又想使用无参的构造方法,就必须显式地定义无参构造方法。
7.在静态方法里边是不能直接使用非静态成员的,因为静态方法在编译的是时候已经构建,非静态成员必须要在运行时通过实例化对象之后才能调用。
8. 静态方法是类的方法,可以被子类继承但是被子类覆盖,如果子类中也有了相同的静态方法,这个关系叫隐藏,但是使用**@Override**还是会报错的。
class Main {
int i = 1;
int j = 2;
public static void main(String[] args) {
// System.out.println(i + j); // 变异错误
Main m = new Main();
System.out.println(m.i + m.j);
}
}
第八章 继承、抽象类、接口和枚举
1.抽象类不一定包含抽象方法,但包含抽象方法的类一定要声明为抽象类,且抽象类的子类要么实现所有的抽象方法,要么就继续声明为抽象,否则报错。
2.实现接口的类则不需要实现接口的所有抽象方法。
3. 抽象方法是不用声明方法体的,非抽象方法都要有方法体。
4. 接口可以没有抽象方法,同时也可以有静态方法和默认方法,默认方法是接口独有的一种方法类型,他们两者的区别就是静态方法不能被覆盖,默认方法可以被覆盖。下面是一个用法的demo,记得看一下注释哦!
interface Inter {
default void say() {
System.out.println("真的不用实现");
}
static void run() {
System.out.println("静态方法");
}
}
class Child implements Inter {
public static void main(String[] args) {
Child child = new Child();
// Child.run(); // 如果这样调用接口中的静态方法的话会报错
Inter.run(); // 这样就不会报错了,同理抽象类也是这种情况。这里再啰嗦对比一下,如果是非抽象类中的静态方法的话,是可以用实例去调用的,只是会报warning而已,所以干脆以后调用静态方法都用“类名/接口名.方法名”
}
}
5.重载,覆盖,实现三者的区别
(1) 重载:同一个类内定义多个名称相同但参数个数或类型不同的方法。
(2) 覆盖:子类中定义名称,参数个数与返回类型均与父类中完全相同的方法,用以重写父类中的功能,针对的是类与类的关系。注意如果父类方法中返回的是父类,而子类继承中返回的是子类,也满足条件。
(3) 实现:针对的实现父类或者接口中的抽象方法同样也需要方法名,参数列表,返回类型要相同。
6. 接口中的都是public的,如果在定义接口的时候没有省略了public修饰符,在实现抽象方法时要加上public,另外接口中的数据成员必须都是静态的且必须赋初值。
7. 抽象类也可以实现接口。
8. 虽然接口中的数据成员没有说一定要用static声明,但是实际上它已经默认时static,final的。
9. 类(不包括接口)之间的关系:继承,依赖,关联,组合,聚合(锯竹管技艺)
reference 重新认识java(四) — 组合、聚合与继承的爱恨情仇
10. 如果一个方法的返回的是引用类型,则该方法可以返回当前类,当前类的子类,当前类的父类。
11. 多态问题,查了很多文章(排除了翻译的问题),overload(静态多态)和override(动态多态)都是多态性的体现,前者的多态主要是在编译过程中体现,后者是在运行时体现。而常说的“多态的三个必要条件”一般指动态多态性。
reference1 java 多态的必要条件,弊端与优势
reference2 深入理解 Java 的三大特性之多态
reference3 polymorphism-vs-overriding-vs-overloading
12. 因为所有类的父类都是Object,还需要注意一点,多个构造方法可以一直this进行递归下去,只要处于最上面的那个构造方法有用super或者用虚拟机默认的super()即可。
构造方法第一句不是this或者super时,表示这里默认有一个super()语句被省略了。
13.父类的静态和private方法是不能被覆盖的(protected可以),但是可以被子类继承,也就是说可以在子类中直接写上这个静态方法名进行调用或者父类名.方法名。
14.Object对象有几个经常用于重写的方法
(1) public boolean equals(Object obj):因为“==”只能用在基本数据类型中的比较,而如果想比较引用之间的关系,需要通过重写equals方法来覆盖。问题来了,为什么当我们比较String引用的时候没有去覆盖就可以直接用equals来比较值呢,因为开发者们已经在String类中写好了equals覆盖方法了,下面是源码。如果我们想要比较自己写的类的两个对象是否相等的话就需要自己去覆盖equals方法了。
public boolean equals(Object anObject) {
if (this == anObject) { //比较两个对象之间的地址是否相等
return true;
}
if (anObject instanceof String) { //类型比较
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) { //比较内容是否相等
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
(2) public String toString():将调用toString()方法的对象转换成字符串,下面是一个例子。
public String toString() {
String str = "实数部分是" + real + "虚数部分是" + image;
return str;
}
(3) public final Class getClass():返回当前对象所属的类,直接对象名.getClass()即可。
(4) protected Object clone():返回调用该方法的对象的一个副本(副本的意思就是说,里边的变量全部拷贝一份,方法也拷贝一份,但是地址是不同的),但是前提这个对象所属的类必须实现clone方法,下面举个实现clone方法的demo,中间也有实现数据封装,对象比较等过程。
/**
* 定义了一个复数的类 实现比较,克隆功能
*/
class ComplexNumber implements Comparable, Cloneable {// Comparable接口是为了实现里边的compareTo方法,Cloneable是为了实现clone方法
private double real; // 数据域封装
private double image;
public double getReal() {
return real;
}
public double getImage() {
return image;
}
public void setReal(double real) {
this.real = real;
}
public void setImage(double image) {
this.image = image;
}
public double mod() {
return Math.sqrt(real * real + image * image);
}
ComplexNumber(double real, double image) {
this.real = real;
this.image = image;
}
public int compareTo(Object o) { // 注意实现的时候要保证方法签名要完全一致,同时还要注意实现接口的方法最后一定要改为public
ComplexNumber another = (ComplexNumber) o;
if (mod() > another.mod()) { // 反面思考,减少代码量
return 1;
} else if (real > another.getReal()) {
return 1;
} else if (image > another.getImage()) {
return 1;
} else
return 0;
}
public String toString() { // toString方法覆盖
String str = "实数部分是" + real + "虚数部分是" + image;
return str;
}
public Object clone() { // clone实现
ComplexNumber num = null;
try {
num = (ComplexNumber) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return num;
}
}
class Test {
public static void main(String[] args) {
ComplexNumber num1 = new ComplexNumber(1.0, 2.0);
ComplexNumber num2 = new ComplexNumber(1.0, 1.9);
System.out.println("num1的" + num1.toString());
System.out.println("num2的" + num2.toString());
ComplexNumber num1copy = (ComplexNumber) num1.clone();
num1copy.setImage(3.0);
System.out.println("num1copy的" + num1copy.toString());
if (num1.compareTo(num2) > 0) {
System.out.println("num1比较大");
} else {
System.out.println("num2比较大或相等");
}
}
}
/* 输出:
* 实数部分是1.0虚数部分是2.0
* 实数部分是1.0虚数部分是1.9
* 实数部分是1.0虚数部分是3.0
* num1比较大
*/
15. 关于方法继承个数问题。
当Sub类和Base类在同一个包时Sub类继承Base类中的public/protected/默认(就是没有修饰符)级别的变量个方法在不同包时继承public/protected级别的变量和方法。
16. 向下转型和向上转型,都写在注释上了。
class Parent {
void funcOfParent() {
System.out.println("父类的方法");
}
void funcOnlyInParent() {
System.out.println("父类的中还没有被覆盖的方法");
}
}
class Kid extends Parent {
void funcOfParent() {
System.out.println("父类的方法已经子类被覆盖");
}
void funcOfChild() {
System.out.println("子类的方法");
}
public static void main(String[] args) {
Kid kid1 = new Kid(); // 正常赋值
Parent kid2 = new Kid(); // 向上转型
Kid kid3 = (Kid) kid2;// 这种就是向下转型,将原本的父类引用降级为同级引用
kid1.funcOfParent();
kid1.funcOfChild();
kid2.funcOfParent();
// kid2.funcOfChild();// 无法访问,因为此时的kid2是父类引用,只能引用父类本身的方法和子类中覆盖了父类的方法
kid3.funcOfParent();
kid3.funcOfChild(); // 将kid2实现向下转型之后就能够访问子类的方法了
kid3.funcOnlyInParent(); // 而且还可以访问父类中未被覆盖的方法,从而实现了kid1一样的功能
}
}
/* 输出:
* 父类的方法已经子类被覆盖
* 子类的方法
* 父类的方法已经子类被覆盖
* 父类的方法已经子类被覆盖
* 子类的方法
* 父类的中还没有被覆盖的方法
*/
17. 接口的抽象方法是不用特别去加上abstract修饰符的,但是抽象类就需要加上。
18. 实现接口的方法之后一定要加上public!!!
19. 如果一个方法是被缺省修饰符修饰的话,只能被同一个包中的子类所继承和覆盖,不能被其包里面的源程序继承和覆盖,protected和public则可以被其他包继承和覆盖。
20. instanceof 是用于测试一个指定对象是否是指定类或它的子类的实例,若是,则返回true。
第九章 异常处理
1. 错误分为三种,语法错误,运行时错误,逻辑错误。
(1) 语法错误:java编译系统在编就能发现检查出错误。
(2) 逻辑错误:编译系统是无法发现错误的,错误需要人为去发现排除错误。
(3) 运行错误:Java中通过异常处理机制处理。
第十章 java语言的输入输出与文件处理
1.java.io包中有四个大的抽象类:InputStream、OutputStream、Reader、Writer。
2.InputStream、OutputStream是针对字节流的抽象类,他们常用也是核心的抽象方法有read,skpi,write,flush这些都是要靠子类去实现的,实现的子类有下面几种
(1)文件字节流: FileInputStream、FileOutputStream
import java.io.*;
class BitStream {
public static void main(String[] args) throws Exception {
// FileInputStream fin = new FileInputStream(FileDescriptor.in); // 构造从控制台输入的方法
FileInputStream fin = new FileInputStream("in"); // 构造从控制台输入的方法
byte[] arr = new byte[100]; // 只能够使用byte数组来作为承接
int len = fin.read(arr); // 读取到的字节数
FileOutputStream fout = new FileOutputStream("out");// 构造方法
fout.write(arr);
}
}
(2) 顺序输入流:SequenceInputStream
(3) 管道字节流:PipedInputStream、PipedOutputStream
(4) 过滤字节流:FilterInputStream、FilterOutputStream这两个其实也是抽象类,DataInputStream,DataOutputStream是他们的具体实现子类,作用就是在其他的流中“挑出”一些数据。
import java.io.*;
class BitStream {
public static void main(String[] args) throws Exception {
FileInputStream fin = new FileInputStream(FileDescriptor.in);
DataInputStream din = new DataInputStream(fin);
int tmp = din.readInt(); // readInt的作用是读取从头到尾的前四个字节
FileOutputStream fout = new FileOutputStream("out");
DataOutputStream dout = new DataOutputStream(fout);
dout.writeInt(tmp); // 写入的是tmp中前4个字节
}
}
字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
3.Reader、Writer是字符流,核心,常用的几个抽象方法也是read,skip,write,flush。下面是具体的实现子类。
(1) 文件字符流:FileReader,FileWriter,这两者都是继承自InputStreamReader,OutputStreamWriter,但是这两个也是继承自Reader和Writer。
import java.io.*;
class CharStream {
public static void main(String[] args) throws IOException {
FileReader fin = new FileReader("in");
char[] arr = new char[100]; // 要用char数组作为承接参数
int num = fin.read(arr);
String str = new String(arr, 0, num);// 将字符数组转化为字符串的方法,记录一下吧
System.out.println(str);
FileWriter fout = new FileWriter("out", true); // true是追加不覆盖
fout.write(arr);
fout.flush(); // 字符流必须要使用flush才能将write的参数输出,而字节流可以不用,flush在字节流的作用是将缓冲区的数据强制输出到控制台
}
}
(2) 缓冲字符流:BufferedReader,BufferedWriter
import java.io.*;
class CharStream {
public static void main(String[] args) throws IOException {
FileReader fin = new FileReader("in");
BufferedReader bin = new BufferedReader(fin);
String str = bin.readLine();
FileWriter fout = new FileWriter("out", true); // true是追加不覆盖
BufferedWriter bout = new BufferedWriter(fout);
bout.write(str, 0, str.length());// 将str整个写进去,当然普通的read和write还是用char数组作为承接的
bout.flush(); // 字符流必须要使用flush才能将write的参数输出,而字节流可以不用,flush在字节流的作用是将缓冲区的数据强制输出到控制台
}
}
4. 结合容器Scanner的io流更加方便,PrintWriter则是Writer的一个子类,也可以很方便的写入文件。
import java.io.*;
import java.util.*;
class ScannerPrintWriter {
public static void main(String[] args) throws IOException {
Scanner reader = new Scanner(new FileReader("in"));
double num = reader.nextDouble();
reader.close();
// System.out.println(num);
PrintWriter writer = new PrintWriter(new FileWriter("out"), true);// 追加
writer.println(num);
writer.flush(); // 一定要加这个才能将成功写入
}
}
第十二章 泛型与容器类
1.容器都装在util包中,直接java.util.*;导入即可。常用的也就是Scanner,Array。
2.java.lang.*;这个包是默认的,Math就是里边的一个类,所以如果想用Math.random()或者Math.sqrt()的时候是不用额外导包。
3.Scanner中的nextLine是以回车为分隔符,但是读不进回车的,next是以空格或者回车为分隔符,同样也读不进分隔符。
空缺的有空再来总结把。
都看到这里啦,不点个赞再走嘛?