java属于面相_Java面试笔试之面相对象技术(一)

news/2024/7/5 9:32:25

一、基本概念

1.1 面相对象的三大特性

继承、封装、多态。

(1)继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类哪里继承方法和实例变量,并且子类可以修改或增加新的方法使之更适合特殊的需要。

(2)封装:封装是指将客观事物抽象成类,每个类对自身的数据和方法实行保护。类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

(3)多态:多态是指允许不同类的对象对同一消息做出响应。多态包括参数化多态和包含多态。多态性语言具有灵活、抽象、行为共享和代码共享的优势,很好地解决了应用程序方法同名问题。

1.2 super和this关键字

在java语言中,每当一个对象被创建后,java虚拟机都会给这个对象分配一个引用自身的指针,这个指针的名字就是this,因此关键字this指的是对当前对象的引用,对象只有被实例化后才存在。同时,this只能在类中的非静态方法中使用,静态方法和静态代码块中绝对不能出现this。而超类(super class)也叫作父类,在java语言中,指的是被继承的类。而继承的类叫做子类,当超类(父类)的实例方法或类方法为private时,是不能被子类调用的。同理,当其他类的实例方法为private时,也不能直接被调用。关键字super指的是对当前对象里面的父类对象的引用。当引用当前对象的某个方法或某个成员时,通常会使用this,而通过super可以调用父类的构造方法、父类的方法和属性。示例代码如下:

classBase{public int status = 0;

Base(int status){this.status =status;}public voidprint(){

System.out.println("base");

}

}class Sub extendsBase{public int status = 1;

Sub(intstatus) {super(status - 1);this.status =status;

}public voidprintSub(){

System.out.println("sub");

System.out.println("status ="+status);

}public voidprintBase(){super.print();

System.out.println("status ="+super.status);

}

}public classTest{public static voidmain(String[] args) {

Sub sub= new Sub(2);

sub.printBase();

sub.printSub();

}

}

程序的运行结果为:

dcf4422f11dd4b4b056e5a8e995f50e0.png

因此,关键字super是在子类对象中指代其父类对象的引用,只能表示父类的的引用,不能表示父类的父类。

1.3 内部类

在Java语言中,可以把一个类定义到另外一个类的内部,在类里面的这个类就叫做内部类,外面的类叫做外部类。在这种情况下,这个内部类可以被看成外部类的一个成员(与类的属性和方法类似)。还有一种类被称为顶层类(Top-level class),指的是类定义代码不嵌套在其他类定义中的类。

内部类主要有以下四种:静态内部类(Static Inner Class)、成员内部类(Member Inner Class)、局部内部类(Local Inner Class)、匿名内部类(Anonymous Inner Class)。他们的定义方法如下:

classOuterClass{static class InnerClass{} //静态内部类

}

classOuterClass{class InnerClass{} //成员内部类(普通内部类)

}

classOuterClass{public voidmemberFunction(){classInnerClass{

}//局部内部类

}

}

public class MyFrame extends Frame { //外部类

publicMyFrame(){

addWindowListener(new WindowAdapter() { //匿名内部类

@Overridepublic voidwindowClosing(WindowEvent e) {

dispose();

System.exit(0);

}

});

}

}

静态内部类是指被声明为static的内部类,它可以不依赖于外部类实例而被实例化,而通常的内部类需要在外部类实例化后才能实例化。静态内部类不能与外部类有相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法(包括私有类型)。一个静态内部类,如果去掉“static”关键字,就成为成员内部类,成员内部类为非静态内部类,它可以自由地引用外部类的属性和方法,无论这些属性是静态的还是非静态的。但是它与一个实例绑定在了一起,不可以定义静态的属性和方法。只有在外部的类被实例化后,这个内部类才能被实例化。需要注意的是,非静态内部类中不能有静态成员。

局部内部类指的是定义在一个代码块内的类,它的作用为其所在的代码块,是内部类中最少使用到的一种类型。局部内部类像局部变量一样,不能被public、protected、private以及static修饰。对一个静态内部类,去掉其声明中的“static”关键字,将其定义移入其外部类的静态方法或静态初始化代码段中就成为局部静态内部类。对一个成员类,将其定义移入其外部类的实例方法或实例初始化代码中就成为局部内部类。局部静态内部类与静态内部类的基本特性相同。局部内部类与内部类的基本特性相同。

