Java知识点总结

Java知识点1、==和equals的区别基本类型比较==比较内容 equals比较地址值引用类型比较==比较地址值 equals比较地址值在String类中equals比较内容2、形参和实参的区别形参 是在定义函数名和函数体的时候使用的参数,用来接受调用该函数时传递的参数实参 是在调用函数时传递的参数特点: 形参只有在被调用时才分配内存单元,再调用结束时立即释放所分配的内存单元实参可以是常量 变量 函数等 必须有确定的值值传递:方法调用时,实参把他对应的值传递给对应的形参,方法中执行形参的改变而不影响实参引用传递: 传的是地址值,方法调用时 实参的引用被传递给方法中对应的形参 在方法中执行对形参的操作就是对实参的操作3、final的特点Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非 抽象类、非抽象类成员方法和变量。final类不能被继承,没有子类,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。final不能用于修饰构造方法。4、final、finally、finallize的区别final 最终的不可改变的 修饰符finally 在try chech 中出现 代表一定执行代码块finallize Object的方法 System类的gc方法在清理内存是调用他用于回收垃圾5、android有哪些新集合,有什么特点Api=23 ArraySet 集合 实现Collection和set集合升序 不可重复SparseArrayApi=19ArrayMap 集合 实现Map集合 final不可被继承键必须是引用类型底层是value数组 无序通过键的hashcode值找value- ArrayMap它不是一个适应大数据的数据结构,相比传统的HashMap速度要慢,因为查找方法是二分法,并且当你删除或者添加数据时,会对空间重新调整,在使用大量数据时,效率并不明显,低于50%。所以ArrayMap是牺牲了时间换区空间。在写手机app时,适时的使用ArrayMap,会给内存使用带来可观的提升。- ArraySet- SparseArray1,SparseArray的原理是二分检索法,也因此key的类型都是整型。2,(HashMap和SparseArray比较)当存储大量数据(起码上千个)的时候,优先选择HashMap。如果只有几百个,用哪个区别不大。如果数量不多,优先选择SparseArray。3,SparseArray有自己的垃圾回收机制。(当数量不是很多的时候,这个不必关心。)- SparseBooleanArray- SparseIntArraySparseBooleanArray和SparseIntArray,其实看名字也知道,它们跟SparseArray极其类似,只是存储类型加以限制了。SparseBooleanArray只能存储boolean值,而SparseIntArray只能存储integer类型的值。它们也同样实现了Cloneable接口,可以直接调用clone方法,也同样是以二分法为依据。- SparseLongArray6、新IO是什么,有什么特点新IO和传统IO都是用于进行输入/输出,相比于传统IO面向流的处理方式,新IO采用内存映射文件来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了,所以这种访问方式既方便又快得多。新IO中的两个核心对象:Channel(通道)和Buffer(缓冲)。Channel用于新IO的数据传输,相对于传统IO中的InputStream和OutputStream,Channel提供了一个map()方法用来将一段数据映射成为一块内存。也就是在新IO中,改变了传统IO面向流的处理而转向面向块的处理方式。Buffer是一个容器,它的本质是一个数组。发送到Channel的所有对象以及从Channel取出的所有数据先存放到Buffer中。除了上面两个核心对象外,新IO还提供了Charset类用于将Unicode字符串映射成字节序列以及逆映射操作。以及Selector类用于支持非阻塞式输入输出。java.nio.ByteBuffer字节的Bufferjava.nio.CharBuffer字符的Bufferjava.nio.ShortBuffer短整数的Bufferjava.nio.IntBuffer整数的Bufferjava.nio.LongBUffer长整数的Bufferjava.nio.FloatBuffer单精度的Bufferjava.nio.DoubleBuffer双精度的Bufferallocate(int allocate)设置缓冲区大小put(int[] value)添加元素flip()重设缓冲区7、TreeSet是如何进行排序的?自然排序:要在自定义类中实现Comparerable接口 ,并且重写compareTo方法比较器排序:在自定义类中实现Comparetor接口,重写compare方法8、什么是线程线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。9、线程都有哪些状态?新建->以创建 还未调用start方法就绪->调用start方法 还未抢占到cpu的执行权执行->抢占到cpu的执行权执行代码阻塞->执行过程中得其他线程 抢占了cpu死亡->线程执行完毕10、什么是线程池? 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程 。线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。11、线程和进程的区别线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:(1)地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源(3)线程是处理器调度的基本单位,但进程不是. (4)二者均可并发执行.进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于:简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.12、如何在Java中实现线程?1、继承Thread类实现多线程继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。2、实现Runnable接口方式实现多线程如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口3、使用ExecutorService、Callable、Future实现有返回结果的多线程ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。 13、Thread 类中的 start() 和 run() 方法有什么区别?调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void.。14、线程池的最大线程数是多少?答: 不同的任务类别应采用不同规模的线程池,任务类别可划分为CPU密集型任务|密集型任务和混合型任务。[N代表CPU个数]对于CPU密集型任务: 线程池中线程个数应尽量少,如配置M+1个线程的线程池;对于IO密集型任务: 由于IO操作速度远低于CPU速度,那么在运行这类任务时,CPU绝大多数时间处于空闲状态,那么线程池可以配置尽量多些的线程,以提高CPU利用率,如2*N;对于混合型任务: 可以拆分为CPU密集型任务和IO密集型任务,当这两类任务执行时间相差无几时,通过拆分再执行的吞吐率高于串行执行的吞吐率,但若这两类任务执行时间有数据级的差距,那么没有拆分的意义。15、HandlerThread是什么?HandlerThread = Handler + Thread + LooperHandlerThread是一个内部有Looper的Thread1)HandlerThread本质上是一个线程类,它继承了Thread。2)HandlerThread有自己的内部Looper对象,可以进行looper循环。3)通过获取HandlerThread的Looper对象传递给Handler对象,可以在handleMessae方法中执行异步任务。4)优点是不会有堵塞,减少了对性能的消耗。5)缺点是不能同时进行多任务处理,需要进行等待,处理效率较低。6)与线程池侧重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。16、HandlerThread 的原理HandlerThread继承自Thread,因此在run()中的逻辑都是在子线程中运行的。接下来就是两个关键的方法,run()和getLooper(): run()中可以看到是很简单的创建Looper以及让Looper工作的逻辑。 run()里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的。 也就是说,我们必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。17、用 Runnable 还是 Thread ?实现Runnable接口比继承Thread类所具有的优势:1):适合多个相同的程序代码的线程去处理同一个资源2):可以避免java中的单继承的限制3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立18、Runnable 和 Callable 有什么不同?在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被 ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的(task) 方法来执行,并且返回一个,是表示任务等待完成的 Future。public interface Callable返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。Callable 接口类似于,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。类包含一些从其他普通形式转换成 Callable 类的实用方法。Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。19、使用多线程的优缺点优点(1)多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态;(2)当前没有进行处理的任务时可以将处理器时间让给其它任务;(3)占用大量处理时间的任务可以定期将处理器时间让给其它任务;(4)可以随时停止任务;(5)可以分别设置各个任务的优先级以优化性能。缺点(1)等候使用共享资源时造成程序的运行速度变慢。(2)对线程进行管理要求额外的 CPU开销。(3)线程的死锁。(4)对公有变量的同时读或写。20、wait()、notify()、notifyAll()的区别Object类里的方法,可以用来控制线程的状态。这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。21、wait() 与 Thread.sleep(long time) 的区别wait()方法睡眠没有其他线程唤醒他就不会重新启动释放同步锁不需要捕捉异常Thread.sleep(long time) 方法睡眠 会指定睡眠时间 自动唤醒不释放同步锁需要捕捉异常22、volatile关键字的作用操作数据对其他线程可见禁止重排序保证有序性23、join方法的作用当前线程调用其他线程的join(加入执行)当前线程会等待加入的线程执行完毕继续执行24、yield方法的作用Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。25、线程池的优点1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。26、有几种线程池,分别是什么?Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。27、如何关闭线程池?方法定义:public void shutdown()(1)线程池的状态变成SHUTDOWN状态,此时不能再往线程池中添加新的任务,否则会抛出RejectedExecutionException异常。(2)线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。 注意这个函数不会等待提交的任务执行完成,要想等待全部任务完成,可以调用:public boolean awaitTermination(longtimeout, TimeUnit unit)方法定义:public ListshutdownNow()

