android 面试汇总二

  • Animation

Q:Android中有哪几种类型的动画?

  • 技术点:动画类型
  • 参考回答: 常见三类动画
    • View动画(View Animation)/补间动画(Tween animation):对View进行平移、缩放、旋转和透明度变化的动画,不能真正的改变view的位置。应用如布局动画、Activity切换动画
    • 逐帧动画(Drawable Animation):是View动画的一种,它会按照顺序播放一组预先定义好的图片
    • 属性动画(Property Animation):对该类对象进行动画操作,真正改变了对象的属性

Q:帧动画在使用时需要注意什么?

  • 技术点:帧动画
  • 参考回答:使用祯动画要注意不能使用尺寸过大的图片,否则容易造成OOM

Q:View动画和属性动画的区别?

  • 技术点:View动画、属性动画
  • 参考回答:

Q:View动画为何不能真正改变View的位置?而属性动画为何可以?

  • 技术点:View动画
  • 参考回答:View动画改变的只是View的显示,而没有改变View的响应区域;而属性动画会通过反射技术来获取和执行属性的get、set方法,从而改变了对象位置的属性值。

Q:属性动画插值器和估值器的作用?

  • 技术点:属性动画
  • 参考回答:
    • 插值器(Interpolator):根据时间流逝的百分比计算出当前属性值改变的百分比。确定了动画效果变化的模式,如匀速变化、加速变化等等。View动画和属性动画均可使用。常用的系统内置插值器:
      • 线性插值器(LinearInterpolator):匀速动画
      • 加速减速插值器(AccelerateDecelerateInterpolator):动画两头慢中间快
      • 减速插值器(DecelerateInterpolator):动画越来越慢
    • 类型估值器(TypeEvaluator):根据当前属性改变的百分比计算出改变后的属性值。针对于属性动画,View动画不需要类型估值器。常用的系统内置的估值器:
      • 整形估值器(IntEvaluator)
      • 浮点型估值器(FloatEvaluator)
      • Color属性估值器(ArgbEvaluator)
  • Window

Q:Activity、View、Window三者之间的关系?

  • 技术点:Activity、View、Window联系
  • 思路:围绕Window是Activity和View的桥梁展开
  • 参考回答:在Activity启动过程其中的attach()方法中初始化了PhoneWindow,而PhoneWindow是Window的唯一实现类,然后Activity通过setContentView将View设置到了PhoneWindow上,而View通过WindowManager的addView()、removeView()、updateViewLayout()对View进行管理。

Q:Window有哪几种类型?

  • 技术点:Window类型
  • 参考回答:Window有三种类型:
    • 应用Window:对应一个Activity。
    • 子Window:不能单独存在,需附属特定的父Window。如Dialog。
    • 系统Window: 需申明权限才能创建。如Toast。

Q:Activity创建和Dialog创建过程的异同?

  • 技术点:Window创建
  • 参考回答:Dialog的Window创建过程:
    • 创建WindowDialog。和Activity类似,同样是通过PolicyManager.makeNewWindow() 来实现。
    • 初始化DecorView并将Dialog的视图添加到DecorView中去。和Activity类似,同样是通过Window.setContentView() 来实现。
    • 将DecorView添加到Window中显示。和Activity一样,都是在自身要出现在前台时才会将添加Window。
      • Dialog.show() 方法:完成DecorView的显示。
      • WindowManager.remoteViewImmediate() 方法:当Dialog被dismiss时移除DecorView。
  • Handler

Q:谈谈消息机制Hander?作用?有哪些要素?流程是怎样的?

  • 技术点:消息机制
  • 参考回答:
    • 作用:跨线程通信。当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
    • 四要素:
      • Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
      • MessageQueue(消息队列):用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
      • Handler(处理者):负责Message的发送及处理。通过 Handler.sendMessage() 向消息池发送各种消息事件;通过 Handler.handleMessage() 处理相应的消息事件。
      • Looper(消息泵):通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
    • 具体流程如图


      • Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;
      • 通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next()
      • 调用目标Handler.dispatchMessage()去传递消息,目标Handler收到消息后调用Handler.handlerMessage()处理消息。

Q:为什么系统不建议在子线程访问UI?

  • 技术点:UI线程、子线程
  • 参考回答:系统不建议在子线程访问UI的原因是,UI控件非线程安全,在多线程中并发访问可能会导致UI控件处于不可预期的状态。而不对UI控件的访问加上锁机制的原因有:
    • 上锁会让UI控件变得复杂和低效
    • 上锁后会阻塞某些进程的执行

Q:一个Thread可以有几个Looper?几个Handler?

  • 技术点:Looper、Handler
  • 参考回答:一个Thread只能有一个Looper,可以有多个Handler
  • 引申:更多数量关系:Looper有一个MessageQueue,可以处理来自多个Handler的Message;MessageQueue有一组待处理的Message,这些Message可来自不同的Handler;Message中记录了负责发送和处理消息的Handler;Handler中有Looper和MessageQueue;

Q:如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?

  • 技术点:Looper
  • 参考回答:通过Looper.prepare()可将一个Thread线程转换成Looper线程。Looper线程和普通Thread不同,它通过MessageQueue来存放消息和事件、Looper.loop()进行消息轮询。

Q:可以在子线程直接new一个Handler吗?那该怎么做?

  • 技术点:Handler
  • 参考回答:不同于主线程直接new一个Handler,由于子线程的Looper需要手动去创建,在创建Handler时需要多一些方法:
