JAVA学习笔记

记录学习Java过程中的一些小知识点

 

空指针访问静态变量时不会引发空指针异常

静态方法必须使用“类名.方法名”,而静态变量既可以使用"类名.变量名" 也可以使用"对象名.变量名"。实际上我们在使用对象名.变量名来操作静态变量时,实际上,JVM会将其解释为类名.变量名,这意味着下面这种情况不会发生空指针异常。

public class Demo {
    public int id;
    public String name;
    public static String country="中国";

    public static void main(String[] args) {
        Demo test = new Demo();
        test = null;
        System.out.println(test.country);
    }
}

输出结果为

结论:只有在”空引用“ 访问”实例“相关的,才会发生空指针异常

 

实例代码块、静态代码块、构造函数的执行顺序

当new一个对象时候。

静态代码块先执行,之后是实例代码块,最后是构造函数

静态代码块可以用来统计对象的数量、初始化静态成员变量等。

实例代码块在开发中作用不大

 

 

this的内存结构

一个this一个对象

this是一个引用,保存当前对象的内存地址,指向自身。

this存储在堆内存当中对象的内部

 

this只能使用在实例方法中

this不能省略的情况

 在setter,形参的名字最好与成员变量的相同,但是这样的话在赋值的时候,成员变量必须加this.修饰,因为不加this.修饰的话,setter中的变量都将是形参(按照就近原则) 赋值操作就变成了形参给形参自己赋值

例如

public class Demo{
       private int a;
       public void setA(int a){
        a=a;  //这里就变成形参a=形参a了
}

}

this还可用在构造方法中

通过当前的构造方法去调用另一个本类的构造方法,可以使用以下的语法格式:this(实际参数列表);

注意事项:对this的调用必须是构造器中的第一个语句(java就是这么规定的)

 

super

  • super能出现在实例方法和构造方法中
  • super的语法是:”super.“和”super()“
  • super.可以访问父中的非私有 属性和方法
  • super不能使用在静态方法中
  • super.大部分情况下是可以省略的,当父类和子类中有同名属性或者方法,若要在子类中访问父类的属性或者和方法则需要加super.限定
  • super()只能出现在构造方法的第一行,通过当前构造方法调用父类的构造方法,目的是创建子类时,先初始化父类型特征。
  • super()和this()不能共存

在子类的构造函数中没有this和super,java会默认添加一个super()表示通过当前子类的构造方法调用父类的无参构造函数,所以必须保证父类的无参构造函数时存在的

super不是引用,也不保存内存地址,也不指向任何对象,只是代表当前对象内部的那一块父类型的特征。

因此输出super不能输出形如”类名@地址的字符串“

public void doSome(){
        System.out.println(this);
        System.out.println(super); //报错
}

为什么this、super不能出现在静态方法中?

因为静态方法是通过类名引用的,调用非静态方法时,this指向当前对象,super是当前对象的父类特征部分。而调用非静态方法是通过类名调用的,就不存在当前对象了this无引用的对象了,因此java不允许这样做。

 

 

Object类中的toString方法

该方法时返回该对象的字符串表示。通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。

Object 类的 toString 方法返回一个字符串,它的值等于:

getClass().getName() + '@' + Integer.toHexString(hashCode())

hashCode()是Object类的一个方法,返回该对象的哈希值。

哈希值是通过哈希算法计算出来的一个地址值(并非实际地址值)。

当我们直接用System.out.println()直接输出一个对象名的时候,会自动的调用该对象的toString方法,输出一个“类名@地址值”的字符串

验证

public class Demo {
    public int id;
    public String name;
    public static String country="中国";
    {
        System.out.println();
    }
    public static void main(String[] args) {
        Demo test = new Demo();
        System.out.println("test = "+test);
        System.out.println("test.toString() = "+test.toString());

    }
}

输出结果为

 我们可以手动重写这个方法,来实现直接用println输出对象的内容

 

匿名内部类

匿名内部类可以使你的代码更加简洁,你可以在定义一个类的同时对其进行实例化。它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类。

 

下面是官方文档里匿名内部类的用法

public class HelloWorldAnonymousClasses {

    /**
     * 包含两个方法的HelloWorld接口
     */
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }

    public void sayHello() {

        // 1、局部类EnglishGreeting实现了HelloWorld接口
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }

        HelloWorld englishGreeting = new EnglishGreeting();

        // 2、匿名类实现HelloWorld接口
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

        // 3、匿名类实现HelloWorld接口
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };

        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
}