(1)线程池的状态立刻变成STOP状态,此时不能再往线程池中添加新的任务。

(2)终止等待执行的线程,并返回它们的列表;

(3)试图停止所有正在执行的线程,试图终止的方法是调用Thread.interrupt(),但是大家知道,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。

28、如何关闭线程?

29、1、 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

30、2、 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

31、3、 使用interrupt方法中断线程。

32、4、 抛出异常

33、什么是 Executor 框架?

Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。

30、Executors 类是什么?

Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。

Executors可以用于方便的创建线程池。

31、Handler原理|如何进行线程间通信?

当创建Handler时将通过ThreadLocal在当前线程绑定一个Looper对象,而Looper持有MessageQueue对象。执行Handler.sendMessage(Message)方法将一个待处理的Message插入到MessageQueue中,这时候通过Looper.loop()方法获取到队列中Message,然后再交由Handler.handleMessage(Message)来处理。

32、String、StringBuffer、StringBuilder的区别

1.可变与不可变

String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字

符串,如下就是,可知这两种对象都是可变的。

2.是否多线程安全

String中的对象是不可变的,也就可以理解为常量,显然线程安全。

StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的

StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

33、冒泡排序、选择排序、二叉树排序

冒泡排序:

相邻的两个数比较,将小数放在前面,大数放在后面

