Java入门笔记(五)java深入理解

Java入门笔记目录

变量及其传递

变量

  1. 基本数据类型与引用数据类型:基本数据类型的值直接存在变量中,也就是栈中的,而引用类型的变量占据一定的存储空间(指针),他引用的实体也要占据空间,是在堆中的
Person a = new Person();
Person b = a;
// b和a表示的是同一个实例的对象,即a和b都指向同一块存储空间
  1. 字段变量与局部变量:字段变量是对象的一部分,在堆中,随着对象的产生创建,可以自动赋初始值;局部变量是方法中定义的变量或者参变量,存在栈中,只有在方法调用的时候才被创建,必须显式赋值,不能够被访问控制符以及static修饰

变量的传递

java是值传递,对于引用型变量,传递的是引用值,而不是复制实体,但是可以通过引用改变对象属性

void modify(int a){ a++; }
void modify(int[] b) {b[0] ++; b = new int [5];}
void main(){
      int a = 0;
      modify(a); // 此时a仍旧为0
      int b = new int [1];
      modify(b); // 此时b[0]就是1了,因为modify中第二句的new其实是对b的复制的引用的修改,而不是当前的b
}

多态

编译时多态:重载

运行时多态:覆盖、动态绑定(虚方法调用)

上溯造型:把派生类型当做基本类型,如Person p = new Teacher();或者person做形参时传入Teacher

虚方法调用

  1. 虚方法调用:子类重载父类方法时,运行时系统根据调用该方法的实例的类型来选择哪个方法调用,此时所有的非final方法都会自动进行动态绑定,这就是虚方法调用的过程
Person{
      sayHello(){}
}

Teacher extends Person{
      sayHello(){ code1 }
}

Student extends Person{
      sayHello(){ code2 }
}

Hello(Person p){ p.sayHello(); }

main(){
      Hello(Teacher); // code1
      Hello(Student); // code2
}
  1. 动态类型确定:instanceof可以用来判断是否是某个实例究竟是什么对象,返回boolean值,如果是当前类型或者是他的子类型,会返回true
void main(){
      Object [] things = new Object[2];
      things[0] = new Integer(4);
      things[0] = new double(3.14);
      if(things[0] instanceof Interger){} // true
      if(things[1] instanceof double){} // true 
}
  1. java的默认方法都是虚方法,static private的方法不是虚方法调用,他们与虚方法编译后用的指令是不同的,invokevirtual/invokespecial/invokestatic

  (1) static方法一声明类型为准,与实例类型无关
  (2) private方法子类不可见,不会被虚化
  (3) final方法子类不能覆盖,不存在虚化问题

对象构造与初始化

构造方法

对象都有构造方法,如果没有,编译器会添加一个default构造方法,抽象类也是有构造方法的

this调用本类的其他构造方法、super调用父类的构造方法,但是this和super只能有一条,且要放在第一句。如果既没有this也没有super,编译器会自动加上父类的构造方法(优先使用默认构造方法,如果已有自定义构造方法就会调用自定义的构造方法)

class A{
      A(int a){}
}

class B extends A{
      B(String b){} // 报错,无法正确调用A的构造方法A(int a)
}

构造方法的执行过程

步骤:

  1. 调用本类或父类的构造方法,直到最高一层

  2. 按照声明顺序执行字段的初始化赋值

  3. 执行构造函数中的各语句

public class Test{
      int a = 2000;
      Test(){
            this.a = 3000;
      }
}
// 先调用object的super构造方法,然后执行int a = 2000;,最后执行构造函数中的语句

Q:如果构造方法中有虚方法怎么办?语法合法但是不合理,所以尽可能避免调用方法,唯一能够安全调用的是具有final属性的方法

初始化方法

  1. 创建对象时初始化:当没有构造函数却又要赋值的时候可以使用双括号Person p = new Person(){{age=18; name="李明" }};

  2. 实例初始化:在类中直接用写{语句},会先于构造方法中的语句执行

  3. 静态初始化:static {语句}第一次使用这个类时执行,执行时机,但是一定在实例初始化之前

public class Test{
    Test(int a){
        this.a = a;
        System.out.println("构造方法初始化 a="+a);
    }
    int a;
    {
        System.out.println("实例初始化");
    }
    static{
        System.out.println("静态初始化");
    }
    public static void main(String args[]){
        Test test = new Test(4);
    }
}

对象清除与垃圾回收

垃圾回收线程

java中,不需要认为使用delete清除对象,java可以自动清除,也就是垃圾回收

垃圾回收由java虚拟机的垃圾回收线程完成,任何对象都有一个引用计数器,当值为0时,对象就可以被回收 (很早期的算法了)

可以使用static方法System.gc()要求系统进行垃圾回收,也只是一个建议,没办法强制进行

finalize()方法

protected void finalize() throws Throwable{}

java中没有析构方法,但是Object的finalize有类似的功能,系统再回收时会自动调用对象的finalize()方法,一般来说,子类的finalize方法中应该调用父类的finalize方法,一般是不会用的

try-with-resource

关闭打开的文件、清除一些非内存资源等工作可以用try-with-resource,对于实现了java.lang.AutoCloseable的对象可以使用,最后会调用其close()方法

try(Scanner scanner = new Scanner(...)){

}

内部类 Inner class

