Java知识点总结 (二)

  1. final关键字的用法?

    保证不可变性,再次赋值会报编译错误,如String和数组的区别,String是不可变的,数组可变,这类设计取决于我们对他们的期望。

    这点也是StringBuilder和Vector等存在的目的之一。

    但final有个缺点,它限制的是赋值语句,所以final只能限制于原始数据类型,当修饰引用类型的变量时,它将永远指向同一个对象,但对象的值仍是可变的。如final一个int[],仍可以修改元素

    final修饰类,类无法被继承,成员方法都会隐式的指定为final方法

    final 修饰方法,使用这种方法的原因:

    1. 锁定方法,防止被修改,private会隐式的指定为final方法,所以一般用来锁定被子类继承的方法。
    2. 提高效率,早期会将final方法转为内嵌调用,新版本不需要如此优化

    final修饰变量时,封装类型和String加final修饰时会被当作编译常量

    final和static的区别:对于成员变量,static表示只保留一份副本,final表示变量不可变,final对于不同的对象可能就不同,static则是一定相同的

  2. Java中的异常、错误和断言?

    断言是一条需要在程序的某处确认为true的布尔表达式,当值为false程序会中止并报告出错信息,只用来测试。
    Exception Error
    java异常分为两种:运行时异常(RuntimeException)和非运行时异常(CheckedException)也叫检查式异常

    1. 运行时异常是不需要捕获的,程序员可以不去处理,当异常出现时,虚拟机会处理。常见的运行时异常有空指针异常

    常见的5中运行时异常:

    • ClassCastException(类转换异常)
    • IndexOutOfBoundsException(数组越界)
    • NullPointerException(空指针)
    • ArrayStoreException(数据存储异常,操作数组时类型不一致)
    • 还有IO操作的BufferOverflowException异常
    1. 非运行时异常就必须得捕获了,否则编译不过去,java编译器要求程序员必须对这种异常进行catch,Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。

    常见的非运行异常有io异常和sql异常:IOException、FileNotFoundExcetion 和SQLException

    finally是异常处理工作的一部分,表示总是执行。一般finally写的代码语句就是流的关闭。也就是做了一项清理,工作清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。

  3. 异常处理中的throws和throw的区别?

    1. throws出现在方法的声明中,表示该方法可能会抛出的异常,允许throws后面跟着多个异常类型
    2. throw出现在方法体中,用于抛出异常。当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw。
  4. Java中定义的各种变量?

    • 成员变量:作用范围是整个类,相当于C语言中的全局变量,定义在方法体和语句块之外,一般定义在类的声明之下;成员变量包括:实例变量、类变量(又叫静态变量)和常量。
    • 局部变量:作用范围在它定义的方法体或者语句块内部,出了这个范围就无效了。
    • 实例变量:不用static修饰的成员变量,随对象的创建而创建,每个对象都有自己的独有的实例变量,属于对象私有;调用要实例化对象,用对象名.实例变量名才可以调用,如:Demo demo = new Demo(); int YouAge = demo.age;(当然,一般都会将成员变量设为private,通过属性方法调用)。
    • 静态变量:有时叫全局变量,和实例变量区别就是不和对象关联。用static修饰的成员变量,又叫类变量,一个类里只有一份,属于对象共有,调用是一般用类名.静态变量名就可以调用,或者用对象名.静态变量名也可以调用,调用的都是同一个变量,如:Demo.height。
  5. 泛型和迭代?

    • 泛型<>,参数化类型,象征性的占位符,没有泛型,我们需要为所有数据类型定义不同的API。

    • 迭代,foreach语句通过迭代遍历或处理集合中的每个元素,需要集合数据类型需要实现iterable接口,添加iterator方法并返回一个Iterator迭代器对象,迭代器对象包含hasNext和next方法

  6. 自动装箱和自动拆箱?

    类型化参数必须实例化为引用类型,所以Java对于原始数据类型和封装类型进行了自动转换。