选择排序:

每一从待排序的数据元素中选出最小的一个元素,存放在序列的起始位置,依次比较,直到全部待排序的数据元素排完。

二叉树排序:

左子树不为空,则左子树上所有节点的值均小于它的根节点的值。

右子树不为空,则右子树上所有节点的值均大于它的根节点的值。

34、异常的继承体系

35、介绍封装、继承、多态

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 继承现有类 + 扩展

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

36、类与接口的区别

抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板。

接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。

37、接口能否写方法体?

可以,java8的接口新特性中可以写默认方法

38、集合子类的特点

Collection

|-->List:有序,可重复,有索引

|-->ArrayList:底层是数组数据结构(具有连续性,查询元素速度快但是增删元素速度低),是线程不同步的

|-->LinkList:底层是链表数据结构(查询的速度稍慢,但是增删的速度快),是线程不同步的

|-->Vector:底层是数组数据结构,是线程同步的,但是已经被ArrayList替代。因为增删和查询的速度都非常慢,效率很低。

|-->Set:无序,不可以重复元素。Set接口的方法和Collection中的方法一致。Set接口中的方法只有迭代器。

|-->HashSet:底层数据结构式哈希表,哈希表这种结构,其实就是对哈希值的存储。而且每个对象都有自己的哈希值,因为Object类中有一个方法为hashCode方法。

如何保证元素的唯一性呢?

通过判断元素的hashCode方法,会判断一次equals方法的返回值是否为true。

如果hashCode值不相同,就确定元素的哈希表中的位置,就不用在判断equals了。

在哈希表中有一个桶结构,每一个桶都有一个哈希值,当哈希值相同,但是equals返回为false时,这些元素都存放在一个桶内。

|-->TreeSet:可以对Set集合中的元素进行排序。

看到array就要想到数组,就要想到角标,就要想到查询很快。

看到link就要想到链表,就要想到增删很快,最好再想到addFirst(offerFirst)

看到hash就要想到hash表,就要想到元素的hashCode方法,和equals方法。

看到tree,就要想到二叉树,就要想到排序,就要想到两个接口 Comparable(Object obj)和Comparator(Objcet o1,Object o2)