定义

  1. 将一个类的定义嵌入到另一个类的内部,这个类就是内部类,生成xxx$xxx这样的class文件

  2. 在封装它的类的内部使用内部类则与普通类相同,在其他地方使用时需要冠上外部类的名字,也要在new前面冠以对象变量

public class Test{
    int a = 2000;
    public Inner in;
    Test(){
        this.a = 3000;
    }
    class Inner{
        private int i;
        Inner(int i){
            this.i = i;
        }
        int value(){
            return this.i;
        }
    }
    void setIn(Inner in){
        this.in = in;
    }
    void printValue(){
        System.out.println(this.in.value());
    }
    public static void main(String args[]){
        Test test = new Test();
        Test.Inner in = test.new Inner(5);
        test.setIn(in);
        test.printValue();
    }
}
  1. 内部中可以直接访问外部类的字段和方法,即便是private也可以

  2. 如果内部类和外部类有同名的字段或方法,则可以用外部类名.this.字段/方法的写法

修饰符

  1. 内部类可以正常修饰,但是外部类要用public

  2. 如果用static修饰内部类,那么内部类其实是一种外部类,也有人认为是嵌套类(nested calss)

  (1) 实例化static时,new前面就不需要对象实例了

  (2)static内部类不能访问外部类中的非static字段和方法

  (3)static方法中不能访问非static的域及方法,也不能够不带前缀地new一个非static的内部类

Outer.Inner io = new Outer.Inner();

局部类 local class

在一个函数中定义的类教方法中的内部类,也叫局部类

class Outer{
      private int a = 100;
      public Object makeTheInner(){
            final int finalLocalVar = 99;
            class Inner{
                  public String toString(){
                        return ("Inner class"+finalLocalVar);
                  }
            }
            return new Inner();
      }
}
  1. 和局部变量一样,不可以用public private protected static修饰,但是可以用final或者abstract修饰

  2. 可以访问外部类的成员,但是不能访问该方法中的局部变量,除非是final局部变量(方法结束之后局部变量就消失了,但是final局部变量是只读的,可以预测的)

匿名类 anonymous class

局部类通常定义万了就使用,所以我们更多使用匿名类,匿名类在定义的同时就生成了该对象的一个实例,是一个一次性使用的类,一般用来扩展一个类或者实现一个接口

class Outer{
      private int a = 100;
      public Object makeTheInner(){
            final int finalLocalVar = 99;
            return new Object(){
                  public String toString(){
                        return ("Inner class"+finalLocalVar);
                  }
            }
      }
}
  1. 不取名字,直接使用父类或者接口的名字,编译器会生成xxxx$1之类的名字

  2. new 类名或者接口名(){}

  3. 构造对象时使用父类的构造方法,因为它没有名字,如果new 对象时要带参数,就用父类中这样的构造方法,一般很少带参数

  4. 典型应用

  (1)注册一个事件监听器

  (2)作为一个方法的参数,排序比较大小的接口Comparator

Lambda表达式

  1. 基本写法:(params) -> result,如(String s) -> s.length(),相当于其他语言的匿名函数或者函数指针,其实是匿名类的一个实例
// LambdaRunnable.java
Runnable doIt = new Runnable(){
      public void run(){
            System.out.println("aaa");
      }
}
new Thread(doIt).start();
// ->
Runnable doIt = ()->System.out.println("aaa");
new Thread(doIt).start();
// ->
new Thread(()->System.out.println("aaa")).start();

实际上是接口或者接口函数的简写,要求这个接口包含且最多只能有一个抽象函数,这样的接口可以用注记@FunctionalInterface来表示,称为函数式接口

高级语法

装箱与拆箱

基本类型的包装类:Boolean Byte Character Integet Long Float Double

正常 Integer I = new Integer(10); 这样很麻烦

装箱boxing后就是 Integet I = 10;

拆箱unboxing就是 int i = I;

实际是

Integer I = Integer.valueOf(10);
int i = I.intValue();

枚举 enum

用法与其他语言的枚举相似

enum Light{Red, Yellow, Green};
Light light = Light.Red;

// 实际上等于 final class Light extends java.lang.Enum<Light>

可以自定义枚举

注解 annotation

所有注解都是java.lang.annotation.Annotation的子类

  1. @Override - 覆盖父类方法

  2. @Deprecated - 过时的方法

  3. @SuppressWarnings - 让编译器不产生警告

其他

  1. 引用就是指针,但是他是受控的,安全的:检查空指引、没有指针运算、不能访问没有引用到的内容、自动回收垃圾

==

  1. 基本数据类型的是比较值,引用数据类型的是比较引用,如果要判断引用的内容是否一样,需要重写equals方法,如果重写equals方法,最好重写hashCode()方法

  2. 浮点数最好不要直接用==

  3. Double.NAN == Double.NAN 结果是false

  4. boolean类型和int类型无法比较

  5. String判断相等要用equals,不要用==,因为String是引用,但是字符串常量一般是可以判断相等的

String hello = "Hello", lo = "lo";
System.out.println(hello == "Hello"); // true 
System.out.println(hello == Other.hello); // true 
System.out.println(hello == new String("Hello")); // false
System.out.println(hello == "Hel"+"lo"); // true 
System.out.println(hello == "Hel"+lo); // false 
System.out.println(hello == ("Hel"+lo).intern()); // true 
posted @ 2020-05-14 21:29  陌良  阅读(218)  评论(0)    收藏  举报