匿名内部类是一种没有类名的内部类,不适用关键字class、extend和implements。没有构造方法,他必须继承其他类或实现其他接口。匿名内部类的一般好处是代码更加简洁、紧凑,但带来的问题是易读性下降。它一般适用于GUI编程中实现事件的处理等。

1.4 java的初始化

java程序的初始化一般遵循以下三个原则(以下三个原则优先级依次递减):①静态对象(变量)优先于非静态对象(变量)初始化,其中静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化多次;②父类优先于子类进行初始化;③按照成员变量定义顺序就行初始化,即使变量定义散布于方法定义中,他们依然在任何方法(包括构造方法)被调用之前先初始化。

java程序初始化工作可以在许多不同的代码块中完成(例如静态代码块、构造函数等),他们执行的顺序如下:父类静态变量、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类的构造函数、子类非静态变量、子类非静态代码块、子类的构造函数。例如:

public class HelloB extendsHelloA{publicHelloB(){

System.out.println("HelloB");

}

{

System.out.println("I'm B class");

}static{

System.out.println("static B");

}public voidmain(String[] args){newHelloB();

}

}classHelloA{publicHelloA(){

System.out.println("HelloA");

}

{

System.out.println("I'm A class");

}static{

System.out.println("static A");

}

}

程序的运行结果为:

de8d94cd8dac5c1b8845c8f7227092f1.png

1.5  动态绑定实现多态

由于子类可以覆盖父类的方法,因此同样的方法会在父类和子类中有不同的表现形式。在Java语言中,基类的引用变量不仅可以指向基类的实例对象,也可以指向其子类的实例对象。同样,接口的引用变量也可以指向其实现类的实例对象。而程序调用的方法在运行期才动态绑定(绑定指的是将一个方法调用和一个方法主体连接到一起),就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。通过这种动态绑定的方法实现了多态。例如:

classBase{int num =1;publicBase(){this.print();

num=2;

}public voidprint(){

System.out.println("Base.num="+num);

}

}class Sub extendsBase{int num =3;publicSub(){this.print();

num=4;

}

@Overridepublic voidprint(){

System.out.println("Sub.num="+num);

}

}public classTest{public static voidmain(String[] args) {

Base base= newSub();

System.out.println(base.num);

}

}

运行结果为:

c8a0d77ac38b1365dd42dd749b2d15d0.png

1.6 Java的反射机制

有如下代码:

classReadOnlyClass{private Integer age = 20;public Integer getAge(){returnage;}

}

现给定一个ReadOnlyClass的对象roc,能否把这个对象的age值改成30?

从正常的编程角度出发分析会发现,在这段代码中,age属性被修饰为private,而且这个类只提供了getAge的public方法,而没有提供修改age的方法,因此这个类是一个只读的类,无法修改age的值。但是Java语言中还有一个非常强大的特性:反射机制,因此可以通过反射机制来修改age的值。

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取对象的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制铜须程序在运行时加载、探知、使用编译期间完全未知的class。换句话说,Java可以加载一个运行时才得知名称的class,获得其完整结构。

在Java语言中,任何一个类都可以得到对应的class实例,通过class实例就可以获取类或对象的所有信息,包括属性(field对象)、方法(Method对象)或构造方法(Constructor对象)。对于这段代码而言,在获取到ReadOnlyClass类的Class实例后,就可以通过反射机制获取到age属性对应的Field对象,然后可以通过这个对象来修改age的值。实现代码如下:

importjava.lang.reflect.Field;classReadOnlyClass{private Integer age = 20;publicInteger getAge() {returnage;

}

}public classTest{public static void main(String[] args) throwsException {

ReadOnlyClass pt= newReadOnlyClass();

Class> clazz = ReadOnlyClass.class;

Field field= clazz.getDeclaredField("age");

field.setAccessible(true);

field.set(pt,30);

System.out.println(pt.getAge());

}

}

运行结果为:

30

1.7 构造方法

构造方法是一种特殊的方法,用来在对象实例化时初始化对象的成员变量。在Java语言中,构造方法具有以下特点:

①构造方法必须与类的名字相同,并且不能有返回值(返回值也不能为void);

②每个类可以有多个构造方法。当开发人员没有提供构造方法时,编译器在把源代码编译成字节码的过程中会提供一个没有参数的默认构造方法,但该构造方法不会执行任何代码。如果开发人员提供了构造方法,那么编译器就不会再创建默认的构造方法;

③构造方法可以有0个、1个或1个以上的参数;