Map集合中常见的三个子接口:

Map

|--Hashtable :底层数据结构是是哈希表结构,该集合是线程同步的,效率低

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以 用作键或值用作键的对象必须实现hashCode 方法和 equals 方法

|--HashMap: 底层也是哈希表数据结构,并允许使用 null 值和 null 键,此集合线程是不同步  效率高

|--TreeMap:  底层是二叉树数据结构,线程不同步,可以用于给Map集合中的键进行排序

不难发现,Set集合的特点和Map集合的特点非常相似,其实,Set底层就是使用了Map集合,它的很多方法都是调用Map集合的。

39、List集合有几种排序方式,Set集合有几种排序方式,Map集合有几种排序方式?

40、HashMap如何保证key的唯一性?

添加的元素都有一个 hashCode(哈希值),他们先比较哈希值,是否相同? 不相同的元素,添加进入 HashTable. 如果hashCode相同的话, 再去比较 equals()方法,如果也相同的话,JVM就认为数据已经存在了,就不会添加数据!

41、介绍OSI参考模型和TCP/IP参考模型

OSI模型

TCP/ip模型

OSI 7层

应用层->包含大量人们普遍需要的协议

表示层->用于完成某些特定功能

会话层->允许不同机器上的用户之间建立会话关系

传输层->实现网络中不同主机上的用户进程之间可靠的数据通信

网络层->完成网络中主机间的报文传输

数据链路层->如何在不可靠的物理线路上进行数据的可靠传输

物理层->完成相邻结点之间原始比特流的传输

TCP/IP 4层

应用层->处理高层协议

传输层->在源结点和目的结点的两个进程实体之间提供可靠的,端到端的数据传输

互连网层->处理上层发送请求,处理输入数据报,处理ICMP报文

网络接口层->涉及分组与网络接口

42、如何解决并发线程的数据安全?

谈到多线程,就涉及到共享数据安全问题,一般来说,解决共享数据安全问题有三种方式

①:在共享数据前加volatile修饰,且保证对共享数据的操作要是原子性的

②:java.util.concurrent.atomic下的AtomicInteger等类来包装共享数据

③:锁如静态锁synchronize、重入锁ReentrantLock等

43、序列化机制和原理

对象序列化,将对象以二进制的形式保存在硬盘上

反序列化;将二进制的文件转化为对象读取

44、java如何进行序列化和反序列化?

? 序列化实现步骤:

(1)需要序列化的对象所属类必须实现Serializable接口;

(2)构造FileOutputStream对象;

(3)构造ObjectOutputStream对象;

(4)使用ObjectOutputStream对象的writeObject()方法进行序列化;

(5)关闭ObjectOutputStream对象;

(6)关闭FileOutputStream对象;

(7)对序列化全程捕获IOException;

? 反序列化实现步骤:

(1)需要序列化的对象所属类必须实现Serializable接口;

(2)构造FileInputStream对象;

(3)构造ObjectInputStream对象;

(4)使用ObjectInputStream对象的readObject()方法进行序列化;

(5)关闭ObjectInputStream对象;

(6)关闭FileInputStream对象;

(7)对序列化全程捕获ClassNotFoundException和IOException;

45、Http和Https的区别

1、https协议需要到ca申请证书,一般免费证书比较少,因而需要一定的费用

2、http是超文本传输协议,信息是文明传输,https则是具有安全性的ssl加密传输协议

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80 后者是

440

4、https的连接很简单,是无状态的,https协议是由SSL+HTTP协议构建安的可进行

加密传输,身份认证的网络协议,比http安全

46、TCP和UDP的区别

连接方面:UDP不需要与服务器建立连接,TCP需要与服务器建立连接,通过三次握手完成数据传递。

数据限制方面:UDP每个数据限制在64k,TCP没有限制。

速度与安全方面:UDP速度快,但是不安全,TCP速度稍慢,是可靠协议,安全。

47、如何获取IP地址信息?

