Hu3sky's blog

java学习笔记

Word count: 4,250 / Reading time: 18 min
2018/07/17 Share

java

  1. 分布性:操作分布,数据分布

堆和栈

  • 存储内容:
    堆:由new等指令创建的对象和数组
    栈:基本类型的变量和对象的引用变量,还会储存对象的引用变量

    JRE 和 JDK

  • JDK,开发java程序用的开发包,JDK里面有java的运行环境(JRE),包括client和server端的。需要配置环境变量。。。。

  • JRE,运行java程序的环境,JVM,JRE里面只有client运行环境,安装过程中,会自动添加PATH。

基础语法

  • 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • 类:类是一个模板,它描述一类对象的行为和状态。
  • 方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
  • 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。

变量类型

  1. 局部变量:在方法、构造方法、语句块中定义的变量。其声明和初始化在方法中实现,在方法结束后自动销毁
1
2
3
4
5
public class ClassName{
public void printNum(){
int a;
}
}

局部变量必须初始化

  1. 成员变量:定义在类中,方法体之外。变量在创建对象时实例化。成员变量可被类中的方法、构造方法以及特定类的语句块访问
1
2
3
4
5
6
public class  ClassName{
int a;
public void printNumber(){
// 其他代码
}
}
  1. 类变量:定义在类中,方法体之外,但必须要有 static 来声明变量类型。静态成员属于整个类,可通过对象名或类名来调用
1
2
3
4
5
6
public class  ClassName{
static int a;
public void printNumber(){
// 其他代码
}
}

在一个类中,局部变量可以与成员变量同名,但是局部变量优先,如果非要访问成员变量的属性,则必须使用 this.color。this代表当前这个对象

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Puppy
{
int age=1;
public Puppy(){
System.out.println("the original age is : "+age);

}
public int Getage(int age){
this.age = age;
System.out.println("this.age is : "+this.age);
return this.age;
}
public static void main(String[] args) {
Puppy st1 = new Puppy();
System.out.println("Hu3sky's age is "+st1.Getage(20));
}
}

运行结果:

1
2
3
4
5
C:\Users\asus\Desktop (master -> origin)
λ java Puppy
the original age is : 1
this.age is : 20
Hu3sky's age is 20

super和this的异同

  • super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
  • this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
  • super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参) this:它代表当前对象名(在程序中易产生二义性之处,应使用 this 来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用 this 来指明成员变量名)
  • 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用 super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
  • super() 和 this() 类似,区别是,super() 从子类中调用父类的构造方法,this() 在同一类内调用其它方法。
  • super() 和 this() 均需放在构造方法内第一行。
  • 尽管可以用this调用一个构造器,但却不能调用两个。
  • this 和 super 不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
  • this() 和 super() 都指的是对象,所以,均不可以在 static 环境中使用。包括:static 变量,static 方法,static 语句块。
  • 从本质上讲,this 是一个指向本对象的指针, 然而 super 是一个 Java 关键字。
  • 修饰符

    public

    private

    protected

    父类的,只有基类能够访问
    例如
1
2
3
4
5
6
7
8
9
10
11
class AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// 实现细节
}
}

class StreamingAudioPlayer extends AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// 实现细节
}
}

如果把 openSpeaker() 方法声明为 private,那么除了 AudioPlayer 之外的类将不能访问该方法。

如果把 openSpeaker() 声明为 public,那么所有的类都能够访问该方法。

如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected。

default

访问控制和继承

  1. 父类中声明为 public 的方法在子类中也必须为 public。

  2. 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。

  3. 父类中声明为 private 的方法,不能够被继承。

    构造器

  • 子类不能继承父类的构造器(构造方法或者构造函数),如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

  • 如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

  • 不能在子类中使用父类构造方法名来调用父类构造方法。 父类的构造方法不被子类继承。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(); 作为子类构造方法的第一条语

重写

  1. 参数列表必须完全与被重写方法的相同;
  2. 返回类型必须完全与被重写方法的返回类型相同;
  3. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
  4. 父类的成员方法只能被它的子类重写。
  5. 声明为final的方法不能被重写。
  6. 声明为static的方法不能被重写,但是能够被再次声明。
  7. 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
  8. 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
  9. 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  10. 构造方法不能被重写。
  11. 如果不能继承一个方法,则不能重写这个方法。

    重载(Overload)

    exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person{
int age;
String a;
public void test(int age){
System.out.println("your age is "+age);
}
public void test(String a){
System.out.println("the String is "+a);
}
public static void main(String[] args) {
Person st1 = new Person();
st1.test(20);
st1.test(5);
st1.test("hhhhhhhhuuuuuuuuuuu3sky");
}
}