1
2
3
List<Integer> list = new ArrayList<>();
list.add(11); //自动装箱 int->Integer
int i = list.get(0); //自动拆箱 Integer->int
  1. Java中的各种内部类?

    • 成员内部类,定义于另一个类的内部,可以任意访问外部类的属性和方法,如果同名默认内部类成员,外部类访问内部类需要先实例对象,内部类访问权限和属性一样有四种
    • 局部内部类,定义于方法或作用域内部,访问权限只限于该方法或作用域,类似局部变量不能有访问修饰以及静态修饰
    • 匿名内部类,new ClassName(){@override……},实现父类或接口的同时产生一个实例对象,当然要先存在该父类或接口,同样不能用访问修饰以及静态修饰。唯一没有构造器的内部类,所以一般只用来做接口回调,不需要增加方法,只重写或实现继承的方法
    • 静态内部类,定义于另一个类内部但多了static修饰,所以不依赖外部类,也不能使用外部类非static成员。
  2. 成员内部类如何无条件引用外部类成员?与静态内部类区别?

    成员内部类会单独编译为一个字节码文件,编译器会为成员内部类添加指向外部类对象的引用,即使内部类是无参构造器,编译仍默认加外部类对象的引用,所以也知道成员内部类会依赖于外部类对象。

    静态内部类区别于成员内部类,不依赖外部类对象,可以直接创建内部类对象,且不包含外部类引用的。

  3. 为什么局部内部类和匿名内部类只能访问局部final变量?

    如一个方法调用线程TheadA,方法内有局部变量a,那么方法执行完毕,a生命周期就结束了,但此时线程A还可能在执行,此时无法访问a怎么办?Java编译器会默认在局部或匿名内部类的常量池增加一个相同的值嵌入执行字节码,如此匿名内部类只是使用的副本,而非方法中的变量a,当然a有可能是不确定值的参数,编译时无法确定值,所以会通过构造器传参的方式来对副本初始化赋值。因此可以明白,如果局部变量改变的话会导致数据不一致,所以Java直接限制只能访问final局部变量。

  4. 内部类使用场景和优点?

    1. 内部类独立继承接口,而不用管外部类如何,所以是Java解决多继承的方案
    2. 方便将关系紧密的类结合,并对外界隐藏
    3. 方便编写事件驱动
    4. 方便编写线程代码
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
public class Test{
public static void main(String[] args){
// 初始化Bean1
(1)
bean1.I++;
// 初始化Bean2
(2)
bean2.J++;
//初始化Bean3
(3)
bean3.k++;
}
class Bean1{
public int I = 0;
}

static class Bean2{
public int J = 0;
}
}

class Bean{
class Bean3{
public int k = 0;
}
}
  1. Test test = new Test();
    Test.Bean1 bean1 = test. new Bean1();

  2. Test.Bean2 bean2 = new Test.Bean2();

  3. Bean bean = new Bean();
    Bean.Bean3 = bean.new Bean3();

  1. 字符串和子字符串?

    计算内存消耗时可以知道:原始数据类型占用字节:char-2字节,int-4字节,double/long-8字节,而对象需要16字节的对象开销,引用即机器地址一般为8字节。所以一个String字符串内存开销:对象开销16+char数组引用8+偏移量int4+字符串长度int4+散列值int4+填充字节+4 + char数组24+2N = 40+24+2N=64+2N

    创建子字符串时,如substring(3,6) 会创建一个新String对象,但其和原字符串共用一个char数组,所以其内存开销为常数:40

  2. Java到底是值传递还是引用传递?

答:值传递,如果参数是基本类型,传递的是基本类型的字面量值的拷贝。如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。

参数在程序语言中分为形式参数实际参数

  • 形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
  • 实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

按值传递:将实际参数的副本传递给方法。方法无法改变调用端变量的值,原始数据类型正期望如此。

引用类型传递:将实际参数直接传递给方法。方法可以直接改变调用端变量的值。

不管参数类型是基本数据类型还是对象,都是值传递,只不过对象参数传递的值是引用。


参考博客和文章书籍等:

Java到底是值传递还是引用传递?

因博客主等未标明不可引用,若部分内容涉及侵权请及时告知,我会尽快修改和删除相关内容