④构造方法总是伴随着new操作一起调用,不能有程序的编写者直接调用,必须要由系统调用,构造方法在对象实例化时会被自动调用,且只运行一次,而普通的方法是在程序执行到它时才被调用,可以被该对象调用多次;

⑤构造方法的主要作用是完成对象的初始化工作;

⑥构造方法不能被继承,因此不能被覆盖,但是构造方法能够被重载,可以使用不同的参数个数或参数类型来定义多个构造方法;

⑦子类可以通过关键字super来显式地调用父类的构造方法。当父类没有提供无参数的构造方法时,子类的构造方法中必须显式地调用父类的构造方法,如果父类提供了无参数的构造方法时,此时子类就可以不显式地调用父类的构造方法,在这种情况下,编译器会默认调用父类的无参数的构造方法。当有父类时,在实例化对象时,会首先执行父类的构造方法,然后才执行子类的构造方法;

⑧当父类和子类都没有定义构造方法时,编译器会为父类生成一个默认的无参数的构造方法,给子类也生成一个默认的无参数的构造方法。此外,默认构造器的修饰符只与当前类的修饰符有关(例如,如果一个类被定义为public,那么他的构造方法也是public)。

如下代码:

classBase {

Base(){System.out.println("Base");}

}public class Alpha extendsBase{public static voidmain(String[] args) {newAlpha();newBase();

}

}

当调用new Alpha()时,会首先调用父类的无参数的构造方法,输出Base,然后调用Alpha类的构造方法(编译器提供了一个默认的构造方法),接着在调用new Base()时也会调用Base类的构造方法输出Base。

结果为:

Base

Base

如下代码是People和Child类的定义,每个构造方法都输出编号,在执行new Child("c")时,程序的运行结果为:

classPeople {

String name;public People(){System.out.print(1);}publicPeople(String name){

System.out.print(2);this.name =name;

}

}class Child extendsPeople{

People father;publicChild(String name){

System.out.print(3);this.name =name;

father= new People(name+":F");

}public Child(){System.out.print(4);}

}

在调用new Child(“c”)时,由于Child继承了People,但是Child类的构造方法中没有显式地调用父类的构造方法,因此编译器会默认调用父类的无参数的构造方法,因此首先会输出1,接着才会调用Child类的构造方法输出3,在Child的构造方法中,调用new People(name+":F")语句会调用People类的有参数的构造方法,输出2。因此运行结果为:

132

那么有如下代码:

public classEx{public static voidmain(String[] args) {

Fx f= new Fx(5);

}

Ex(){System.out.println("Ex,no-args");}

Ex(int i){System.out.println("Ex,int");}

}class Fx extendsEx{

Fx(){super();

System.out.println("Fx,no-args");

}

Fx(inti){this();

System.out.println("Fx,int");

}

}

运行结果为:

0453755613030b8dd86065fb3b707e01.png

只有当子类的构造方法没有调用父类的构造方法时,编译器才会默认地去调用父类中无参数的构造方法。类Fx中无参数的构造方法显然已经调用了父类的构造方法,而Fx类中带参数的构造方法也通过调用无参数的构造方法间接调用了父类的构造方法。在调用Fx f = new Fx(5)时,先调用Fx的构造方法,在这个方法中,首先会调用无参数的构造方法,在无参数的构造方法中,首先会调用父类的无参数的构造方法输出Ex,no-args,接着在类Fx无参数的构造方法中输出Fx,no-args,最后调用类Fx有参数的构造方法,输出Fx,int。

那么问题来了,构造方法、成员变量初始化以及静态成员变量初始化三者的先后顺序是什么呢?

当类第一次被加载的时候,静态变量会首先初始化,接着编译器会把实例变量初始化为默认值,然后执行构造方法。因此顺序为:静态成员变量->成员变量->构造方法。

Java程序的初始化可参见目录1.4。

在Java语言中,最常用的创建对象的方法为使用new创建一个对象,这种对象通过调用类的构造方法来完成对象的创建,那么不通过构造方法可不可以创建对象呢?

其实在Java语言中,除了以上使用new方式来创建对象以外,还有以下几种创建对象的方法:

(1)调用对象的clone方法,需要以下几个步骤才能使用clone方法:

①实现clone的类首先需要继承Cloneable接口。Cloneable接口实质上是一个标识接口,没有任何接口方法;

②在类中重写(override)Object类中的clone方法;

③在clone方法中调用super.clone()。无论clone类的继承结构是什么,super.clone()都会直接或间接调用java.lang.Object类的clone()方法。

示例代码如下:

