Java 易混淆知识汇总
1. Java与C++的关键不同
a)C++具有指针的概念,可以直接操作内存,Java不能直接操作内存,程序内存更为安全;
b)C++中类可以多继承,Java只能单继承,但是Java的接口可以多继承
c)Java具有自动内存管理机制,不需要手动管理内存
d)Java需要编译后执行,运行速度会比较慢,目前提出的即时编译器的编译水平低于传统的编译方法
e)Java采用Unicode字符集,C++通常用ASCII字符集,由于ASCII是Unicode的子集,整体影响不大
f)C++支持“运算符的重载”,允许直接对对象进行四则运算,Java不支持这种多态机制,降低了复杂性
2. Java主类
一个java文件中只能有一个public修饰的类,每个类中只能有一个main方法,但是一个java文件中可以有多个类,也就是可以有多个main方法。JVM在加载类时会加载和.class文件名相同的那个类。Java文件的名称要和public修饰的类名一样。编译时会生成多个class文件,可以选择让JVM首先加载某个类,加载完成之后,会先找静态代码块,然后找该类的main方法。一般都说main方法是程序的入口,如果main方法里用到了别的类,JVM会自动去加载给类,并且只加载一次。main方法执行完了,程序基本也就结束了。
在使用编译器=加载程序时,编译器会让你选择其中一个main方法成为程序的入口,其他main函数成为普通函数,可以进行调用
3. 细碎的知识点
a)字符常量(char)与字符串常量(String)
字符常量(char)通常是单引号引起的一个字符,相当于一个整型值( ASCII 值),可以参加表达式运算,字符常量只占 2 个字节。
字符串常量(String)是双引号引起的若干个字符,代表一个地址值(该字符串在内存中存放位置),字符串常量占据多个字节。
b)重载(override)与重写(overwrite)
重载(override)发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
重写(overwrite)是子类对父类的允许访问的方法的实现过程进行重新编写,发生在子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。另外,如果父类方法访问修饰符为 private 则子类就不能重写该方法。
c)封装 继承 多态
封装:把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
继承:使用已存在的类的定义作为基础建立新的类,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承能够非常方便地复用以前的代码。
子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。*
子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
多态:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
- 实现多态的两种方法:继承父类(多个子类对同一方法的重写)和实现接口(实现接口并覆盖接口中同一方法)。*
d)String为什么不可变
String 类:使用final
关键字修饰字符数组来保存字符串,private final char value[]
,所以String
对象是不可变的。
StringBuffer:继承自 AbstractStringBuilder
类,使用字符数组保存字符串char[]value
但是没有用final
关键字修饰,因此可变。具有同步锁,线程安全。
StringBuilder:继承自 AbstractStringBuilder
类,使用字符数组保存字符串char[]value
但是没有用final
关键字修饰,因此可变。不具有同步锁,线程不安全。
AbstractStringBuilder
是StringBuilder
与StringBuffer
的公共父类,定义了一些字符串的基本操作,如expandCapacity()
、append()
、insert()
、indexOf ()
等公共方法。每次对
String
类型进行改变的时候,都会生成一个新的String
对象,然后将指针指向新的String
对象。相同情况下使用
StringBuilder
相比使用StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
e)无参构造方法的作用
Java 程序在执行子类的构造方法之前,如果没有用 super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
f)接口和抽象类的区别
- 接口的方法默认是
public
,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。 - 接口中除了
static
、final
变量,不能有其他变量,而抽象类中则不一定。
一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。 - 接口方法默认修饰符是
public
,抽象方法可以有public
、protected
和default
这些修饰符(抽象方法为了被重写不能使用private
关键字修饰)。 - 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
g)成员变量和局部变量
从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被
public
,private
,static
等修饰符所修饰,而局部变量不能被访问控制修饰符及static
所修饰;但是,成员变量和局部变量都能被final
所修饰。从变量在内存中的存储方式来看:如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。从变量在内存中的生存时间上看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外:被
final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。
h) 静态方法和实例方法有何不同
在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。