静态数组作为方法参数

必须这么写: 方法名(new int[]{1,2,3,4});

 

数组拷贝

只能再建一个更大的数组,将满了的数组里的元素拷贝进去。

可以使用System.arraycopy()方法

System.arraycopy

public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
作用:将指定源数组中的数组从指定位置复制到目标数组的指定位置。
参数:

src - 源数组。

srcPos - 源数组中的起始位置。

dest - 目标数组。

destPos - 目的地数据中的起始位置。

length - 要复制的源数组元素的数量。

 

举例:

原数组:int[] arr={1,2,3,4,5,6,7,8,9,0};
目标数组:int[] Arr=new int[4];
操作:将原数组第二个位置以后的4个数据copy到目标数组
    System.arrayCopy(arr,1,Arr,0,4);

 

除此之外,Array.copyOf也可以用来拷贝数组

Arrays.copyOf()不仅仅只是拷贝数组中的元素,在拷贝元素时,会创建一个新的数组对象。而System.arrayCopy只拷贝已经存在数组元素。

其实Array.copyOf底层使用System.arrayCopy实现的,下面是源代码

public static int[] copyOf(int[] original, int newLength) {
        int[] copy = new int[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

Arrays类

 

Java的Arrays类中有一个sort()方法,该方法是Arrays类的静态方法,在需要对数组进行排序时,非常的好用。

但是sort()的参数有好几种,下面我就为大家一一介绍,这几种形式的用法。

1、Arrays.sort(int[] a)

这种形式是对一个数组的所有元素进行排序,并且是按从小到大的顺序。

2、Arrays.sort(int[] a, int fromIndex, int toIndex)

这种形式是对数组部分排序,也就是对数组a的下标从fromIndex到toIndex-1的元素排序,注意:下标为toIndex的元素不参与排序哦!

3、public static void sort(T[] a,int fromIndex,int toIndex, Comparator c)

上面有一个拘束,就是排列顺序只能是从小到大,如果我们要从大到小,就要使用这种方式

 1 package test;
 2 
 3 import java.util.Arrays;
 4 import java.util.Comparator;
 5 
 6 public class Main {
 7     public static void main(String[] args) {
 8         //注意,要想改变默认的排列顺序,不能使用基本类型(int,double, char)
 9         //而要使用它们对应的类
10         Integer[] a = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
11         //定义一个自定义类MyComparator的对象
12         Comparator cmp = new MyComparator();
13         Arrays.sort(a, cmp);
14         for(int i = 0; i < a.length; i ++) {
15             System.out.print(a[i] + " ");
16         }
17     }
18 }
19 //Comparator是一个接口,所以这里我们自己定义的类MyComparator要implents该接口
20 //而不是extends Comparator
21 class MyComparator implements Comparator<Integer>{
22     @Override
23     public int compare(Integer o1, Integer o2) {
24         //如果n1小于n2,我们就返回正值,如果n1大于n2我们就返回负值,
25         //这样颠倒一下,就可以实现反向排序了
26         if(o1 < o2) { 
27             return 1;
28         }else if(o1 > o2) {
29             return -1;
30         }else {
31             return 0;
32         }
33     }
34     
35 }

 

二分查找

Arrays.binarySearch(int arr[],int key)

 

 

finally子句

在异常处理机制中,finally子句总会执行。

即使前面有return也会执行

但是如果退出虚拟机的话,finally就不执行了

面试题,求输出的result为几?

public class test{
    public static void main(String[] args) {
        int result = m();
        System.out.println(result);
        
        public static int m(){
            int i =100;
            try {
                return i;
            }finally {
                i++;
            }
        }
    }

}

答案是100。为什么是100而不是101呢?不是说先执行try,在执行finally,最后在执行return吗?实际上确实是这样的,之所以输出100是因为java中有一些规则是不能破坏的,一旦这么说了就必须这么做!

java中有这么一条规则:方法体中的代码必须遵循自上而下顺序依次逐行执行。

就是说,即使实际上是按照try、finally、return顺序执行的,但是程序返回的的结果是按自上而下的顺序执行出的结果。

下面看一下反编译后的代码

 

final finally finalize有什么区别

final是一个关键字,表示最终的、不可变的

finally也是一个关键字,和try联合使用,表示一定会执行

finalize是一个方法,Object类中的方法,一个对象被垃圾回收时会执行它的finalize方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值