一般获取客户端的IP地址的方法是:request.getRemoteAddr();但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了。 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值 多次反向代理后会有多个ip值,第一个ip才是真实ip

48、Socket如何保持长连接

方法1:应用层自己实现的心跳包

由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用。

方法2:TCP的KeepAlive保活机制

因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心跳包,代码较多 且稍显复杂,而利用TCP/IP协议层为内置的KeepAlive功能来实现心跳功能则简单得多。 不论是服务端还是客户端,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包, 而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。但是

开启KeepAlive功能需要消耗额外的宽带和流量,所以TCP协议层默认并不开启KeepAlive功 能

KeepAlive设置不合理时可能会 因为短暂的网络波动而断开健康的TCP连接

49、Socket、TCP/IP、HTTP的区别

1、TCP/IP连接

TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。

在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。

在传输层中有TCP协议与UDP协议。

在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。

因此,HTTP本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。

手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。

建立起一个TCP连接需要经过“三次握手”:

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开).

2、HTTP连接

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。

2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

3、SOCKET原理

3.1套接字(socket)概念

套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

3.2 建立socket连接

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

4、SOCKET连接与TCP/IP连接

创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。

5、Socket连接与HTTP连接

由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。

而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。

很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。

http协议是应用层的协义

一个是发动机(Socket),提供了网络通信的能力

一个是轿车(Http),提供了具体的方式

两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现`是以不同的应用层协议来定义的`如HTTP`FTP`...

socket是对端口通信开发的工具,它要更底层一些 .

50、Socket如何实现流量控制和拥塞控制的实现机制

1. 利用滑动窗口实现流量控制

如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收

利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。

设A向B发送数据。在连接建立时,B告诉了A:“我的接收窗口是 rwnd = 400 ”(这里的 rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给 出的接收窗口的数值。

TCP的拥塞控制

拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。

几种拥塞控制方法

慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。

51、多线程如何同步

1.同步方法

即有synchronized关键字修饰的方法。

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻

塞状态。

2.同步代码块

即有synchronized关键字修饰的语句块。

被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

3.使用特殊域变量(volatile)实现线程同步

a.volatile关键字为域变量的访问提供了一种免锁机制,

b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,

c.因此每次使用该域就要重新计算,而不是使用寄存器中的值

d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

4.使用重入锁实现线程同步

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。

ReentrantLock类是可重入、互斥、实现了Lock接口的锁,

它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其

能力

ReenreantLock类的常用方法有:

ReentrantLock() : 创建一个ReentrantLock实例

lock() : 获得锁

unlock() : 释放锁

5. 使用局部变量实现线程同步

ThreadLocal与同步机制

ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题

前者采用以”空间换时间”的方法,后者采用以”时间换空间”的方式

52、tcp连接建立的时候3次握手的具体过程

第一次握手:

建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

SYN:同步序列编号(Synchronize Sequence Numbers)

第二次握手:

服务器收到syn包,必须确认客户的SYN(ack=j+1),

同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:

客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),

此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手.

完成三次握手,客户端与服务器开始传送数据

53、tcp断开连接的具体过程

(客户机发起断开请求)

四次断开是由于tcp的半关闭造成的,即一方已经断开连接二另一方没有断开

1.客户机发送一个带有fin的包给服务器,然后进入fin_wait_1状态.而此时服务器处于close_wait状态

2.服务器收到后,回应一个ack到客户机.此时客户机进入fin_wait_2状态

3.服务器继续回应一个Fin的包,服务器进入last_ack状态,而客户机进入time_wait状态

4.客户机发送ack给服务器,客户机进入cloesed状态.

最终双方连接终止.

54、socket关闭释放网络资源的时候,数据还未传输完毕,如何解决?

解决方案一:

传一个文件,建立一个连接,传完文件后,立即关闭连接。

解决方案二:

在传一个文件之前,先传一个文件长度过去,再传文件的数据,传完了关闭连接。

接收端,先接收文件长度,然后,按照文件长度来读取数据,