循环

while

do while

for

增强型for循环

1
2
3
4
for(声明语句 : 表达式)
{
//代码句子
}

包装类

Number类

Integer、Long、Byte、Double、Float、Short

Math类

1
2
3
4
5
6
7
8
9
10
11
public class Test {  
public static void main (String []args)
{
System.out.println("90 度的正弦值:" + Math.sin(Math.PI/2));
System.out.println("0度的余弦值:" + Math.cos(0));
System.out.println("60度的正切值:" + Math.tan(Math.PI/3));
System.out.println("1的反正切值: " + Math.atan(1));
System.out.println("π/2的角度值:" + Math.toDegrees(Math.PI/2));
System.out.println(Math.PI);
}
}

封装

  1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
1
2
3
4
public class Person {
private String name;
private int age;
}

这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

  1. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:·
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person{
private String name;
private int age;

public int getAge(){
return age;
}

public String getName(){
return name;
}

public void setAge(int age){
this.age = age;
}

public void setName(String name){
this.name = name;
}
}

抽象类

  • 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
  • 在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
    public abstract class ClassName子抽象类继承,并不是说”一定要调用父类的显性构造器”,而是子类在继承父类时,如果父类的显式构造器中有参数,子类要声明给出这个参数。这是一个关于继承的问题。

接口

  • 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类
  • 接口中的成员变量只能是 public static final 类型的。
  • 接口的成员特点:

    1. 成员变量只能是常量,默认修饰符 public static final
    2. 成员方法只能是抽象方法。默认修饰符 public abstract
      所以,Java 接口中,使用变量的时候,变量必须被赋值。

      接口的声明

1
2
3
4
[可见度] interface 接口名称 [extends 其他的类名] {
// 声明变量
// 抽象方法
}

接口的实现

...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
exp:public class MammalInt implements Animal

抽象类和接口的区别

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    标记接口

    最常用的接口。标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情
1
2
public interface EventListener
{}

JAVA包

StringBuffer和 StringBuilder 类

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。

1
2
3
4
5
6
7
8
public class Person{
public static void main(String[] args) {
StringBuffer st = new StringBuffer("hu3sky's blog is ");
st.append("hu3sky");
st.append(".ooo");
System.out.println(st);
}
}

StringBuilder 的方法不是线程安全的(不能同步访问)
StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类

数组

语法
Type [ ] name;

创建数组

arrayRefVar = new dataType[arraySize];
例如 double[] myList = new double[size];
或者
double [] List={1,2,3,4}

异常处理

常见异常:

  • 用户输入非法数据
  • 要打开的文件不存在
  • 网络中断,或者JVM内存溢出

Java流 文件 和 I/O

从控制台读取输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流.BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。

Scanner 类

hasNext 与 hasNextLine 判断是否还有输入的数据。hasNexInt()和hasNxtFloat().判断是否是int或float。

next()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner; 

public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据

// next方式接收字符串
System.out.println("next方式接收:");
// 判断是否还有输入
if (scan.hasNext()) {
String str1 = scan.next();
System.out.println("输入的数据为:" + str1);
}
scan.close();
}
}

执行以上程序输出结果为:

$ javac ScannerDemo.java
$ java ScannerDemo
next方式接收:
runoob com
输入的数据为:runoob

nextLine()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner;

public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据

// nextLine方式接收字符串
System.out.println("nextLine方式接收:");
// 判断是否还有输入
if (scan.hasNextLine()) {
String str2 = scan.nextLine();
System.out.println("输入的数据为:" + str2);
}
scan.close();
}
}

执行以上程序输出结果为:

$ javac ScannerDemo.java
$ java ScannerDemo
nextLine方式接收:
runoob com
输入的数据为:runoob com

next()与nextLine() 区别:

next():

  1. 一定要读取到有效字符后才可以结束输入。
  2. 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
  3. 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
  4. next() 不能得到带有空格的字符串。

nextLine():

  1. 以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
  2. 可以获得空白。

Java序列化和反序列化

序列化

一个类的对象要想序列化成功,必须满足两个条件:

该类必须实现 java.io.Serializable 对象。

该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