new Thread(new Runnable() {@Overridepublic void run() { Looper.prepare();//为子线程创建Looper new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //子线程消息处理 } }; Looper.loop(); //开启消息轮询 } }).start(); 

Q:Message可以如何创建?哪种效果更好,为什么?

  • 技术点:Message
  • 参考回答:创建Message对象的几种方式:
    • Message msg = new Message();
    • Message msg = Message.obtain();
    • Message msg = handler1.obtainMessage();
      后两种方法都是从整个Messge池中返回一个新的Message实例,能有效避免重复Message创建对象,因此更鼓励这种方式创建Message

Q:这里的ThreadLocal有什么作用?

  • 技术点:ThreadLocal
  • 参考回答:ThreadLocal类可实现线程本地存储的功能,把共享数据的可见范围限制在同一个线程之内,无须同步就能保证线程之间不出现数据争用的问题,这里可理解为ThreadLocal帮助Handler找到本线程的Looper。
    • 底层数据结构:每个线程的Thread对象中都有一个ThreadLocalMap对象,它存储了一组以ThreadLocal.threadLocalHashCode为key、以本地线程变量为value的键值对,而ThreadLocal对象就是当前线程的ThreadLocalMap的访问入口,也就包含了一个独一无二的threadLocalHashCode值,通过这个值就可以在线程键值值对中找回对应的本地线程变量。

Q:主线程中Looper的轮询死循环为何没有阻塞主线程?

  • 技术点:Looper
  • 参考回答:Android是依靠事件驱动的,通过Loop.loop()不断进行消息循环,可以说Activity的生命周期都是运行在 Looper.loop()的控制之下,一旦退出消息循环,应用也就退出了。而所谓的导致ANR多是因为某个事件在主线程中处理时间太耗时,因此只能说是对某个消息的处理阻塞了Looper.loop(),反之则不然。

Q:使用Hanlder的postDealy()后消息队列会发生什么变化?

  • 技术点:Handler
  • 参考回答:post delay的Message并不是先等待一定时间再放入到MessageQueue中,而是直接进入并阻塞当前线程,然后将其delay的时间和队头的进行比较,按照触发时间进行排序,如果触发时间更近则放入队头,保证队头的时间最小、队尾的时间最大。此时,如果队头的Message正是被delay的,则将当前线程堵塞一段时间,直到等待足够时间再唤醒执行该Message,否则唤醒后直接执行。
  • 线程

Q:Android中还了解哪些方便线程切换的类?

  • 技术点:线程通信
  • 参考回答:对Handler进一步的封装的几个类:
    • AsyncTask:底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作。
    • HandlerThread:一种具有消息循环的线程,其内部可使用Handler。
    • IntentService:是一种异步、会自动停止的服务,内部采用HandlerThread。
  • 引申:更多是对消息机制的理解

Q:AsyncTask相比Handler有什么优点?不足呢?

  • 技术点:AsyncTask、Handler
  • 参考回答:
    • Handler机制存在的问题:多任务同时执行时不易精确控制线程。
    • 引入AsyncTask的好处:创建异步任务更简单,直接继承它可方便实现后台异步任务的执行和进度的回调更新UI,而无需编写任务线程和Handler实例就能完成相同的任务。

Q:使用AsyncTask需要注意什么?

  • 技术点:AsyncTask
  • 参考回答:
    • 不要直接调用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()和onCancelled()方法
    • 一个异步对象只能调用一次execute()方法
  • 引申:谈谈AsyncTask初始化、五个核心方法如何配合进而体现Handler的作用

Q:AsyncTask中使用的线程池大小?

  • 技术点:AsyncTask
  • 参考回答:在AsyncTask内部实现有两个线程池:
    • SerialExecutor:用于任务的排队,默认是串行的线程池,在3.0以前核心线程数为5、线程池大小为128,而3.0以后变为同一时间只能处理一个任务
    • THREAD_POOL_EXECUTOR:用于真正执行任务。
  • 引申:谈谈对线程池的理解

Q:HandlerThread有什么特点?

  • 技术点:HandlerThread
  • 参考回答:HandlerThread是一个线程类,它继承自Thread。与普通Thread不同,HandlerThread具有消息循环的效果,这是因为它内部HandlerThread.run()方法中有Looper,能通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。

Q:快速实现子线程使用Handler

  • 技术点:HandlerThread
  • 思路:不同于之前手动在子线程创建Looper再构建Handler的想法,这里从HandlerThread角度去快速实现在子线程使用Handler
  • 参考回答:HandlerThread实现方法
    • 实例化一个HandlerThread对象,参数是该线程的名称;
    • 通过 HandlerThread.start()开启线程;
    • 实例化一个Handler并传入HandlerThread中的looper对象,使得与HandlerThread绑定;
    • 利用Handler即可执行异步任务;
    • 当不需要HandlerThread时,通过HandlerThread.quit()/quitSafely()方法来终止线程的执行。

Q:IntentService的特点?

  • 技术点:IntentService
  • 思路:和普通线程和普通Service比较突出其特点
  • 参考回答: 不同于线程,IntentService是服务,优先级比线程高,更不容易被系统杀死,因此较适合执行一些高优先级的后台任务;不同于普通Service,IntentService可自动创建子线程来执行任务,且任务执行完毕后自动退出

Q:为何不用bindService方式创建IntentService?

  • 技术点:IntentService
  • 思路:从底层实现出发
  • 参考回答:IntentService的工作原理是,在IntentService的onCreate()里会创建一个HandlerThread,并利用其内部的Looper实例化一个ServiceHandler对象;而这个ServiceHandler用于处理消息的handleMessage()方法会去调用IntentService的onHandleIntent(),这也是为什么可在该方法中处理后台任务的逻辑;当有Intent任务请求时会把Intent封装到Message,然后ServiceHandler会把消息发送出,而发送消息是在onStartCommand()完成的,只能通过startService()才可走该生命周期方法,因此不能通过bindService创建IntentService。

Q:线程池的好处、原理、类型?

  • 技术点:线程池
  • 参考回答:
  • (1)线程池的好处:
    • 重用线程池中的线程,避免线程的创建和销毁带来的性能消耗;
    • 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
    • 进行线程管理,提供定时/循环间隔执行等功能
  • (2)线程池的分类:
    • FixThreadPool:线程数量固定的线程池,所有线程都是核心线程,当线程空闲时不会被回收;能快速响应外界请求。
    • CachedThreadPool:线程数量不定的线程池(最大线程数为Integer.MAX_VALUE),只有非核心线程,空闲线程有超时机制,超时回收;适合于执行大量的耗时较少的任务
    • ScheduledThreadPool:核心线程数量固定,非核心线程数量不定;可进行定时任务和固定周期的任务。
    • SingleThreadExecutor:只有一个核心线程,可确保所有的任务都在同一个线程中按顺序执行;好处是无需处理线程同步问题。
  • (3)线程池的原理:实际上通过ThreadPoolExecutor并通过一系列参数来配置各种各样的线程池,具体的参数有:
    • corePoolSize核心线程数:一般会在线程中一直存活
    • maximumPoolSize最大线程数:当活动线程数达到这个数值后,后续的任务将会被阻塞
    • keepAliveTime非核心线程超时时间:超过这个时长,闲置的非核心线程就会被回收
    • unit:用于指定keepAliveTime参数的时间单位
    • workQueue任务队列:通过线程池的execute()方法提交的Runnable对象会存储在这个参数中。
    • threadFactory:线程工厂,可创建新线程
    • handler:在线程池无法执行新任务时进行调度
  • 引申:使用Executors各个方法创建线程池的弊端

Q:ThreadPoolExecutor的工作策略?

  • 技术点:线程池
  • 参考回答:ThreadPoolExecutor的默认工作策略
    • 若程池中的线程数量未达到核心线程数,则会直接启动一个核心线程执行任务。
    • 若线程池中的线程数量已达到或者超过核心线程数量,则任务会被插入到任务列表等待执行。
      • 若任务无法插入到任务列表中,往往由于任务列表已满,此时如果
        • 线程数量未达到线程池最大线程数,则会启动一个非核心线程执行任务;
        • 线程数量已达到线程池规定的最大值,则拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
  • 引申:ThreadPoolExecutor的拒绝策略

Q:什么是ANR?什么情况会出现ANR?如何避免?在不看代码的情况下如何快速定位出现ANR问题所在?

  • 技术点:ANR
  • 思路:
  • 参考回答:
    • ANR(Application Not Responding,应用无响应):当操作在一段时间内系统无法处理时,会在系统层面会弹出ANR对话框
    • 产生ANR可能是因为5s内无响应用户输入事件、10s内未结束BroadcastReceiver、20s内未结束Service
    • 想要避免ANR就不要在主线程做耗时操作,而是通过开子线程,方法比如继承Thread或实现Runnable接口、使用AsyncTask、IntentService、HandlerThread等
  • 引申:快读定位ANR方法:使用命令导出ANR日志,并分析关键信息,详见如何分析ANR
  • Bitmap

Q:加载图片的时候需要注意什么?

  • 技术点:Bitmap高效加载
  • 参考回答:
    • 直接加载大容量的高清Bitmap很容易出现显示不完整、内存溢出OOM的问题,所以最好按一定的采样率将图片缩小后再加载进来
    • 为减少流量消耗,可对图片采用内存缓存策略,又为了避免图片占用过多内存导致内存溢出,最好以软引用方式持有图片
    • 如果还需要网上下载图片,注意要开子线程去做下载的耗时操作

Q:LRU算法的原理?

  • 技术点:LRU算法
  • 参考回答:为减少流量消耗,可采用缓存策略。常用的缓存算法是LRU(Least Recently Used):
    • 核心思想:当缓存满时, 会优先淘汰那些近期最少使用的缓存对象。主要是两种方式:
      • LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用一个LinkedHashMap强引用的方式存储外界的缓存对象,并提供getput方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
      • DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果
  • 引申:感兴趣可了解具体实现算法
  • 性能优化

Q:项目中如何做性能优化的?

  • 技术点:性能优化实例
  • 思路:举例说明项目注意了哪些方面的性能优化,如布局优化、绘制优化、内存泄漏优化、 响应速度优化、列表优化、Bitmap优化、 线程优化......

Q:了解哪些性能优化的工具?

  • 技术点:性能优化工具
  • 思路:做项目时是否使用过的系统自带的性能优化工具?公司是否有自己的性能优化工具?实现原理怎样的?

Q:布局上如何优化?

  • 技术点:布局优化
  • 参考回答:布局优化的核心就是尽量减少布局文件的层级,常见的方式有:
    • 多嵌套情况下可使用RelativeLayout减少嵌套。
    • 布局层级相同的情况下使用LinearLayout,它比RelativeLayout更高效。
    • 使用<include>标签重用布局、<merge>标签减少层级、<ViewStub>标签懒加载。

Q:内存泄漏是什么?为什么会发生?常见哪些内存泄漏的例子?都是怎么解决的?

  • 技术点:内存泄漏
  • 参考回答:内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请的内存空间。简单地说,发生内存泄漏是由于长周期对象持有对短周期对象的引用,使得短周期对象不能被及时回收。常见的几个例子和解决办法:
    • 单例模式导致的内存泄漏:单例传入参数this来自Activity,使得持有对Activity的引用。
      • 解决办法:传参context.getApplicationContext()
    • Handler导致的内存泄漏:Message持有对Handler的引用,而非静态内部类的Handler又隐式持有对外部类Activity的引用,使得引用关系会保持至消息得到处理,从而阻止了Activity的回收。
      • 解决办法:使用静态内部类+WeakReference弱引用;当外部类结束生命周期时清空消息队列。
    • 线程导致的内存泄漏:AsyncTask/Runnable以匿名内部类的方式存在,会隐式持有对所在Activity的引用。
      • 解决办法:将AsyncTask和Runnable设为静态内部类或独立出来;在线程内部采用弱引用保存Context引用
    • 资源未关闭导致的内存泄漏:未及时注销资源导致内存泄漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
      • 解决办法:在Activity销毁的时候要及时关闭或者注销。
        • BraodcastReceiver:调用unregisterReceiver()注销;
        • Cursor,Stream、File:调用close()关闭;
        • 动画:在Activity.onDestroy()中调用Animator.cancel()停止动画
  • 引申:谈谈项目中是如何注意内存泄漏的问题

Q:内存泄漏和内存溢出的区别

  • 技术点:内存泄漏、内存溢出
  • 参考回答:
    • 内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请的内存空间。是造成应用程序OOM的主要原因之一。
    • 内存溢出(out of memory)是指程序在申请内存时,没有足够的内存空间供其使用。

Q:什么情况会导致内存溢出?

  • 技术点:内存溢出
  • 参考回答:内存泄漏是导致内存溢出的主要原因;直接加载大图片也易造成内存溢出
  • 引申:谈谈如何避免内存溢出(如何避免内存泄漏、避免直接加载大图片)
  • 开源框架(略)

  • 谷歌新动态

Q:是否了解和使用过谷歌推出的新技术?
Q:有了解刚发布的Androidx.0的特性吗?
Q:Kotlin对Java做了哪些优化?

  • 可能意图:了解候选者对谷歌&安卓的关注度、共同探讨对新技术的看法、学习主动性、平时学习习惯
  • 思路:谷歌的安卓官方网站(中文版):https://developer.android.google.cn,了解最新动态

1.2 Java

  • 基础

Q:面向对象编程的四大特性及其含义?

  • 技术点:面向对象编程特点
  • 思路:分条简述每个特性的含义
  • 参考回答:
    • 抽象:对现实世界的事物进行概括,抽象为在计算机虚拟世界中有意义的实体
    • 封装:将某事物的属性和行为包装到对象中,构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,并且尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系
    • 继承:子类继承父类,不仅可以有父类原有的方法和属性,也可以增加自己的或者重写父类的方法及属性
    • 多态:允许不同类的对象对同一消息做出各自的响应

Q:String、StringBuffer和StringBuilder的区别?

  • 技术点:String
  • 参考回答:
    • String是字符串常量,而StringBuffer、StringBuilder都是字符串变量,即String对象一创建后不可更改,而后两者的对象是可更改的:
    • StringBuffer是线程安全的,而StringBuilder是非线程安全的,这是由于StringBuffer对方法加了同步锁或者对调用的方法加了同步锁
    • String更适用于少量的字符串操作的情况,StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况,StringBuffer适用于多线程下在字符缓冲区进行大量操作的情况

Q:String a=""和String a=new String("")的的关系和异同?

  • 技术点:String
  • 参考回答:
    • 通过String a=""直接赋值的方式得到的是一个字符串常量,存在于常量池;注意,相同内容的字符串在常量池中只有一个,即如果池已包含内容相等的字符串会返回池中的字符串,反之会将该字符串放入池中
    • 通过new String("")创建的字符串不是常量是实例对象,会在堆内存开辟空间并存放数据,且每个实例对象都有自己的地址空间
  • 引申:对于用String a=""和String a=new String("")两种方式定义的字符串,判断使用equals()、"=="比较结果是什么

Q:Object的equal()和==的区别?

  • 技术点:equal()、==
  • 参考回答:
    • equals():是Object的公有方法,具体含义取决于如何重写,比如String的equals()比较的是两个字符串的内容是否相同
    • "==" :对于基本数据类型来说,比较的是两个变量值是够是否相等,对于引用类型来说,比较的是两个对象的内存地址是否相同
  • 引申:对于用String a=""和String a=new String("")两种方式定义的字符串,判断使用equals()、"=="比较结果是什么

Q:装箱、拆箱什么含义?

  • 技术点:装箱、拆箱
  • 参考回答:装箱就是自动将基本数据类型转换为包装器类型,拆箱就是自动将包装器类型转换为基本数据类型

Q:int和Integer的区别?

  • 技术点:基本数据类型、引用类型
  • 参考回答:
    • Integer是int的包装类,int则是java的一种基本数据类型
    • Integer变量必须实例化后才能使用,而int变量不需要
    • Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
    • Integer的默认值是null,int的默认值是0

Q:遇见过哪些运行时异常?异常处理机制知道哪些?

  • 技术点:Java异常机制
  • 思路:对Throwable异常进行分类说明每种异常的特点和常见问题,简述几种常见异常处理机制,详见Java基础之异常机制
  • 参考回答:
  • (1) Throwable继承层次结构,可见分成两大类Error和Exception:
    • Error(错误):指程序无法恢复的异常情况,表示运行应用程序中较严重的问题;发生于虚拟机自身、或者在虚拟机试图执行应用时,如Virtual MachineError(Java虚拟机运行错误)、NoClassDefFoundError(类定义错误);属于不可查异常,即不强制程序员必须处理,即使不处理也不会出现语法错误。
    • Exception(异常):指程序有可能恢复的异常情况,表示程序本身可以处理的异常。又分两大类:
      • RuntimeException(运行时异常):由程序自身的问题导致产生的异常;如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常);属于不可查异常。
      • 非运行时异常:由程序外部的问题引起的异常;除了RuntimeException以外的异常,如FileNotFoundException(文件不存在异常);属于可查异常,即强制程序员必须进行处理,如果不进行处理则会出现语法错误。
  • (2)常见的异常处理机制有:
    • 捕捉异常:由系统自动抛出异常,即try捕获异常->catch处理异常->finally 最终处理
    • 抛出异常:在方法中将异常对象显性地抛出,之后异常会沿着调用层次向上抛出,交由调用它的方法来处理。配合throws声明抛出的异常和throw抛出异常
    • 自定义异常:继承Execption类或其子类

Q:什么是反射,有什么作用和应用?

  • 技术点:反射
  • 思路:简述反射的定义、功能和应用,详见Java基础之泛型&反射
  • 参考回答:
    • 含义:在运行状态中,对于任意一个类都能知道它的所有属性和方法,对于任何一个对象都能够调用它的任何一个方法和属性。
    • 功能:动态性,体现在:在运行时判断任意一个类所具有的属性和方法; 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时调用任意一个对象的方法;生成动态代理
    • 应用:反射&泛型
  • 引申:是否在项目中使用过反射机制,有什么优缺点

Q:什么是内部类?有什么作用?静态内部类和非静态内部类的区别?

  • 技术点:内部类
  • 思路:
  • 参考回答:内部类就是定义在另外一个类里面的类。它隐藏在外部类中,封装性更强,不允许除外部类外的其他类访问它;但它可直接访问外部类的成员。静态内部类和非静态内部类的区别有:
    • 静态内部类是指被声明为static的内部类,可不依赖外部类实例化;而非静态内部类需要通过生成外部类来间接生成。
    • 静态内部类只能访问外部类的静态成员变量和静态方法,而非静态内部类由于持有对外部类的引用,可以访问外部类的所用成员
  • 引申:谈谈匿名内部类

Q:final、finally、finalize()分别表示什么含义

  • 技术点:final、finally、finalize()
  • 参考回答:
    • final关键字表示不可更改,具体体现在:
      • final修饰的变量必须要初始化,且赋初值后不能再重新赋值
      • final修饰的方法不能被子类重写
      • final修饰的类不能被继承
    • finally:和try、catch成套使用进行异常处理,无论是否捕获或处理异常,finally块里的语句都会被执行,在以下4种特殊情况下,finally块才不会被执行:
      • 在finally语句块中发生了异常
      • 在前面的代码中用了System.exit()退出程序
      • 程序所在的线程死亡
      • 关闭CPU
    • finalize():是Object中的方法,当垃圾回收器将回收对象从内存中清除出去之前会调用finalize(),但此时并不代表该回收对象一定会“死亡”,还有机会“逃脱”

Q:重写和重载的区别?

  • 技术点:重写、重载
  • 参考回答:重写表示子类重写父类的方法;重载表示有多个同名函数同时存在,区别在于有不同的参数个数或类型
  • 引申:谈谈动态分派和静态分派

Q:抽象类和接口的异同?

  • 技术点:抽象类、接口
  • 参考回答:
    • 使用上的区别:一个类只能继承一个抽象类却可以实现多个接口
    • 设计上的区别:接口是对行为的抽象,无需有子类的前提,是自上而下的设计理念;抽象类是对类的抽象,建立于相似子类之上,是自下而上的设计理念

Q:为什么匿名内部类中使用局部变量要用final修饰?

  • 技术点:匿名内部类
  • 参考回答:一方面,由于方法中的局部变量的生命周期很短,一旦方法结束变量就要被销毁,为了保证在内部类中能找到外部局部变量,通过final关键字可得到一个外部变量的引用;另一方面,通过final关键字也不会在内部类去做修改该变量的值,保护了数据的一致性。

Q:Object有哪些公有方法?

  • 技术点:Object
  • 思路:列举常见的几个公有方法
  • 参考回答:
    • equals(): 和==作用相似
    • hashCode():用于哈希查找,重写了equals()一般都要重写该方法
    • getClass(): 获取Class对象
    • wait():让当前线程进入等待状态,并释放它所持有的锁
    • notify()&notifyAll(): 唤醒一个(所有)正处于等待状态的线程
    • toString():转换成字符串
  • 引申:equals()和==的不同、在synchronized 同步代码块里wait()和notify()&notifyAll()如何配合、hashCode()和equals()的关系、获取Class对象还有什么方法
  • 集合

Q:Java集合框架中有哪些类?都有什么特点

  • 技术点:集合框架
  • 思路:分条解释每种类的特点
  • 参考回答:可将Java集合框架大致可分为Set、List、Queue 和Map四种体系
    • Set:代表无序、不可重复的集合,常见的类如HashSet、TreeSet
    • List:代表有序、可重复的集合,常见的类如动态数组ArrayList、双向链表LinkedList、可变数组Vector
    • Map:代表具有映射关系的集合,常见的类如HashMap、LinkedHashMap、TreeMap
    • Queue:代表一种队列集合

Q:集合、数组、泛型的关系,并比较

  • 技术点:集合、数组、泛型
  • 参考回答:
  • (1)集合和数组的区别:
    • 数组元素可以是基本类型,也可以是对象;数组长度限定;数组只能存储一种类型的数据元素
    • 集合元素只能是对象;集合长度可变;集合可存储不同种的数据元素
  • (2)泛型相比与集合的好处在于它安全简单。具体体现在提供编译时的强类型检查,而不用等到运行;可避免类类型强制转换

Q:ArrayList和LinkList的区别?

  • 技术点:List对比
  • 参考回答:
    • ArrayList的底层结构是数组,可用索引实现快速查找;是动态数组,相比于数组容量可实现动态增长
    • LinkedList底层结构是链表,增删速度快;是一个双向循环链表,也可以被当作堆栈、队列或双端队列

Q:ArrayList和Vector的区别?

  • 技术点:List对比
  • 参考回答:
    • ArrayList非线程安全,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList;默认初始容量为10,每次扩容为原来的1.5倍
    • Vector使用了synchronized关键字,是线程安全的,比ArrayList开销更大,访问更慢;默认初始容量为10,默认每次扩容为原来的2倍,可通过capacityIncrement属性设置

Q:HashSet和TreeSet的区别?

  • 技术点:Set对比
  • 参考回答:
    • HashSet不能保证元素的排列顺序;使用Hash算法来存储集合中的元素,有良好的存取和查找性能;通过equal()判断两个元素是否相等,并两个元素的hashCode()返回值也相等
    • TreeSet是SortedSet接口的实现类,根据元素实际值的大小进行排序;采用红黑树的数据结构来存储集合元素;支持两种排序方法:自然排序(默认情况)和定制排序。前者通过实现Comparable接口中的compareTo()比较两个元素之间大小关系,然后按升序排列;后者通过实现Comparator接口中的compare()比较两个元素之间大小关系,实现定制排列

Q:HashMap和Hashtable的区别?

  • 技术点:Map对比
  • 参考回答:
    • HashMap基于AbstractMap类,实现了Map、Cloneable(能被克隆)、Serializable(支持序列化)接口; 非线程安全;允许存在一个为null的key和任意个为null的value;采用链表散列的数据结构,即数组和链表的结合;初始容量为16,填充因子默认为0.75,扩容时是当前容量翻倍,即2capacity
    • Hashtable基于Map接口和Dictionary类;线程安全,开销比HashMap大,如果多线程访问一个Map对象,使用Hashtable更好;不允许使用null作为key和value;底层基于哈希表结构;初始容量为11,填充因子默认为0.75,扩容时是容量翻倍+1,即2capacity+1

Q:HashMap在put、get元素的过程?体现了什么数据结构?

  • 技术点:HashMap
  • 参考回答:
    • 向Hashmap中put元素时,首先判断key是否为空,为空则直接调用putForNullKey(),不为空则计算key的hash值得到该元素在数组中的下标值;如果数组在该位置处没有元素,就直接保存;如果有,还要比较是否存在相同的key,存在的话就覆盖原来key的value,否则将该元素保存在链头,先保存的在链尾。
    • 从Hashmap中get元素时,计算key的hash值找到在数组中的对应的下标值,返回该key对应的value即可,如果有冲突就遍历该位置链表寻找key相同的元素并返回对应的value
    • 由此可看出HashMap采用链表散列的数据结构,即数组和链表的结合,在Java8后又结合了红黑树,当链表元素超过8个将链表转换为红黑树

Q:如何解决Hash冲突?

  • 技术点:Hash冲突
  • 参考回答:
    • 开放定址法:常见的线性探测方式,在冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表
    • 链地址法:将有冲突数组位置生出链表
    • 建立公共溢出区:将哈希表分为基本表和溢出表两部分,和基本表发生冲突的元素一律填入溢出表
    • 再哈希法:构造多个不同的哈希函数,有冲突使用下一个哈希函数计算hash值

Q:如何保证HashMap线程安全?什么原理?

  • 技术点:ConcurrentHashMap
  • 思路:这里回答一种办法,使用ConcurrentHashMap
  • 参考回答:ConcurrentHashMap是线程安全的HashMap,它采取锁分段技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。在JDK1.8中对ConcurrentHashmap做了两个改进:
    • 取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,将数组元素作为锁,对每一行数据进行加锁,可减少并发冲突的概率
    • 数据结构由“数组+单向链表”变为“数组+单向链表+红黑树”,使得查询的时间复杂度可以降低到O(logN),改进一定的性能。
  • 引申:LinkHashMap线程安全的底层实现

Q:HashMap是有序的吗?如何实现有序?

  • 技术点:LinkHashMap
  • 思路:这里回答一种办法,使用LinkedHashMap
  • 参考回答:HashMap是无序的,而LinkedHashMap是有序的HashMap,默认为插入顺序,还可以是访问顺序,基本原理是其内部通过Entry维护了一个双向链表,负责维护Map的迭代顺序
  • 引申:LinkHashMap有序的底层实现

Q:HashMap是如何扩容的?如何避免扩容?

  • 技术点:HashMap
  • 参考回答:
    • HashMap几个默认值,初始容量为16、填充因子默认为0.75、扩容时容量翻倍。也就是说当HashMap中元素个数超过16*0.75=12时会把数组的大小扩展为2*16=32,然后重新计算每个元素在数组中的位置
    • 由于每次扩容还需要重新计算元素Hash值,损耗性能,所以建议在使用HashMap时,最好先估算Map的大小,设置初始值,避免频繁扩容

Q:hashcode()的作用,与equal()有什么区别?

  • 技术点:Hash值
  • 参考回答:hashCode()用于计算对象的Hash值,确认对象在散列存储结构中的存储地址。和equal()的区别:
    • equals()比较两个对象的地址值是否相等 ;hashCode()得到的是对象的存储位置,可能不同对象会得到相同值
    • 有两个对象,若equals()相等,则hashcode()一定相等;hashcode()不等,则equals()一定不相等;hashcode()相等,equals()可能相等、可能不等
    • 使用equals()比较两个对象是否相等效率较低,最快办法是先用hashCode()比较,如果hashCode()不相等,则这两个对象肯定不相等;如果hashCode()相等,此时再用equal()比较,如果equal()也相等,则这两个对象的确相等,反之
  • 并发

Q:同步和非同步、阻塞和非阻塞的概念

  • 技术点:同步、阻塞
  • 参考回答:
    • 同步和异步体现的是消息的通知机制:所谓同步,方法A调用方法B后必须等到方法B返回结果才能继续后面的操作;所谓异步,方法A调用方法B后可让方法B在调用结束后通过回调等方式通知方法A
    • 阻塞和非阻塞侧重于等待消息时的状态:所谓阻塞,就是在结果返回之前让当前线程挂起;所谓非阻塞,就是在等待时可做其他事情,通过轮询去询问是否已返回结果

Q:Thread的join()有什么作用?

  • 技术点:线程相关方法
  • 参考回答:Thread的join()的含义是等待该线程终止,即将挂起调用线程的执行,直到被调用的对象完成它的执行。比如存在两个线程t1和t2,下述代码表示先启动t1,直到t1的任务结束,才轮到t2启动。
t1.start();
t1.join(); 
t2.start();

Q:线程的有哪些状态?

  • 技术点:线程状态
  • 思路:可分条解释每种状态的特点以及如何转换。详见要点提炼| 理解JVM之内存模型&线程
  • 参考回答:在任意一个时间点,一个线程只能有且只有其中的一种状态:
    • 新建(New):线程创建后尚未启动
    • 运行(Runable):包括正在执行(Running)和等待着CPU为它分配执行时间(Ready)两种
    • 无限期等待(Waiting):该线程不会被分配CPU执行时间,要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期等待状态:
      • 没有设置Timeout参数的Object.wait()
      • 没有设置Timeout参数的Thread.join()
      • LockSupport.park()
    • 限期等待(Timed Waiting):该线程不会被分配CPU执行时间,但在一定时间后会被系统自动唤醒。以下方法会让线程进入限期等待状态:
      • Thread.sleep()
      • 设置了Timeout参数的Object.wai()
      • 设置了Timeout参数的Thread.join()
      • LockSupport.parkNanos()
      • LockSupport.parkUntil()
    • 阻塞(Blocked):线程被阻塞。和等待状态不同的是,阻塞状态表示在等待获取到一个排他锁,在另外一个线程放弃这个锁的时候发生;而等待状态表示在等待一段时间或者唤醒动作的发生,在程序等待进入同步区域的时候发生。
    • 结束(Terminated):线程已经结束执行

Q:什么是线程安全?保障线程安全有哪些手段?

  • 技术点:线程安全
  • 思路:详见要点提炼| 理解JVM之线程安全&锁优化
  • 参考回答:线程安全就是当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。保证线程安全可从多线程三特性出发:
    • 原子性(Atomicity):单个或多个操作是要么全部执行,要么都不执行
      • Lock:保证同时只有一个线程能拿到锁,并执行申请锁和释放锁的代码
      • synchronized:对线程加独占锁,被它修饰的类/方法/变量只允许一个线程访问
    • 可见性(Visibility):当一个线程修改了共享变量的值,其他线程能够立即得知这个修改
      • volatile:保证新值能立即同步到主内存,且每次使用前立即从主内存刷新;
      • synchronized:在释放锁之前会将工作内存新值更新到主存中
    • 有序性(Ordering):程序代码按照指令顺序执行
      • volatile: 本身就包含了禁止指令重排序的语义
      • synchronized:保证一个变量在同一个时刻只允许一条线程对其进行lock操作,使得持有同一个锁的两个同步块只能串行地进入

Q:ReentrantLock和synchronized的区别?

  • 技术点:线程安全(ReentrantLock、synchronized)
  • 思路:详见要点提炼| 理解JVM之线程安全&锁优化
  • 参考回答: ReentrantLock与synchronized的不同在于ReentrantLock:
    • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
    • 公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。而synchronized是非公平的,即在锁被释放时,任何一个等待锁的线程都有机会获得锁。ReentrantLock默认情况下也是非公平的,但可以通过带布尔值的构造函数改用公平锁。
    • 锁绑定多个条件:一个ReentrantLock对象可以通过多次调用newCondition()同时绑定多个Condition对象。而在synchronized中,锁对象wait()和notify()或notifyAl()只能实现一个隐含的条件,若要和多于一个的条件关联不得不额外地添加一个锁。

Q:synchronized和volatile的区别?

  • 技术点:线程安全(synchronized、volatile)
  • 参考回答:
    • synchronized能保证操作的原子性,而volatile不可以,假设线程A和线程B同时读取到变量a值,A修改a后将值更新到主内存,同时B也修改a值会覆盖A的修改操作
    • synchronized可修饰变量、方法和类,而volatile只能修饰变量
    • synchronized可能会造成线程阻塞,而volatile不会造成线程的阻塞

Q:synchronized同步代码块还有同步方法本质上锁住的是谁?为什么?

  • 技术点:线程安全(synchronized)
  • 参考回答:本质上锁住的是对象。在java虚拟机中,每个对象和类在逻辑上都和一个监视器相关联,synchronized本质上是对一个对象监视器的获取。当执行同步代码块或同步方法时,执行方法的线程必须先获得该对象的监视器,才能进入同步代码块或同步方法;而没有获取到的线程将会进入阻塞队列,直到成功获取对象监视器的线程执行结束并释放锁后,才会唤醒阻塞队列的线程,使其重新尝试对对象监视器的获取。

Q:sleep()和wait()的区别?

  • 技术点:sleep()、wait()
  • 参考回答:
    • sleep()来自Thread类;wait()来自Object类
    • sleep()用于线程控制自身流程;而wait()用于线程间通信,配合notify()/notifyAll()在同步代码块或同步方法里使用
    • sleep()的线程不会释放对象锁;wait()会释放对象锁进入等待状态,使得其他线程能使用同步代码块或同步方法
  • Java新动态

Q:是否了解Java1.x的特性吗?
Q:谈谈对面向过程编程、面向对象编程还有面向切面编程的理解

  • 可能意图:了解候选者对Java和其他语言的关注度和看法、学习主动性、平时学习习惯
  • 思路:Oracle技术网(Java):https://www.oracle.com/technetwork/cn/java/index.html 、开源中国:https://www.oschina.net ,了解最新动态



知识来源:minmin_1123
链接:https://www.jianshu.com/p/2dd855aa1938



转载于:https://www.cnblogs.com/loaderman/p/9761069.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/426648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java学习(24):if..else...if

/任务3&#xff1a;if… else if…. else if….else语句 编写控制台java程序&#xff0c;模拟根据有多少钱买车的功能。 使用Scanner对象相关方法从控制台接收用户输入有多少钱&#xff0c; 单位为万。如果输入的钱数量大于等于100万&#xff0c;则输出“可以买奔驰” &#xf…

java学习(25):三目运算符

/任务4&#xff1a;三目运算符 编写控制台java程序&#xff0c; 使用Scanner对象相关方法从控制台接收两个整数&#xff0c;比较他们的大小/ import java.util.Scanner; public class test04{ public static void main(String[] args){ Scanner in new Scanner(System.in); Sys…

java学习(26):switch

/扩展练习&#xff1a; 任务5&#xff1a;switch语句 编写控制台Java程序&#xff0c;使用Scanner对象相关方法接收用户输入的年份和月份&#xff0c; 输出这个月有多少天。(提示:闰年计算方法为&#xff1a;能被4整除单不能被100整除&#xff1b;或者能被400整除就是闰年。)/ …

java学习(27):巩固练习

/1 使用Eclipse编写控制台应用程, 使用while循环在控制台打印10行10列的如下图形 □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ …

java学习(28):switch

/2使用Eclipse编写控制台应用程, 使用while循环输入如下图形 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 6…

java学习(29):大神指导(巩固练习)

/3使用Eclipse编写控制台应用程&#xff0c;使用do while循环处理从控制台接收不定数量的学生英语成绩&#xff0c; 统计不及格(小于60分)的成绩个数&#xff0c;大于等于90分的优秀成绩数量&#xff0c; 计算所有成绩的总分、平均分并输出相关统计结果到控制台/ import java.u…

java学习(30):巩固练习

/4使用Eclipse编写控制台应用程&#xff0c;利用for循环打印一个等腰三角图形/ public class test04 { public static void main(String[] args){ int x; int y; int z; for(x1;x<4;x)//外循环控制行数&#xff0c;总共四行 { for (y1;y<4-x ;y ) {//打印小星星前面的空格…

java学习(31):for循环

/5使用Eclipse编写控制台应用程&#xff0c;利用for循环计算输入的两个整数的最大公约数和最小公倍数。/ import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner innew Scanner(System.in); System.out.println(“输入两个整数”);…

java学习(32):巩固练习

/6使用Eclipse编写控制台应用程&#xff0c;利用循环模仿一个用户的注册&#xff0c; 输入用户名和密码&#xff0c;判断用户输入的用户名是否长度位于4到16个字符之间&#xff0c; 如果不满足要求则应能够让用户重写输入&#xff0c;判断密码长度是否在6到16个字符之间 &#…

java学习(34):巩固练习

/1 使用Eclipse编写控制台应用程, 提示老师预计输入&#xff1f;个学生成绩&#xff0c;接收此数后&#xff0c;最为创建int类型数组的大小限制&#xff0c; 使用循环统计包含N个元素的数组&#xff0c;对包含的成绩进行统计&#xff0c;计算如下&#xff1a; 共接收成绩数量是…

artTemplate/template.js模板将时间戳格式化为正常的日期

1&#xff1a;引用<script type"text/javascript" src"../js/artTemplate/template.js"></script> 2&#xff1a;在正常渲染中引入方法 | timestamp: yyyy-MM-dd hh:mm:ss <div>开始时间<span>{{value.endTime | timestamp: yyyy-…

java学习(35):巩固练习

/2使用Eclipse编写控制台应用程,将给定的如下数组进行互换存储&#xff0c;互换原则如下所示 源整型数组 { 56,28,15,999,30,65,12,77} 互换后数组元素排列 {77, 12,15,999,30,65, 28, 56}/ public class test04 { public static void main(String[] args){ int[] arr{56,28,15…

单实例数据库和多实例数据库

java持续学习中&#xff0c;暂时说说工作上学到的知识 单实例数据库模式 单实例模式下,一个数据库只能通过一个实例进行访问 RAC(Real Application Clusters)集群模式下,共享数据库文件,一个数据库生成多个相同的实例被用户访问。

java list 从0开始_Java从零开始学二十一(集合List接口)

package com.pb.demo1;import java.util.LinkedList;import java.util.List;public class PersonLinkedListTest {public static void main(String[] args) {/** 创建多个Person对象并赋值*/Person p1 new Person("张三",21);Person p2 new Person("李四"…

oracle用户相关操作

我们主要学习数据库的一些基本操作&#xff0c;比如如何在数据库创建用户&#xff0c;授权&#xff0c;删除用户&#xff0c;回收权限&#xff0c;为用户加锁或者解锁等一些常用的操作。 首先&#xff0c;我们要知道数据库中创建用户的语句怎么写&#xff0c;看下面: 1.创建用户…

Oracle数据库操作

一、Oracle数据库操作 1、创建数据库 create database databasename 2、删除数据库 drop database dbname 3、备份数据库 • 完全备份 exp demo/demoorcl buffer1024 filed&#xff1a;\back.dmp fully demo&#xff1a;用户名、密码 buffer: 缓存大小 file: 具体的备份文件地址…

Django的各种初识

1,django项目的各个文件的介绍 1.1>项目的根目录:是各个子文件的根目录,在各个文件相互导入文件的时候使用 1.2>配置文件:为django的各个文件配置相关的各种默认配置 1.3>路径和函数的对应关系:是当form表单提交数据的时候和有关于网址的东西都会来urls来查找向对应的…

IIS介绍

IIS7及以上版本提供的请求-处理架构包括以下内容&#xff1a; • Windows Process Activation Service(WAS)可以让站点支持更多协议&#xff0c;不仅仅是HTTP和HTTPS • 可以通过增加或移除模块来自定义Web服务器引擎 • 集成IIS和ASP.NET请求-处理管线 IIS中的组件 IIS包含多个…

Linux卸载MariaDB

CentOS7.5 安装MySQL8.0.12 安装前&#xff1a;  先卸载 CentOS7.5默认安装的MariaDB&#xff0c;配置文件在/etc/my.cnf rpm -qa | grep mariadb 找到 使用 yum remove mariadb-libs-5.5.56-2.el7.x86_64 再次查找 rpm -qa | grep mariadb 同时,配置文件在/etc/my.cnf 也已经…

win10安装iis(亲测,工作需要)

• 点击“Windows”键进入“开始”菜单&#xff0c;点击“所有应用”&#xff0c;在所有应用菜单里点击“Windows系统”里的“控制面板” • 在控制面板对话框里点击“程序” • 在“程序”对话框里点击“启用或关闭Windows功能” • 在“Windows功能”对话框里选中“Interne…