1.Java代码一次编写、到处运行
因为Java虚拟机可以实现跨平台的代码运行,不同的操作系统中有不同的JVM。
在程序运行前,JVM会将编写的Java代码编译为字节码格式,这种格式是JVM能够识别的格式。
运行程序时,Java类加载器先加载字节码文件,Java解释器会将字节码翻译为操作系统可以识别的机器码,方便操作系统去执行。为了提高Java程序运行速度,引入了JIT(即时编译器),在第一次编译后,将字节码对应的机器码保存,下次可以直接使用。
2.Java文件里可以有多个类吗(不包含内部类)?
一个Java文件中可以有多个类,但是最多只能有一个被public修饰的类;
如果该文件中包含public修饰的类,则类的名称和Java文件的名称必须相同。
3.Java访问权限
Java语言中共有三种访问修饰符:public、private、protected;
在使用这些修饰符修饰目标时,一共可以形成四种访问权限,public、private、protected、default(不加任何修饰符)。
修饰成员变量/方法时:
- private:该成员可以被该类内部成员访问;
- default:该成员可以被该类内部成员访问,也可以被同一包下的其他类访问;
- protected:该成员可以被该类内部成员访问,也可以被同一包下的其他类访问,还可以被它的子类访问;
- public:该成员可以被任意类访问。
修饰类时:
- default:该类可以被同一包下的其他类访问;
- public: 该类可以被任意包下,任意类访问。
4.Java数据类型
Java数据类型包括两大类:基本数据类型,引用数据类型。
基本数据类型有8个,可以分为4类:整数类型(byte/short/int/long)、浮点类型(float/double)、字符类型(char)、布尔类型(boolean)。除了布尔类型,其他都可以看作为数组类型,可以进行类型转换。
引用类型就是对一个对象的引用,根据引用对象的不同,可以分为数组、类、接口引用类型。实质上就是通过指针,指向堆中对象所持有的内存空间。
基本数据类型所占的内存空间:
- byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1;
- short:2字节(16位),数据范围是 -2^15 ~ 2^15-1;
- int:4字节(32位),数据范围是 -2^31 ~ 2^31-1;
- long:8字节(64位),数据范围是 -2^63 ~ 2^63-1;
- float:4字节(32位),数据范围是 -3.4*10^38 ~ 3.4*10^38;
- double:8字节(64位),数据范围是 -1.8*10^308 ~ 1.8*10^308;
- char:2字节(16位),数据范围是 \u0000 ~ \uffff;
- boolean:不同的JVM有不同的实现机制,没有固定的参数。
6.全局变量(成员变量)和局部变量
Java中没有全局变量,这是c语言中的概念,Java中对应的名称叫做成员变量。
成员变量:
- 成员变量是在类的范围内定义的变量;
- 成员变量有默认初始值;
- 未被static修饰的成员变量也叫实例变量,存储于对象所在的堆内存当中,生命周期与对象相同;
- 被static修饰的成员变量也叫类变量,存储于方法区当中,生命周期与当前类相同。
局部变量:
- 局部变量是在方法内定义的变量;
- 局部变量没有默认初始值;
- 局部变量存储于栈内存中,作用的范围结束,变量空间会自动的释放。
7.为什么要有包装类?
Java是面向对象的语言,设计理念就是“一切皆是对象”。但是8种基本数据类型却不属于对象,为了解决这个问题,Java为每个基本数据类型都定义了一个对应的引用类型,叫做包装类。
自动装箱与自动拆箱是JDK1.5提供的功能。
自动装箱:可以把一个基本类型的数据直接赋值给对应的包装类型;
自动拆箱:可以把一个包装类型的数据直接赋值给对应额基本类型;
应用场景:例如某个方法的参数是包装类型,调用时我们所使用的数据却是基本类型的数据,这时可以不做任何处理,直接将这个基本类型的值传入给方法即可。
8.对面向对象的理解
面向对象是一种程序设计方法,基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。从现实世界中客观存在的事物出发来构造软件系统,在系统构造中尽可能运用人类的自然思维方法,根据事物的本质特点,把它们抽象地表示为系统中的类,作为系统的基本构成单元,这使得软件系统的组件可以直接映像到客观世界。
面向对象的三大特征是:封装、继承、多态。
-
封装:利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据保护在抽象数据类型的内部,只保留一些对外交流的接口。对Java而言,一个对象封装的是自己的属性和方法,有以下好处:①良好的封装可以减少耦合;②类内部的结构可以自由修改;③可以对成员进行更加精确的控制;④隐藏一些重要信息,保护信息安全。
-
继承:使用已经存在的类作为基础建立新的类,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但是不能选择性的继承父类。并且子类拥有父类非private的属性和方法,子类也可以拥有自己独有的属性和方法,子类可以用自己的方法实现父类。
-
多态:子类对象可以直接赋值给父类变量,但运行时任然表现出子类的行为特征,意味着同一个类型的对象在执行同一个方法时,可能表现出多种行为特征。不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。Java实现多态的三个必要条件:继承、重写、向上转型。设计程序时,可以将参数的类型定义为父类型,调用程序时,根据实际情况,传入该父类型的某个子类型的实例,从而实现多态。
9.封装的目的是什么,为什么要有封装?
封装是面向对象语言对客观世界的模拟,对一个类或对象实现良好的封装,可以实现以下目的:
- 可以隐藏类的实现细节;
- 使用者只能通过接口来访问数据,限制了对成员变量不合理的访问;
- 进行数据检查,有利于保证对象信息的完整性;
- 提高代码的可维护性。
实现良好的封装,要将对象的成员变量和实现细节隐藏起来,不允许外部直接访问;把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。
10.Java是单继承的,为什么不能多继承?
Java单继承指的是一个类只能有一个直接的父类。不能多继承是说一个类不能继承多个父类。
Java借鉴了C++的语法,而C++是支持多继承的,但是因为多继承容易产生混淆。例如两个父类包含同样的方法时,子类在调用该方法或重写的时候就很容易分不清楚。
11.重载和重写的区别
重载是相同的一个方法能够根据输入数据的不同,做出不同的处理
重写就是当子类中继承父类的相同方法,输入数据的类型相同,但是想要做出与父类不同的相应,就要覆盖该方法。
重载:
发生在同一个类中(父类和子类也可以),方法名必须相同,但是参数类型、个数、顺序不同,返回值和访问修饰符也可以不同。
例如构造方法,有参和无参构造就是对构造方法的重载。
重载就是同一个类中,多个同名方法根据不同的传参来执行不同的逻辑处理。
重写:
发生在运行期间,是子类对父类中允许访问方法的实现过程重新进行编写的过程。
重写就是子类对父类方法的重新改造,外部的样子不能变,但内部的逻辑处理可以不同。
12.构造方法不能重写
构造方法不能重写,因为构造方法需要和类保持同名,而重写的要求是子类方法要和父类方法保持同名,如果允许重写的话,那么子类中将会存在与父类不同的构造方法,这与构造方法的要求的矛盾的。
13.Object中常用的方法
- getClass():返回该对象的运行时类;
- boolean equals(Object obj):判断对象是否相等;
- int hashCode():返回该对象的hashCode值,默认情况下根据地址计算;
- String toString():返回该对象的字符串表示;
- finalize():当系统中没有引用变量引用到该对象时,垃圾回收器调用此方法来清理该对象的资源,同一个对象只会调用一次。(不建议主动调用)
14.hashCode()和equals()的关系
hashCode()用于获取哈希码(散列码),equals()用于比较两个对象是否相等。
如果两个对象相等,必须有相同的哈希码;但是如果哈希码相同,未必相等。
Java中实现HashSet首先会调用对象的hashCode方法获取哈希码,通过哈希码确定对象存储的位置,如果该位置已经存了一个对象,则会调用equals进行比较。如果相等,不会保存新加的对象,不过不相等,就会发生哈希碰撞,此时HashSet会采用链式结构在同一位置上存储多个对象。
Object类中的equals方法使用==判断,如果是两个不同的对象,==不能直接判断,所以需要重写equals();
重写equals()必须重写hashCode()方法,如果不重写的话可能会出现equals方法判断两个对象是相等的,但是哈希码的值不相等。
15.String常用的方法
-
char charAt(int index):返回指定索引处的字符
-
String substring(int beginIndex, int endIndex):从此字符串中截取出一部分子字符串;
-
String[] split(String regex):以指定的规则将此字符串分割成数组;
-
String trim():删除字符串前导和后置的空格;
-
int indexOf(String str):返回子串在此字符串首次出现的索引;
-
int lastIndexOf(String str):返回子串在此字符串最后出现的索引;
-
boolean startsWith(String prefix):判断此字符串是否以指定的前缀开头;
-
boolean endsWith(String suffix):判断此字符串是否以指定的后缀结尾;
-
String toUpperCase():将此字符串中所有的字符大写;
-
String toLowerCase():将此字符串中所有的字符小写;
-
String replaceFirst(String regex, String replacement):用指定字符串替换第一个匹配的子串;
-
String replaceAll(String regex, String replacement):用指定字符串替换所有的匹配的子串。
16.String、StringBuffer、StringBuilder的区别
可变性
String是不可变的,因为String使用final关键字修饰字符数组来保存字符串,也不可继承;
StringBuffer和StringBuilder都继承自AbstractStringbuilder类,该类也是使用字符数组保存字符串,但是没有使用final和private关键字修饰,该类还提供了很多修改字符串的方法;
线程安全性
String对象是不可变的,可以理解为常量,是线程安全的;
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的;
StringBuilder并没有对方法加同步锁,所以是非线程安全的;
性能
每次对String类型进行改变的时候,都会生成一个新的String对象,然后指针指向该对象;
StringBuffer每次会对对象本身进行操作,而不是生成新的对象并改变对象引用;
相同情况下使用StringBuilder相比于StringBuffer获得的性能提升很有限,却要冒多线程不安全的风险;
使用情况
操作少量的数据使用String;
使用String时,new方法会多创建一个对象出来,占用更多的内存,所以一般使用直接量方式创建字符串(String str = "abc")。
String str = "abc"; :JVM会使用常量池来管理字符串直接量,执行这种方式时,JVM首先会检查常量池中是否已经存在要创建的字符,若没有就放入,若有就直接用常量池中的值,将其赋值给变量。
String str = new String("abc"); :JVM使用常量池管理字符串直接量后,再创建一个String的对象,该对象保存在堆内存中,堆中的数据会指向常量池中的直接量。
字符串拼接:
如果拼接的都是字符串直接量,适合使用+运算符实现拼接;
如果对两个字符串进行拼接,并且包含变量,适合使用concat方法;
单线程操作字符串缓冲区下操作大量数据使用StringBuilder,不要求线程安全;
多线程操作字符串缓冲区下操作大量数据使用StringBuffer,要求线程安全;
17.接口和抽象类有什么区别
接口体现的是一种规范
对接口的实现者来说,接口规定了实现者必须向外提供某种服务;
对接口的调用者来说,接口规定了调用者可以调用哪些服务,以及如何调用这些服务;
在一个程序中使用接口时,接口是多个模块间的耦合标准;
在多个程序中使用接口时,接口是多个程序之间的通信标准;
抽象类体现的是一种模板式设计
抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能,但是不能当成最终产品,必须有更进一步的完善,这种完善可能有几种不同的方式。
从使用方式上来说,二者有如下的区别:
-
接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法;
-
接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量;
-
接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作;
-
接口里不能包含初始化块;但抽象类则完全可以包含初始化块;
-
一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。
接口和抽象类很像,它们都具有如下共同的特征:
-
接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承;
-
接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
18.Java怎么处理异常
处理异常需要三个步骤:
1.捕获异常(try)
将业务代码包裹在try块内部,当出现异常时,系统会为此异常创建一个异常对象。
2.处理异常(catch)
创建异常对象之后,JVM会在try块后寻找catch块,将异常对象交给catch处理。在catch块中处理异常时,要记录日志,便于后续追溯这个异常,根据异常的种类结合当前的业务情况处理异常。(返回空值、向外抛出异常)
抛出异常:当程序出现错误时,系统会自动抛出异常。Java允许程序主动抛出异常,当业务代码中判断某项错误的条件成立时,可以使用throw关键字向外抛出异常。如果当前方法无法处理这个异常,可以在方法签名上通过throws关键字声明抛出异常,交给JVM处理。
异常跟踪栈:程序运行过程中,在调用方法时会形成方法调用栈。异常机制会导致异常在方法中传递,异常传递的顺序与方法调用相反。从异常发生的方法向外传递,首先传给该方法的调用者,再给上层调用者传递,最后传递到main方法,最后如果还是没有处理异常,JVM会终止程序,打印异常跟踪栈的信息。
Throwable:异常的顶层父类,代表所有的非正常情况,它有两个直接子类:Error、Exception。
Error:错误。一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误等,这种错误无法恢复或不能捕获,会导致应用程序中断。
Exception:异常,可以分为两大类,分别是Checked异常和Runtime异常。RuntimeException类及子类的实例称为Runtime异常,其他的被称为Checked异常。
Java认为Checked异常都是可以被处理的,所以必须显式处理Checked异常,如果不处理则无法通过编译。
Runtime异常则更加灵活,无需显式声明抛出,如果要捕获Runtime异常也可以使用try...catch实现。
3.回收资源(finally)
无论是否发生异常,都要尝试回收业务代码执行完之后的系统资源,将关闭的资源写在finally中,finally内的代码总会被执行。
一般不建议在finally块中使用return、throw等导致方法终止的语句,一旦使用,会导致try...catch块中的return、throw语句失效。
如果有finally块,try..catch块中的代码执行结束后,还会去执行finally中的语句,最后再执行try...catch中的终止语句。如果finally中有终止语句,会直接终止,不会等到再去查看try...catch块中的代码。
19.static关键字
Java中包含5种成员:成员变量、方法、构造器、初始化块、内部类(接口、枚举),static可以修饰成员变量、方法、初始化块、内部类,以static修饰的成员就是类成员。类成员属于整个类,而不属于单个对象。
类成员不能访问实例成员,因为类成员是属于类的,类成员的作用域比实例成员的作用域更大,可能会出现类成员已经初始化完成,而实例成员还没初始化的情况,如果允许访问实例成员会引起大量错误。
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,有的地方也称为静态内部类。
static关键字的作用是把类的成员变成类相关,而不是实例相关,static修饰的成员属于整个类,而不属于单个对象,static不可以修饰外部类,可以修饰内部类。
静态内部类需要满足以下规则:
1.静态内部类可以包含静态成员,也可以包含非静态成员;
2.静态内部类不能访问外部类的实例成员,只能访问它的静态成员;
3.外部类的所有方法、初始化块都能访问其内部定义的静态内部类;
4.在外部类的外部,也可以实例化静态内部类,语法如下:
外部类.内部类 变量名 = new 外部类.内部类构造方法();
static和final有什么区别
static修饰成员变量、成员方法、初始化块、内部类时表现的特征:
- 类变量:被static修饰的成员变量叫做类变量(静态变量)。类变量属于类,它随类的信息存储在方法区,并不随对象存储在堆中,类变量可以通过类名来访问(常用);
- 类方法:被static修饰的成员方法叫类方法(静态方法)。类方法属于类,可以通过类名访问(常用),也可以通过对象名访问;
- 静态块:被static修饰的初始化块叫静态初始化块。静态块属于类,在类加载的时候被隐式的调用一次,之后便不会被调用;
- 静态内部类:被static修饰的内部类叫静态内部类。静态内部类可以包含静态成员,也可以包含非静态成员。静态成员不能访问外部类的实例成员,只能访问外部类的静态成员。外部类的所有方法、初始化块都能访问其内部定义的静态内部类。
final关键字可以修饰类、方法、变量时表现的特征:
- final类:类不可以被继承;
- final方法:方法不可以被重写;
- final变量:被final修饰的变量一旦获得初始值,就不可以被修改。
20.泛型
Java集合有一个缺点是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,对象的编译类型就变成了Object类型(其运行时类型没变)。
这样设计的目的是集合能够保存任何类型的对象,具有很好的通用性,但是也引入了两个问题:
- 集合对元素类型没有任何的限制,会引发一些问题,可能创建的集合只想保存某一个对象,但是另一个完全不同的对象也会被保存在该集合中,会引发异常;
- 把对象“丢进”集合时,集合失去了对象的状态信息,只知道保存的时Object类型,因此取出元素时还需要强制类型转换,增加了编程的复杂度,也可能引发ClassCastException异常。
Java5引入了“参数化类型”的概念,允许程序在创建集合时指定集合元素的类型,Java的参数化类型被称为泛型,集合会自动记住所有元素的数据类型,无需对元素再进行强制类型转换。
泛型擦除:把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有尖括号之间的类型信息都将被扔掉,该规则就称为泛型擦除。
List<? super T>和List<? extends T>有什么区别
- ?是类型通配符,List<?>可以表示各种泛型List的父类,意思是元素类型未知的List;
- List<? super T>用于设定类型通配符的下限,此处?代表一个未知的类型,但必须是T的父类型;
- List<? extends T>用于设定类型通配符的上限,此处?代表一个未知的类型,但必须是T的子类型;
21.反射
Java程序中的对象在运行时可以表现为两种类型:编译时类型和运行时类型。
Person p = new Student();
p是变量,该变量的编译时类型为Person,运行时类型为Student。
程序在运行时接收到外部传入的一个对象,该对象的编译时类型是Object,但程序又需要调用该对象的运行时类型的方法,这就要求程序要在运行时发现对象和类的真实信息。有两种方法:
- 第一种做法是假设在编译时和运行时都完全知道该类型的具体信息,在这种情况下,可以先使用instanceof运算符进行判断,再利用强制类型转换将其转换成运行时类型的变量;
- 第二种做法是编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,这是就要用到反射。
Java反射机制是在运行状态时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。具体的说就是,把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。
拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值;
通过反射机制可以实现如下操作:
- 程序运行时,可以通过反射获得任意一个类的Class对象,通过这个对象可以查看这个类的信息;
- 程序运行时,可以通过反射创建任意一个类的实例,并且访问该实例的成员;
- 程序运行时,可以通过反射生成一个类的动态代理或动态代理对象。
反射的应用场景:
- 使用JDBC时,如果要创建数据库的连接,需要通过反射机制加载数据库的驱动程序;
- 多数框架都支持注解和XML配置,从配置中解析出来的类是字符串,需要使用反射机制实例化;
- 面向切面编程(AOP)的实现方案,是在程序运行时创建目标对象的代理类,必须由反射机制来实现。
22.Java中的四种引用方式
四种引用分别是:强引用、软引用、弱引用、虚引用。
- 强引用:程序创建一个对象,把这个对象赋值给一个引用变量,程序通过该引用变量来操作实际对象。当一个对象被强引用变量所引用时,不能被系统垃圾回收机制回收;
- 软引用:软引用用来描述一些有用但是非必须的引用对象,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象,但是当系统空间不足时,系统可能会回收该对象;
- 弱引用:弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统执行垃圾回收机制时,不管内存空间是否充足,都会回收该对象的内存空间;
- 虚引用:虚引用类似于没有引用,虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。虚引用主要应用于跟踪对象垃圾回收的状态,虚引用不能单独使用,必须与引用队列联合使用。
Java语言的特点:
1.简单易学
2.面向对象(封装、继承、多态)
封装:利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据保护在抽象数据类型的内部,只保留一些对外交流的接口。对Java而言,一个对象封装的是自己的属性和方法,有以下好处:①良好的封装可以减少耦合;②类内部的结构可以自由修改;③可以对成员进行更加精确的控制;④隐藏一些重要信息,保护信息安全。
继承:使用已经存在的类的定义作为基础建立新的类,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但是不能选择性的继承父类。并且子类拥有父类非private的属性和方法,子类也可以拥有自己独有的属性和方法,子类可以用自己的方法实现父类。
多态:一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是在哪个类中实现的方法,必须在由程序运行期间才能决定。不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。Java实现多态的三个必要条件:继承、重写、向上转型。
3.平台无关性(Java虚拟机提供)
3.支持多线程(C++没有内置的多线程操作,必须调用操作系统的多线程功能,而Java内部有内置的多线程操作)
4.安全可靠
5.支持网络编程
6.编译与解释并存
编译型语言:通过编译器将全部代码一次性翻译成操作系统可以识别的机器码,编译型语言的执行速度较快,但是开发效率很慢。
解释型语言:通过解释器一句一句将代码解释为操作系统可以识别的机器码,解释性语言的执行速度比较慢,但是开发效率较快。
Java代码执行步骤
第一步:.java文件(Java源程序)被编译为.class文件(字节码文件)是一次性全部进行编译,所以Java是编译型语言。
第二部:.class文件(字节码文件)被解释为机器可执行的二进制机器码,属于解释型语言。这一步中JVM类加载器先加载字节码,然后通过解释器逐行解释执行,这种方法执行速度相对较慢。所以后续引进了JIT编译器(即时编译器),当JIT编译器完成第一次编译(一些常用的方法和代码块)之后,会将字节码对应的机器码保存下来,下次可以直接使用。
JDK - JVM - JRE
JVM
Java虚拟机(JVM)是运行Java字节码的虚拟机。JVM有针对不同系统的特定实现,其目的就是使用相同的字节码,这样会给出相同的结果。字节码和不同系统的JVM是实现Java语言跨平台的关键。
JVM并不是只有一种!只要满足JVM规范,每个公司、组织,甚至个人都可以开发自己的JVM,我们平时接触到的HotSpot VM仅仅是JVM规范中的一种实现。
JDK
JDK是功能齐全的SDK,拥有JRE的全部内容,还有编译器(javac)和工具(如javadoc和jdb),能够创建和编译Java程序。
JRE
JRE是Java运行时环境,它是运行和编译Java程序所需的所有内容的集合,包括Java虚拟机,Java类库,Java命令和其他的一些基础构建,但是不能用于创建新程序。
Java与C++的区别
Java不提供指针来直接访问内存,程序内存更加的安全;
Java的类是单继承的,也支持接口继承,C++支持多重继承(一个类可以同时继承多个类);
Java有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存;
Java只支持方法重载,C++同时支持方法重载和操作符重载(重载“+”等符号);
Java基本语法
字符串常量和字符型常量的区别
1.形式:字符常量通常是单引号引起的单个字符,字符串常量是双引号引起的0个或多个字符;
2.含义:字符常量相当于一个整型值(ASCII),可以参加运算,字符串常量代表一个地址值(该字符串在内存中存放的位置)。
3.占内存的大小:字符串常量占若干个字节,字符常量只占2个字节(char);
自增自减运算符
b=++a,先自增(a先+1),再赋值(再赋值给b);
b=a++,先赋值(赋值给b),再自增(a+1);
也就是说++a 输出的是 a+1 的值,a++输出的是 a 值,符号在前就先加/减,符号在后就后加/减
continue、break和return的区别
continue:跳出当前这一次循环,继续下一次循环;
break:跳出整个循环体,继续执行循环下面的语句;
return;:直接使用return结束方法的执行,用于无返回值的方法;
return value:返回一个特定的值,用于有返回值的方法;
静态方法不能调用非静态成员
静态方法是属于类的,在类加载过程中会分配内存,可以通过类名直接访问。而非静态成员是属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问;
在类的非静态成员还不存在的时候,静态成员就已经存在了,此时调用在内存中还不存在的非静态成员属于非法操作;
静态方法和实例方法的不同
1.调用方法不同
调用静态方法时,可以使用类名.方法名方式,也可以使用对象.方法名方式;
调用实例方法,只能使用对象.方法名方式;
也就是说调用静态方法可以不用创建新的对象,但还是建议使用对象.方法名方式进行调用,类名.方法名方式容易造成混淆,容易被误认为是该类型的一个类,而不是方法。
2.访问类成员是否存在限制
静态方法访问本类的成员时,只允许访问静态成员(静态成员变量和静态方法),不允许访问实例对象;
实例对象访问本类的成员时,不存在上述的这些限制。
重载和重写的区别
重载是相同的一个方法能够根据输入数据的不同,做出不同的处理
重写就是当子类中继承父类的相同方法,输入数据的类型相同,但是想要做出与父类不同的相应,就要覆盖该方法。
重载:
发生在同一个类中(父类和子类也可以),方法名必须相同,但是参数类型、个数、顺序不同,返回值和访问修饰符也可以不同。
例如构造方法,有参和无参构造就是对构造方法的重载。
重载就是同一个类中,多个同名方法根据不同的传参来执行不同的逻辑处理。
重写:
发生在运行期间,是子类对父类中允许访问方法的实现过程重新进行编写的过程。
重写就是子类对父类方法的重新改造,外部的样子不能变,但内部的逻辑处理可以不同。
==与equals()的区别
==对基本数据类型来说,比较的是值的大小是否相同。
==对引用数据类型来说,比较的是地址是否相同。
因为Java只有值传递,所以==实质上比较的还是值,只是引用数据类型的变量存储的值是对象地址。
equals()不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,因此所有的类都包括equals()方法。
equals()方法存在两种使用情况:
①类没有重写equals()方法:通过equals()比较该类的两个对象,等价于通过“==”比较这两个对象,使用的Object类中默认的equals()方法;
②类重写了equals()方法:一般重写equals()方法用来比较两个对象中的属性是否相等,若属性相等则返回true,认为这两个对象相等。
String中的equals()方法是重写过的,Object中是比较对象的内存地址,String中比较的是对象的值。
当创建String类型的对象是,虚拟机首先在常量池中查找是否有已经存在的值和要创建的值相同的对象,如果有就把它赋值给当前的引用,如果没有就在常量池中重新创建一个String对象。