当,文件长度读取完毕后,等待下次传递

55、什么是请求队列?

消息队列跟队列本质上是一个东西,因为功能不一样所以名字有区别,首先队列是一

种数据结构,特点就是先进先出,后今 后出,这跟堆栈刚好相反。最简单的消息队

列其实也是用队列这种数据结构实现的。我们把消息按照先进先出的规则一个一个

的放进队列里,这个过程叫做进队,然后在队头一个一个取出来叫做出队。

56、常用的HTTP方法有哪些?

57、HTTP请求报文与响应报文格式

HTTP请求报文与响应报文格式

请求报文包含三部分:

a、请求行:包含请求方法、URI、HTTP版本信息

b、请求首部字段

c、请求内容实体

响应报文包含三部分:

a、状态行:包含HTTP版本、状态码、状态码的原因短语

b、响应首部字段

c、响应内容实体

58、常见的HTTP相应状态码

200:请求被正常处理

204:请求被受理但没有资源可以返回

206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,

相应报文中通过Content-Range指定范围的资源。

301:永久性重定向

302:临时重定向

303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通

过GET方法重定向到另一个URI上

304:发送附带条件的请求时,条件不满足时返回,与重定向无关

307:临时重定向,与302类似,只是强制要求使用POST方法

400:请求报文语法有误,服务器无法识别

401:请求需要认证

403:请求的对应资源禁止被访问

404:服务器无法找到对应资源

500:服务器内部错误

503:服务器正忙

59、HTTP1.1版本新特性

默认持久连接,

节省通信量,只要客户端和服务端任意一端没有明确的断开TCP连接,就可以发送多次HTTP请求

管线化

客户端可以同时发送多个HTTP请求,而不用一个个等待响应

断点续传原理

其原理是:客户端记录下当前的下载进度,并在需要续传时通知服务器本次需要下载的内容片断

60、常见HTTP首部字段

a、通用首部字段(请求报文与响应报文都会使用的首部字段)

Date:创建报文时间

Connection:连接的管理

Cache-Control:缓存的控制

Transfer-Encoding:报文主体的传输编码方式

b、请求首部字段(请求报文会使用的首部字段)

Host:请求资源所在服务器

Accept:可处理的媒体类型

Accept-Charset:可接收的字符集

Accept-Encoding:可接受的内容编码

Accept-Language:可接受的自然语言

c、响应首部字段(响应报文会使用的首部字段)

Accept-Ranges:可接受的字节范围

Location:令客户端重新定向到的URI

Server:HTTP服务器的安装信息

d、实体首部字段(请求报文与响应报文的的实体部分使用的首部字段)

Allow:资源可支持的HTTP方法

Content-Type:实体主类的类型

Content-Encoding:实体主体适用的编码方式

Content-Language:实体主体的自然语言

Content-Length:实体主体的的字节数

Content-Range:实体主体的位置范围,一般用于发出部分请求时使用

61、Http与Https优缺点?

https:// 要比http://更加安全一些,也就是说HTTPS协议是由SSL+HTTP协议构

建的可进行加密传输、身份认证的网络协议要比http协议安全。

1、https协议需要到ca申请证书,一般免费证书比较少,因而需要一定的费用

2、http是超文本传输协议,信息是文明传输,https则是具有安全性的ssl加密传

输协议

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80

后者是440

4、https的连接很简单,是无状态的,https协议是由SSL+HTTP协议构建安的

可进行加密传输,身份认证的网络协议,比http安全

62、什么是Http协议?

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。

63、说一下Http协议中302状态

302重定向很容易被搜索引擎误认为是利用多个域名指向同一网站,那么你的网站就会被封掉,罪名是“利用重复的内容来干扰Google搜索结果的网站排名”。因为302重定向经常被用于做url劫持,黑帽seo技术中,而且百度在处理302重定向技术还不成熟,经常将它纳入到黑帽seo的范畴中,而google对这方面识别处理就完善了许多。所以302重定向在现阶段的搜索引擎技术中,还是容易导致网站降权的,尽量不用。但从seo、网站优化方面来说是弊大于利

