面向对象是相对于面向过程来讲的,指的是把相关的数据和方法组织作为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。
面向过程到面向对象思想层面的转变:
面向过程关注的是执行的过程,面向对象关注的是具备功能的对象。
从面向过程到面向对象,是程序员思想上从执行者到指挥者的转变。
面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP。
OOA:面向对象分析(Object Oriented Analysis)
OOD:面向对象设计(Object Oriented Design)
OOP:面向对象程序(Object Oriented Programming)
三大特征
封装性:所有的内容对外部不可见
继承性:将其他的功能继承下来继续发展
多态性:方法的重载本身就是一种多态性的体现
(面试)
封装:
1、私有化成员变量,使用private关键字修饰
2、提供公有的get/set方法,并在方法体中进行合理的判断
3、在构造方法中调用set方法进行合理的判断
封装的意义在于保护或者防止代码(数据)被我们无意中破坏,保护成员属性不让类以外的程序直接访问和修改。
创建对象的格式:类名 对象名 = new 类名();
对象的属性赋值:对象名.属性名 = 值;
栈存放的是基本数据类型的数据以及引用数据类型的引用。
例如:int a = 10; Person p = new Person(); //10 p
栈的存取速度特别快(先进后出)。
堆存放的是类的对象。
堆内存与栈内存不同,优点在于我们创建对象时,不必关注堆内存中需要开辟多少存储空间,也不必关注内存占用的时长。
方法区存放的是类信息、静态的变量、常量和成员方法。
方法区中包含了一个特殊的区域:常量池(存储的是使用static修饰的成员)。
PC寄存器存放的是当前正在执行的JVM指令的地址。
在java程序中,每一个线程启动时,都会创建一个PC寄存器。
本地方法栈指的是保存本地(native)方法的地址。
构造方法(构造器)
作用:用于对象初始化
执行时机:在创建对象时,自动调用
特点:所有的java类中都会至少存在一个构造方法
如果一个类中没有明确的编写构造方法,则编译器会自动生成一个无参的构造方法,构造方法中没有任何的代码;如果自行编写了任意一个构造器,则编译器不会再自动生成无参的构造方法。
(面试)
一个类中定义的方法是允许重载的:
1、方法名称相同
2、参数列表长度不同或参数列表类型不同或参数的顺序不同
注意:方法的重载与返回值无关。
没有对象名称的对象就是匿名对象(匿名对象只能使用一次)。
在开发中为了避免出现逻辑错误(例如年龄:-30),我们建议对所有属性进行封装,并为其提供set/get方法进行设置和取得操作。
在java基础中,this关键字是一个重要的概念,使用this关键字可以完成以下的操作:
1、调用类中的属性
2、调用类中的方法或构造方法
3、表示当前的对象 this.(自动隐藏)name = name;
static表示“静态(只有一份)”的意思,可用来修饰成员变量和成员方法,主要作用是被static关键字修饰的方法或变量不需要依赖于对象来进行访问,只要类被加载就可以通过类名.的方式去进行访问;并且不会因为对象的多次创建,而在内存中建立多份数据。
(面试)
由于静态修饰的方法在被访问时,有可能对象还未创建,因此静态(隶属于类层级)不能访问非静态(隶属于对象层级),反之则可以。
在类中的成员代码块,我们称其为构造代码块,在每次对象创建时执行,执行在构造方法之前(无论用户调用哪一个构造方法来创建对象,构造代码块都必然会执行)。
在类中使用static修饰的成员代码块,我们称其为静态代码块,在类加载时执行,每次程序从启动到关闭,只会执行一次的代码块。
(面试)
构造方法、构造代码块和静态代码块的先后执行顺序:
静态代码块 > 构造代码块 > 构造方法
包的命名:com.公司名.项目名.模块名...(全部小写)
import 包名.类名;
单例设计模式:保证程序在内存中只有一个对象存在(被程序所共享)。
继承:java中只有单继承和多重继承(子→父→爷),没有多继承。
通过super构造方法可以访问父类的方法、属性和构造方法,调用super构造方法的代码,必须写在子类构造方法的第一行(与this的用法冲突,需分开)。
(面试)
重写规则(子继承父):
1、参数列表必须完全与被重写方法的参数列表相同
2、返回类型必须完全与被重写方法的返回类型相同
3、子类中的访问权限不能比父类中被重写方法的访问权限更低
4、父类的成员方法只能被它的子类重写
5、声明为static和private的方法不能被重写,但是能够再次的声明
object类是所有类的父类,如果一个类没有明确的继承某一个具体的类,则将默认继承object类。
(面试)
Java中重写(Override)与重载(Overload)的区别:
1.发生的位置
重载:一个类中
重写:子父类中
2.参数列表限制
重载:必须不同的
重写:必须相同的
3.返回值类型
重载:与返回值无关
重写:返回值类型必须一致
4.访问权限
重载:与访问权限无关
重写:子类的方法权限必须不能低于父类的方法权限
5.异常处理:
重载:与异常无关
重写:异常范围可以更小,但不能抛出新的异常
多态:父类的引用指向子类的对象。
判断某个对象是否为指定类的实例,可以使用instanceof关键字;此操作返回boolean类型的数据。
只声明而未实现的方法称为抽象方法(未实现指的是:没有{方法体}),抽象方法必须使用abstract关键字声明。
抽象类必须使用abstract class声明,一个抽象类中可以没有抽象方法,但抽象方法必须写在抽象类或者接口中。
抽象类本身是不能直接进行实例化操作的,即:不能直接使用new关键字。
一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须重写抽象类中的全部抽象方法。
抽象类对子类具有强制性和约束性(模板设计模式)。
final(最终):用于修饰属性、变量;使变量成为常量,无法对其再次赋值。
局部变量只能赋值一次(可以先声明后赋值),成员属性必须在声明时赋值。
final修饰的类,不能被继承;修饰的方法,不能被子类重写。
(面试)
abstract(等着被继承)和private(私有化无法被继承)不能共存。
abstract(没有方法体,需要重写)和final(修饰之后不能被重写)不能共用。
abstract和static(可通过类名.的方式调用)不能同时出现。
全局常量(public static final)
常量的命名规范:由1个或多个单词组成,单词与单词之间必须使用下划线隔开,单词中所有的字母大写,例如:SQL_INSET。
如果一个类中的全部方法都是抽象方法,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口(interface),因为接口本身都是由全局常量和抽象方法组成,全局常量在编写时可以省略public static final关键字;抽象方法在编写时可以省略public abstract关键字。
如果一个接口要想使用,必须依靠子类;子类(如果不是抽象类的话)要实现接口中的所有抽象方法。
(面试)
抽象类和接口的区别:
1、定义抽象类的关键字是abstract class,而定义接口的关键字是interface
2、继承抽象类的关键字是extends,而实现接口的关键字是implements
3、继承抽象类支持单继承,而实现接口支持多实现
4、抽象类中可以有构造方法,而接口中不可以有构造方法
5、抽象类中可以有成员变量,而接口中只可以有全局常量
6、抽象类中可以有成员方法,而接口中只可以有抽象方法(Java8前的版本)
7、抽象类中增加方法时,子类可以不用重写,而接口中增加方法时,实现类需要重写(Java8前的版本)
8、从Java8开始增加了新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法要使用default关键字修饰
9、从Java9开始增加了新特性,接口中允许出现私有方法
toString方法的作用是返回对象的字符串表示形式,Object类中的toString方法,返回对象的内存地址。当使用print或println语句时,程序会自动调用toString方法(建议重写)。
==
如果是用于基本数据类型的比较,则比较的是具体的值,如果是用于引用数据类型(包装类型)的比较,则比较的是两个对象的地址。
equals方法,默认不重写的情况下比较的是两个对象的地址,方法重写后,则比较的是内容。
若需要比较两个对象某一属性的值是否相等,需要重写equals方法;与此同时,HashCode方法也需要重写(为了使结果保持一致)。
public boolean equals(object obj){
//当调用对象和参数对象的地址相同时,则内容一定相同
if(this == obj) return true;
//当参数对象为空时,则内容一定不相同
if(null == obj) return false;
//若参数对象与调用的对象不一致时,则内容一定不相同
if( !(obj instanceof Student) ) return false;
//判断调用对象和参数对象的学号是否相等
Student student = (Student)obj;
return this.getId() == student.getId();
}
当String、Math、Integer、Double等包装类在使用equals方法时,就已经覆盖了object类的 equals方法,不再是地址的比较,而是内容的比较。
在java中,可以将一个类定义在一个方法或者另一个类里面,这样的类称为内部类。(了解即可)
广泛意义上的内部类一般来说包括这四种:
1、成员内部类 2、局部内部类 3、匿名内部类 4、静态内部类
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和static成员)。
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。
如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量 外部类.this.成员方法
外部类使用成员内部类
Outter outter = new Outter(); 外部类
Outter.Inner inner = outter.new Inner();内部类
局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
匿名内部类我们必须要继承一个父类或者实现一个接口,当然也只能继承一个父类或者实现一个接口。同时它没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用,当然这个引用是隐式的。
new 父类构造器(参数列表) | 实现接口(){ //匿名内部类的类体部分 }
在使用匿名内部类的过程中,我们需要注意如下几点:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,因此局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承类或者实现接口的所有抽象方法。
6、只能访问final类型的局部变量。
静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非静态成员变量或方法。
(了解)
请描述什么是内存溢出以及什么是内存泄漏?
内存溢出:装不下,溢出来了
内存泄漏:无法使用且不会被当作垃圾回收的内容
需要对内部类有深刻的认知:内部类的对象持有外部类对象的引用
垃圾回收器回收什么样的数据:没有引用的数据会被当作垃圾清理
外部类 x:100个方法 内部类 y:10个方法
Y y = null;
{ X x = new X(); //执行完毕当前{ }内容就会超过作用域被垃圾回收
y = x.new Y(); }
↓
持有x但是无法使用,只能使用y的10个方法,从而造成内存泄漏
八种基本数据类型的包装类,可分为两种大的类型:
Number:Integer、Short、Long、Double、Float、Byte都是Number的子类,表示是一个数字。
Object:Character、Boolean都是Object的直接子类。
将一个基本数据类型变为包装类,这样的操作称为装箱操作。
将一个包装类变为基本数据类型,这样的操作称为拆箱操作。
(面试)
//-128~127自动装箱
Integer i1 = 127;//若将127改为128,结果为FTFT
Integer i2 = 127;
Integer i3 = new Integer(127);
Integer i4 = new Integer(127);
System.out.println(i1 == i2); // T,比较地址
System.out.println(i1.equals(i2)); // T,比较内容
System.out.println(i3 == i4); // F,比较地址
System.out.println(i3.equals(i4)); // T,比较内容
使用包装类还有一个优点在于:可以将一个字符串变为指定的基本数据类型,此操作一般在接收输入数据上使用较多。
String text = input.nextLine();
int x = Integer.parseInt(text); //将字符串变为Integer类型并进行拆箱操作
(面试)
int ia = Integer.parseInt("12345");//实现字符串到整数的转换
System.out.println(ia);
String S1 = "" + ia;//实现整数到字符串的转换
System.out.println(S1);
int...nums表示可变参数,调用时可以传递0~n个数字;在方法的内部可变参数以数组的形式作为载体体现。
递推(1*2*3*4*5)使用for循环的方式
递归(!5)是指在方法的定义中调用方法本身,使用if语句判断
Object→Throwable(父类)→Error(错误)JVM发出只能尽量避免,无法代码处理
↓
Exception(异常,try-catch处理)
↓ ↓
受检异常 非受检异常RuntimeException
(必须明确抛出,飘红) (运行时异常,不飘红)
异常是指在程序中导致程序中断运行的一种指令流,对异常进行处理的标准处理格式,如下:
格式一
try{
// 有可能发生异常的代码段
}catch(异常类型1 对象名1){
// 异常的处理操作
}catch(异常类型2 对象名2){
// 异常的处理操作
} finally{
// 异常的统一出口
}
格式二
try{
// 有可能发生异常的代码段
}catch(异常类型1 | 异常类型2 对象名){
// 异常的处理操作
}
格式三
try{
// 有可能发生异常的代码段
}catch(Exception 对象名){
// 异常的处理操作
// 处理所有类型的异常,扩大范围
}
在进行异常的处理之后,异常的处理格式中还有一个finally语句,此语句将作为异常的统一出口,不管是否产生了异常,最终都要执行此段代码。
常见的异常类型:
ArithmeticException 算数异常 NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ClassNotFoundException 未找到相应类异常 IOException 输入输出异常
ClassCastException 类型转换异常 SQLException 操作数据库异常
throws关键字主要在方法的声明上使用,表示方法中不处理异常,交给调用处处理。
throw关键字表示在程序中人为的抛出一个异常,因为从异常处理机制来看,所有的异常一旦产生之后,实际上抛出的就是一个异常类的实例化对象,那么此对象也可以由throw直接抛出。
代码:throw new Exception("抛着玩的。") ;
问:异常是否抛出去,应该站在哪个角度思考?
答:如果是因为传参而导致异常,应通过throws将其抛出。
只要是RuntimeException的子类,则表示程序在操作的时候可以不必使用try-catch进行处理,如果有异常发生,则由JVM进行处理。
(面试)
问:try-catch-finally中哪个部分可以省略?
答:catch和finally可以省略其中一个,但是不能同时省略。
注意:格式上允许省略catch块, 但是发生异常时就不会捕获异常了,我们在开发中也不会这样去写。
(面试)
问:try-catch-finally中,如果catch中return了,finally 还会执行吗?
答:finally中的代码会执行。
执行流程:
1、先计算返回值,并将返回值存储起来,等待返回
2、执行finally代码块
3、将之前存储的返回值,返回出去
注意:如果返回的是变量,返回值是在finally运算之前就确定了,并且缓存了,不管finally对该值做任何的改变,返回的值都不会改变。
(面试)
问:try、catch、finally中,如果都包含return代码,返回值以谁的为最终返回值?
答:finally中的返回值为最终返回值。
(面试)
问:如果try或catch通过System.exit(0);退出程序,finally块还会执行吗?
答:不执行。
System.exit(0);表示停止JVM,虚拟机停止,则所有java代码停止。