编程语言面试常用题

来源:互联网 发布:手机arp扫描软件 编辑:程序博客网 时间:2024/06/06 19:09

1 .虚函数

虚函数(impure virtual),C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。子类可以重写父类的虚函数实现子类的特殊化。

纯虚函数(pure virtual),C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。C++中的纯虚函数更像是“只提供申明,没有实现”,是对子类的约束,是“接口继承”。C++中的纯虚函数也是一种“运行时多态”。

class A{public:    virtual void out1(string s)=0;//纯虚函数    virtual void out2(string s)    {        cout<<"A(out2):"<<s<<endl;    }};
#include <iostream>using namespace std;class A{public:    virtual void out1()=0;  ///由子类实现    virtual ~A(){};    virtual void out2() ///默认实现    {        cout<<"A(out2)"<<endl;    }    void out3() ///强制实现    {        cout<<"A(out3)"<<endl;    }};class B:public A{public:    virtual ~B(){};    void out1()    {        cout<<"B(out1)"<<endl;    }    void out2()    {        cout<<"B(out2)"<<endl;    }    void out3()    {        cout<<"B(out3)"<<endl;    }};int main(){    A *ab=new B;    ab->out1();    ab->out2();    ab->out3();    cout<<"************************"<<endl;    B *bb=new B;    bb->out1();    bb->out2();    bb->out3();    delete ab;    delete bb;    return 0;}

这里写图片描述

2 . java的反射机制浅谈

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

java.lang.Class

     Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。
import java.lang.reflect.Array;   import java.lang.reflect.Constructor;   import java.lang.reflect.Field;   import java.lang.reflect.Method;   /**   * Java Reflection Cookbook   *   * @author Michael Lee   * @since 2006-8-23   * @version 0.1a   */  public class Reflection {       /**       * 得到某个对象的公共属性       *       * @param owner, fieldName       * @return 该属性对象       * @throws Exception       *       */      public Object getProperty(Object owner, String fieldName) throws Exception {           Class ownerClass = owner.getClass();           Field field = ownerClass.getField(fieldName);           Object property = field.get(owner);           return property;       }       /**       * 得到某类的静态公共属性       *       * @param className   类名       * @param fieldName   属性名       * @return 该属性对象       * @throws Exception       */      public Object getStaticProperty(String className, String fieldName)               throws Exception {           Class ownerClass = Class.forName(className);           Field field = ownerClass.getField(fieldName);           Object property = field.get(ownerClass);           return property;       }       /**       * 执行某对象方法       *       * @param owner       *            对象       * @param methodName       *            方法名       * @param args       *            参数       * @return 方法返回值       * @throws Exception       */      public Object invokeMethod(Object owner, String methodName, Object[] args)               throws Exception {           Class ownerClass = owner.getClass();           Class[] argsClass = new Class[args.length];           for (int i = 0, j = args.length; i < j; i++) {               argsClass[i] = args[i].getClass();           }           Method method = ownerClass.getMethod(methodName, argsClass);           return method.invoke(owner, args);       }         /**       * 执行某类的静态方法       *       * @param className       *            类名       * @param methodName       *            方法名       * @param args       *            参数数组       * @return 执行方法返回的结果       * @throws Exception       */      public Object invokeStaticMethod(String className, String methodName,               Object[] args) throws Exception {           Class ownerClass = Class.forName(className);           Class[] argsClass = new Class[args.length];           for (int i = 0, j = args.length; i < j; i++) {               argsClass[i] = args[i].getClass();           }           Method method = ownerClass.getMethod(methodName, argsClass);           return method.invoke(null, args);       }       /**       * 新建实例       *       * @param className       *            类名       * @param args       *            构造函数的参数       * @return 新建的实例       * @throws Exception       */      public Object newInstance(String className, Object[] args) throws Exception {           Class newoneClass = Class.forName(className);           Class[] argsClass = new Class[args.length];           for (int i = 0, j = args.length; i < j; i++) {               argsClass[i] = args[i].getClass();           }           Constructor cons = newoneClass.getConstructor(argsClass);           return cons.newInstance(args);       }       /**       * 是不是某个类的实例       * @param obj 实例       * @param cls 类       * @return 如果 obj 是此类的实例,则返回 true       */      public boolean isInstance(Object obj, Class cls) {           return cls.isInstance(obj);       }       /**       * 得到数组中的某个元素       * @param array 数组       * @param index 索引       * @return 返回指定数组对象中索引组件的值       */      public Object getByArray(Object array, int index) {           return Array.get(array,index);       }   }  

3 . 线程同步

public class TraditionalThreadSynchronized {    public static void main(String[] args) {        final Outputter output = new Outputter();        new Thread() {            public void run() {                output.output("zhangsan");            }        }.start();              new Thread() {            public void run() {                output.output("lisi");            }        }.start();    }}class Outputter {    public void output(String name) {        // TODO 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符        for(int i = 0; i < name.length(); i++) {            System.out.print(name.charAt(i));            // Thread.sleep(10);        }    }}

( 1). 使用synchronized将需要互斥的代码包含起来,并上一把锁。

{    synchronized (this) {        for(int i = 0; i < name.length(); i++) {            System.out.print(name.charAt(i));        }    }}

这把锁必须是需要互斥的多个线程间的共享对象,像下面的代码是没有意义的。

{    Object lock = new Object();    synchronized (lock) {        for(int i = 0; i < name.length(); i++) {            System.out.print(name.charAt(i));        }    }}

每次进入output方法都会创建一个新的lock,这个锁显然每个线程都会创建,没有意义。
(2) . 将synchronized加在需要互斥的方法上。

public synchronized void output(String name) {    // TODO 线程输出方法    for(int i = 0; i < name.length(); i++) {        System.out.print(name.charAt(i));    }}

这种方式就相当于用this锁住整个方法内的代码块,如果用synchronized加在静态方法上,就相当于用××××.class锁住整个方法内的代码块。
(3) . volatile
volatile是第二种Java多线程同步的机制,根据JLS(Java LanguageSpecifications)的说法,一个变量可以被volatile修饰,在这种情况下内存模型(主内存和线程工作内存)确保所有线程可以看到一致的变量值,来看一段代码:

class Test {    static int i = 0, j = 0;    static void one() {        i++;        j++;    }    static void two() {        System.out.println("i=" + i + " j=" + j);    }}

一些线程执行one方法,另一些线程执行two方法,two方法有可能打印出j比i大的值,按照之前分析的线程执行过程分析一下:

    1. 将变量i从主内存拷贝到工作内存;    2. 改变i的值;    3. 刷新主内存数据;    4. 将变量j从主内存拷贝到工作内存;    5. 改变j的值;    6. 刷新主内存数据;

这个时候执行two方法的线程先读取了主存i原来的值又读取了j改变后的值,这就导致了程序的输出不是我们预期的结果,要阻止这种不合理的行为的一种方式是在one方法和two方法前面加上synchronized修饰符:

class Test {    static int i = 0, j = 0;    static synchronized void one() {        i++;        j++;    }    static synchronized void two() {        System.out.println("i=" + i + " j=" + j);    }}

根据前面的分析,我们可以知道,这时one方法和two方法再也不会并发的执行了,i和j的值在主内存中会一直保持一致,并且two方法输出的也是一致的。另一种同步的机制是在共享变量之前加上volatile:

class Test {    static volatile int i = 0, j = 0;    static void one() {        i++;        j++;    }    static void two() {        System.out.println("i=" + i + " j=" + j);    }}

one方法和two方法还会并发的去执行,但是加上volatile可以将共享变量i和j的改变直接响应到主内存中,这样保证了主内存中i和j的值一致性,然而在执行two方法时,在two方法获取到i的值和获取到j的值中间的这段时间,one方法也许被执行了好多次,导致j的值会大于i的值。所以volatile可以保证内存可见性,不能保证并发有序性。

4 .抽象类和接口的区别

1.语法层面上的区别

   1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;   2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;   3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;   4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

2.设计层面上的区别

(1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。(2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

5 .Spring IOC

IOC容器的概念
IOC容器就是具有依赖注入功能的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。

Spring IOC容器如何知道哪些是它管理的对象呢?这就需要配置文件,Spring IOC容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于xml配置文件进行配置元数据,而且Spring与配置文件完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于java文件的、基于属性文件的配置都可以。
那Spring IOC容器管理的对象叫什么呢?

Bean的概念
由IOC容器管理的那些组成你应用程序的对象我们就叫它Bean, Bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。那IOC怎样确定如何实例化Bean、管理Bean之间的依赖关系以及管理Bean呢?这就需要配置元数据,在Spring中由BeanDefinition代表,后边会详细介绍,配置元数据指定如何实例化Bean、如何组装Bean等。概念知道的差不多了,让我们来做个简单的例子。

package com.ljq.test;public interface HelloService {    public void sayHello();  }package com.ljq.test;public class HelloServiceImpl implements HelloService{    public void sayHello(){        System.out.println("Hello World!");    }}public class Excute{     public HelloService helloService;     void setHelloService(HelloService helloService){//set依赖注解,也可以使用构造依赖注解         this.helloService=helloService;     }     void sayHello()     {         helloService.sayHello();     }}<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="        http://www.springframework.org/schema/beans                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/context                        http://www.springframework.org/schema/context/spring-context-3.0.xsd">    <!-- id 表示组件的名字,class表示组件类 -->    <bean id="helloService" class="com.ljq.test.HelloServiceImpl" />    <bean id="excute"         class="com.ljq.test.Excute">        <property id="helloService">            <ref bean="helloService" />        </property>    </bean></beans>package com.ljq.test;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 测试 *  * @author 林计钦 * @version 1.0 2013-11-4 下午10:56:04 */public class HelloServiceTest {    @Test    public void testHelloWorld() {        // 1、读取配置文件实例化一个IOC容器        ApplicationContext context = new ClassPathXmlApplicationContext("helloworld.xml");        // 2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现”        Excute excute= context.getBean("excute", Excute.class);        // 3、执行业务逻辑        excute.sayHello();    }}

在上面的代码例子中就包括控制反转和set依赖注入。
 

0 0
原创粉丝点击