64、Http协议有什么组成?

HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URL、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,响应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。

65、Http协议中Http1.0与1.1区别?

1,HTTP/1.0协议使用非持久连接,即在非持久连接下,一个tcp连接只传输一个Web对象,;

2,HTTP/1.1默认使用持久连接(然而,HTTP/1.1协议的客户机和服务器可以配置成使用非持久连接)。

在持久连接下,不必为每个Web对象的传送建立一个新的连接,一个连接中可以传输多个对象!

66、Http优化

利用负载均衡优化和加速HTTP应用

利用HTTP Cache来优化网站

67、Http协议有那些特征?

无连接:HTTP本身是无连接的,即交换HTTP报文前不需要建立HTTP连接

无状态:HTTP协议是无状态的:数据传输过程中,并不保存任何历史信息和状态信息。无状态特性简化了服务器的设计,使服务器更容易支持大量并发的HTPP请求。

传输可靠性高:采用TCP作为运输层协议(面向连接、可靠传输),即交换报文时需要预先建立TCP连接

兼容性好:支持B/S模式及C/S模式;

简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST

灵活:HTTP 允许传输任意类型的数据对象

68、什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别

Bit最小的二进制单位 ,是计算机的操作部分 取值0或者1

Byte是计算机操作数据的最小单位由8位bit组成 取值(-128-127)

Char是用户的可读写的最小单位,在java里面由16位bit组成 取值(0-65535)

Bit 是最小单位 计算机 只能认识 0或者1

8个字节 是给计算机看的

字符 是看到的东西 一个字符=二个字节

69、什么是流,按照传输的单位,分成哪两种流,并且他们的父类叫什么

流是指数据的传输

字节流,字符流

字节流:InputStream OutputStream

字符流:Reader Writer

3.流按照传输的方向可以分为哪两种,分别举例说明

答案

输入输出相对于程序

输入流InputStream

,输出流OutputStream

70、BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法

属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法

71、什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征

节点流 直接与数据源相连,用于输入或者输出

处理流:在节点流的基础上对之进行加工,进行一些功能的扩展

处理流的构造器必须要 传入节点流的子类

72、如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么

Printwriter 可以打印各种数据类型

73、怎么样把我们控制台的输出改成输出到一个文件里面,这个技术叫什么

SetOut(printWriter,printStream)重定向

74、怎么样把输出字节流转换成输出字符流,说出它的步骤

使用 转换处理流OutputStreamWriter 可以将字节流转为字符流

New OutputStreamWriter(new FileOutputStream(File file));

75、把包括基本类型在内的数据和字符串按顺序输出到数据源,或者按照顺序从数据源读入,一般用哪两个流

DataInputStream DataOutputStream

76、把一个对象写入数据源或者从一个数据源读出来,用哪两个流

ObjectInputStream ObjectOutputStream

77、如果在对象序列化的时候不想给一个字段的数据保存在硬盘上面,采用那个关键字?

transient(短暂)关键字

78、在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做版本标识,一般有什么用

简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

79、InputStream里的read()返回的是什么,read(byte[] data)是什么意思,返回的是什么值

返回的是所读取的字节的int型(范围0-255)

read(byte [ ] data)将读取的字节储存在这个数组

返回的就是传入数组参数个数

Read 字节读取字节 字符读取字符

80、OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

write将指定字节传入数据源

Byte b[ ]是byte数组

b[off]是传入的第一个字符

b[off+len-1]是传入的最后的一个字符

len是实际长度

81、流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?

流一旦打开就必须关闭,使用close方法

放入finally语句块中(finally 语句一定会执行)

调用的是处理流就关闭处理流

多个流互相调用只关闭最外层的流

82、说说你对io流的理解

Io流主要是用来处理输入输出问题,常用的io流有InputStream,

OutputStream,Reader,Writer等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容