class Obj implementsCloneable{private int aInt = 0;publicObj(){

System.out.println("construct");

}public int getaInt(){returnaInt;}public void setaInt(intaInt) {this.aInt =aInt;

}public void changeInt(){this.aInt = 1;}

@OverridepublicObject clone(){

Object o= null;try{

o= (Obj)super.clone();

}catch(CloneNotSupportedException e){

e.printStackTrace();

}returno;

}

}public classTest{public static voidmain(String[] args) {

Obj a= newObj();

Obj b=(Obj)a.clone();

b.changeInt();

System.out.println("a:"+a.getaInt());

System.out.println("b:"+b.getaInt());

}

}

程序的运行结果为:

construct

a:0b:1

(2)通过反射机制来创建对象,示例代码如下:

classPerson{

String name= "Jack";publicPerson(){

System.out.println("construct");

}

@Overridepublic String toString(){returnname;}

}public classTest{public static voidmain(String[] args) {

Class classType;try{

classType= Class.forName("Person");

Person p=(Person)classType.newInstance();

System.out.println(p);

}catch(Exception e){

e.printStackTrace();

}

}

}

运行结果为:

construct

Jack

(3)通过反序列化的方式来创建对象,示例代码如下:

import java.io.*;public class People implementsSerializable{privateString name;publicPeople(){this.name = "lili";

System.out.println("construct");

}

@Overridepublic String toString(){return this.name;}public static voidmain(String[] args) {

People p= newPeople();

System.out.println(p);

ObjectInputStream ois= null;

ObjectOutputStream oos= null;try{

FileOutputStream fos= new FileOutputStream("people.out");

oos= newObjectOutputStream(fos);

oos.writeObject(p);

oos.close();

}catch(Exception e){

}

People p1;try{

FileInputStream fis= new FileInputStream("people.out");

ois= newObjectInputStream(fis);

p1=(People)ois.readObject();

System.out.println(p1);if (p !=p1)

System.out.println("two different object");

ois.close();

}catch(Exception e){

}

}

}

程序的运行结果为:

construct

lili

lili

two different object


http://www.niftyadmin.cn/n/4204506.html

相关文章

vue中elementui command绑定变量对象方法

需求&#xff1a;点击下拉框&#xff0c;切换组&#xff0c;选中当前项 <el-dropdown trigger"click" class"child-controllerChild"command"(command) >handleDispatchTabClickBoxCommand(command)"><img class"child-iconAnd…

浮动图片(JS)

代码作用&#xff1a;鼠标上移到图片上时&#xff0c;图片浮动显现&#xff0c;就像是Apple的浮动菜单一样的效果。<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">&l…

vue组件和js实现鼠标悬停显示title效果

需求&#xff1a; 显示文本内容过长&#xff0c;显示…鼠标悬浮时&#xff0c;全部显示 使用element组件<el-tooltip offset"-2" class"item" effect"dark" placement"top"> <span class"dispatchSystemAddressBookIt…

java 判断以中文开始_java判断是否是中文字符

public class StringUtil {/*** 判断是否为中文字符* param c* return*/private static boolean isChinese(char c) {// GENERAL_PUNCTUATION 判断中文的“号// CJK_SYMBOLS_AND_PUNCTUATION 判断中文的。号// HALFWIDTH_AND_FULLWIDTH_FORMS 判断中文的&#xff0c;号Characte…

百度-相信中国-电子书-下载

/Files/dayouluo/相信中国.rar

JS之给元素添加类的方法

原生js中添加类的方法 //1.为 <div> 元素添加一个类: document.getElementById("div").classList.add("类名");//2.为 <div> 元素添加多个类: document.getElementById("div").classList.add("类名1","类名2",…

java中io操作详解_Java语言中的IO系统详解

Java语言中的IO系统Java的核心库java.io提供了全面的IO接口&#xff0c;包括&#xff1a;文件读写&#xff0c;标准设备输出等等。Java中IO是以流为基础进行输入输出的&#xff0c;所有数据被串行化写入输出流&#xff0c;或者从输入流读入。在具体使用中很多初学者对Java.io包…

工程师侵入北京移动数据库 获利370余万元

程稚瀚称&#xff0c;侵入北京移动数据库是因为他对移动的“霸王条款”不满。  在5个月的时间里&#xff0c;软件研发工程师程稚瀚利用互联网4次侵入北京移动充值中心数据库&#xff0c;盗取充值卡密码并通过淘宝网出售&#xff0c;共获利370余万元。昨天&#xff0c;这起全国…