如果你想知道一个 Java 标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。
例子(Person.java):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.*;
class People implements java.io.Serializable
{
public String name;
public int age;
public void peopleCheck()
{
System.out.println("He is "+name+",his age is "+age);
}
}
public class Person
{
public static void main(String[] args) throws IOException{
People st1 = new People();
st1.name="hu3sky";
st1.age=19;
FileOutputStream fileOut = new FileOutputStream("C:/Users/asus/Desktop/1.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(st1);
fileOut.close();
out.close();
System.out.println("the serialize has done");

}
}

People类实现了Serializable 接口。

反序列化

ObjectOutputStream 类用来序列化一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.*;
class People implements java.io.Serializable
{
public String name;
public int age;
public void peopleCheck()
{
System.out.println("He is "+name+",his age is "+age);
}
}
public class Un{
public static void main(String[] args) throws IOException,ClassNotFoundException{
People st = null;
FileInputStream fileIn=new FileInputStream("C:/Users/asus/Desktop/1.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
st=(People) in.readObject();
in.close();
fileIn.close();
System.out.println("Deserialized Employee...");
System.out.println("Name: " + st.name);
System.out.println("Age: " + st.age);
}
}

注意!!!:
readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象,它必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个 ClassNotFoundException 异常。

写成同一个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//引入必要的java包文件
import java.io.*;

//创建测试类,注意要继承Serializable接口
class serialdemo implements Serializable{
public static int number;
public serialdemo(int inputnum) {
this.number = inputnum;
}
}

//主类
public class test{
//测试主类
public static void main(String[] args) throws IOException, ClassNotFoundException {
//主函数入口
serialdemo object = new serialdemo(100);
FileOutputStream fileoutputstream = new FileOutputStream("serail.ser");//创建文件写入对象
ObjectOutputStream outputstream = new ObjectOutputStream(fileoutputstream);//创建类型序列化通道对象
outputstream.writeObject(object);//把类对象(实例)序列化进入文件
outputstream.close();
FileInputStream fileinputstream = new FileInputStream("serail.ser");//从文件读取对象
ObjectInputStream inputstream = new ObjectInputStream(fileinputstream);//对象反序列化
// 通过反序列化恢复对象obj
serialdemo object2 = (serialdemo)inputstream.readObject();
System.out.println("反序列化后的对象的值:");
System.out.println(object2.number);
inputstream.close();
}
}

Java反序列化漏洞

重写readObject()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.*;
public class Un{

public static void main(String[] args) throws IOException, ClassNotFoundException {

People st1 = new People();
FileOutputStream fileOut = new FileOutputStream("C:/Users/asus/Desktop/1.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(st1);
out.close();
FileInputStream st2 = new FileInputStream("C:/Users/asus/Desktop/1.ser");
ObjectInputStream in = new ObjectInputStream(st2);
People st3 = (People)in.readObject();
in.close();
}
}

class People implements Serializable{
public String name;
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException
{
Runtime.getRuntime().exec("calc.exe");
}
}

运行结果会打开计算器。

CATALOG
  1. 1. java
  2. 2. 堆和栈
  3. 3. JRE 和 JDK
  4. 4. 基础语法
  5. 5. 变量类型
  6. 6. super和this的异同
  7. 7. 修饰符
    1. 7.1. public
    2. 7.2. private
    3. 7.3. protected
    4. 7.4. default
    5. 7.5. 访问控制和继承
  8. 8. 构造器
  9. 9. 重写
  10. 10. 重载(Overload)
  11. 11. 循环
    1. 11.1. while
    2. 11.2. do while
    3. 11.3. for
    4. 11.4. 增强型for循环
  12. 12. 包装类
    1. 12.1. Number类
    2. 12.2. Math类
  13. 13. 封装
  14. 14. 抽象类
  15. 15. 接口
    1. 15.1. 接口的声明
    2. 15.2. 接口的实现
    3. 15.3. 抽象类和接口的区别
    4. 15.4. 标记接口
  16. 16. JAVA包
  17. 17. StringBuffer和 StringBuilder 类
  18. 18. 数组
    1. 18.1. 创建数组
  19. 19. 异常处理
  20. 20. Java流 文件 和 I/O
  21. 21. Scanner 类
    1. 21.1. next()
    2. 21.2. nextLine()
    3. 21.3. next()与nextLine() 区别:
  22. 22. Java序列化和反序列化
    1. 22.1. 序列化
    2. 22.2. 反序列化
    3. 22.3. 写成同一个文件
  23. 23. Java反序列化漏洞
    1. 23.1. 重写readObject()