[Java程序员面试宝典]读书笔记
- ClassLoader具备层次关系,且不止一种。不同的类装载器分布创建的同一个类的字节码数据属于完全不同的对象,没有任何关联
- 通过Class.forName(String className),能够动态加载一个类
- Java程序不是一个可执行文件,而是由许多独立的类文件组成的,每一个文件对应一个Java类。此外,这些类文件并非全部都装入内存,而是根据程序需要逐渐载入。
- ClassLoader是JVM实现的一部分,ClassLoader包括bootstrap classloader(启动类加载器),ClassLoader在JVM运行的时候加载Java核心的API,以满足Java程序最基本的需求,其中就包括用户定义的ClassLoader。这里所谓的用户定义,是指通过Java程序实现的两个ClassLoader:一个是ExtClassLoader,它的作用是用来加载Java的扩展API,也就是/lib/ext中的类;第二个是AppClassLoader,它是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载
- ClassLoader加载流程:当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
- 在Java中字符只以一种形式存在,那就是Unicode(在JVM中、在内存中、在你的代码里声明的每一个char、String类型的变量中)
- I++和++i使用的不同点在于一个是程序完毕后自增,一个是程序开始前自增
- Java的包装类就是可以直接将简单类型的变量表示为一个类。Java共有6个包装类,分别是Boolean、Character、Integer、Long、Float和Double。String和Date本身就是类,不存在包装类的概念
- 在进行简单数据类型之间的转换(自动转换或强制转换)时,可以利用包装类进行中间过渡。一般情况下,首先声明一个变量,然后生成一个对应的包装类,就可以利用包装类的各种方法进行类型转换了。
- 将字符型变量转换为数值型变量实际上有两种对应关系:一种是将其转换成对应的ASCII码,另一种是转换关系,可以使用Character的getNumericValue(char ch)方法
- Assert是断言,断言是一个包含布尔表达式的语句,在执行这个语句时,假定该表达式为true,如果表达式计算为false,那么系统会报告一个Assertionerror。它用于调试目的。
- Main方法必须是public的
- &、|逻辑运算与&&、||运算的重要区别是:前者是非短路运算,后者是短路运算。编译器对于&&和||已经优化过,凡&&前面的是false,那么&&后面的表达式就不用再做了。||前面是true,||后面的也就不用做了。无论运算符&,|前面的是true或false,运算符后面的表达式都得继续进行运算,这就是所谓的非短路
- 在Java运行时,常常会出现一些非正常的现象,这种情况称为运行错误。根据其性质可分为错误和异常。Java程序中,所有抛出(throw)的异常都必须从Throwable派生而来。类Throwable有两个直接子类:Error和Exception
- Error类对象由Java虚拟机生成并抛弃(通常Java程序不对这类异常进行处理)
- 异常是程序执行时遇到的非正常情况或意外行为。比如数组下标越界、除数为0,这种情况不像错误类那样,程序运行时本身可以解决,由异常代码调整程序运行方向,使程序仍可继续运行,直至正常结束
- Java异常对应的类为Exception类。Exception类对象是Java程序处理或抛弃的对象,它有各种不同的子类分别对应于不同类型的异常。Java编译器要求程序必须捕获或声明所有的非运行时异常,但对运行时异常可以不做处理。
- Try,catch,finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面
- 若有多个catch块,只会匹配其中一个异常类并指向catch块代码,而不会再执行别的catch块,并且匹配catch语句的顺序是由上到下
- Throw关键字用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常,则还应该在头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。如果所有的方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈消息。如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常
- Throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出的某些异常。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出
- 探探final,finally,finallize的区别
22.1 final可以用于控制成员、方法,或者是一个类是否可被覆写或继承等功能。当在类中定义变量时,如果加上final,就意味着这个变量一旦被初始化,便不可改变,这里不可改变的意思是对基本类型来说是其值不可变,而对于对象变量来说是其引用不可变。其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。
22.2 还有一种用法是定义方法中的参数为final。对象变量在传递时是传递其引用的。当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
22.3 final方法,不允许任何从此类继承的类来覆写这个方法,但是仍然可以继承这个方法
22.4 final类是无法被任何人继承的。对于fina类中的成员,可以定义为final,也可以不是final。而对于方法,全部都是final型的。
- Finally结构使代码总会执行,而不管有无异常发生
- 根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。通常,finalize用于一些不容易控制,并且非常重要的资源的释放
- 如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能既被声明为abstract,又被声明为final。将变量或方法声明为final,可以保证它们在使用中不被改变。其初始化可以在两个地方:一是其定义处,即在final变量定义时直接给其赋值;二是在构造函数中。被声明为final的方法只能使用,不能重写(override)
- 在异常处理时提供finally块来执行任何清除操作。任何抛出一个异常,那么相匹配的catch子句就会执行,然后控制就会进入finally块(如果有的话)
- Finalize是方法名。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,它是在Object类中定义的,因此,所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。Finalize()方法是在垃圾收集器删除对象之前对这个对象进行调用的
- Try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会执行,在return前执行(一个函数return后就会被销毁)
- 反射主要是指程序可以访问、检测盒修改它本身的状态或行为的一种能力。Java中的反射是一种强大的工具,它能够创建灵活的代码,这些代码可以在运行时装配,无须在组件之间进行链接。反射允许在编写和执行时,使程序代码能够接入装载到JVM中的类的内部信息,而不是源代码中选定的类协作的代码。
- Java中的类反射Reflection是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性。
- 不管Java的参数的类型是什么,一律传递参数的副本
- When you are passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference.[如果Java是传值,那么传递的是值的副本;如果Java是传引用,那么传递的是引用的副本]
- String类型也是对象型变量,它也是传引用副本。
- 对基本类型而言,传值就是把自己复制一份传递,即使自己的副本变了,自己也不变。而对于对象类型而言,它传的是引用副本指向自己的地址,而不是自己实际值的副本。
- 对象类型是放在堆里的,一方面,速度相对于基本类型比较慢,另一方面,对象类型本身比较大
- 以参数形式传递简单类型的变量时,实际上是将参数的值作为一个副本传进方法函数的,那么在方法函数中不管怎么改变其值,其结果都是只改变了副本的值,而不是源值
- Java对于引用形式传递对象类型的变量时,实际上是将引用作为一个副本传进方法函数的。那么这个函数里面的引用副本所指向的是什么呢?是对象的地址,通过引用副本找到地址并修改地址中的值,也就修改了对象。
- 对于基本类型变量,Java是传值的副本;对于一切对象型变量,Java都是传引用的副本
- 在任意多个类的实例中,一个静态变量的实例只存在一个
- 方法参数有基本类型,另外一种类型是Object对象。Java方法参数传对象,传的是这个对象引用的一份副本,即地址值,跟原来的引用都是指向同一个对象
- Java的对象序列化能将一个实现了Serializable接口的对象转换成一组byte,这样日后要用这个对象的时候,就能把这些byte数据恢复出来,并据此重新构建那个对象
- 一个字符串中可能包含a~z中的多个字符,如有重复,如String data = "aavzcadfdsfsdhshgWasdfasdf",求出现次数最多的那个字母及次数,如有多个重复的则都求出。思路如下:1. 引入TreeSet,通过集合快速找到所有出现的字符串;2.引入ArrayList,为了快速排序,再通过StringBuffer生成排序后的字符串;3. 通过String API中的基本方法indexOf,lastIndexOf来计算TreeSet中每个字符串的最大值
- Java在创建对象时会自动分配内存,并当该丢下的引用不存在时释放这块内存
- Java中使用被称为垃圾收集器的技术来监视Java程序的运行,当对象不再使用时,就自动释放对象所使用的内存。
- 垃圾收集器是自动运行的,程序运行时,垃圾收集器会不时检查对象的各个引用,并回收无引用对象所占用的内存。调用System类中的静态gc()方法可以运行垃圾收集器,但这样并不能保证立即回收指定对象
- Java垃圾回收机制:gc即垃圾收集机制,是指JVM用于释放那些不再使用的对象所占用的内存。Java语言并不要求JVM有gc,也没有规定gc如何工作。
- Java的垃圾回收机制是为所有的Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能命令垃圾回收机制做什么,怎么做或做多少。在JVM垃圾回收期收集一个对象之前,一般要求程序调用适当的方法释放资源,但是没有明确释放资源的情况下,Java提供了默认机制终止化该对象来释放资源,这个方法就是finalize()。在finalize()方法返回之后,对象消失,垃圾收集开始执行。
- 在Java语言中,判断一块内存空间是否符合垃圾收集器收集的标准只有以下两个:1. 给对象赋予了空值null,以后再没有调用过。2. 给对象赋予了新值,即重新分配了内存空间
- 一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集
- Java的内存管理就是对象的分配和释放问题。在Java中,需要通过new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是由GC完成的
- 在Java中,内存泄露就是存在一些被分配的对象,这些对象有下面两个特征:1. 对象是可达的,2.对象是无用的。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄露,这些对象不会被GC所回收,然而它们却占用内存
- Object类的clone是protected的,不能直接调用,可以被子类调用。Object类的clone会知道对象大小,为它分配足够的内存空间,并将旧对象的内复制到新的对象中。但是Object.clone()执行其动作之前必须先检查class是否实现了Cloneable接口。
- 现实世界中的对象均有属性和行为,映射到计算机程序上,属性则表示对象的数据,行为表示对象的方法(其作用是处理数据或同外界交互)。所谓封装,就是用一个自主式框架把对象的数据和方法连在一起形成一个整体。对象是支持封装的手段,是封装的基本单位。
- 类描述了一组具有相同特性(属性)和相同行为(方法)的对象。
- Java容器类库一共有两种主要类型:Collection和Map
- Collection和Map的区别在于容器内每个槽所存储的元素个数不同。Collection类型中,每个槽只有一个元素,Map类型中,持有key-value关联,像一个小型数据库。所有的Java容器类都可以自动调整自己的尺寸。
- List,Collection的子类,以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。
- ArrayList,删除随机访问元素,但在List中间插入、删除、移动元素较慢
- LinkedList,插入、删除、移动元素方便,随机访问元素差。
- Set,每个值之内保存一个对象,不能含有重复的元素
- HashSet,使用散列函数
- TreeSet,使用红黑树
- LinkedHashSet,使用链表结合散列函数
- Queue,先进先出的容器
- List,Set,Map将所有的对象一律视为Object类型。
- Collection、List、Set、Map都是接口,不能实例化。继承自它们的ArrayList、Vector、HashTable、HashMap、Stack是具体class,这些才可能实例化。
- Vector容器确切地知道它所持有的对象隶属什么类别。Vector不进行边界检查。
- Collections是针对集合类的一个帮助类,它提供了一系列静态方法实现对各种集合的搜索、排序、线程完全化等操作。
- 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息复制一份至数列某位置,一旦将对象置入容器内,便损失了该对象的类别信息。
- 在各种Lists中,最好的做法是以ArrayList作为默认选择。当插入、删除频繁时,使用LinkedList();Vector总是比ArrayList慢,在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。HashTree的意义是用来维护其内元素的排序状态。在各种Maps中,HashMap用于快速查找。当元素个数固定时,最好使用Array,因为Array的效率是最高的。
- HashMap和Hashtable的区别。它们都属于Map接口的类,实现了将唯一键映射到特定的值上。HashMap类没有分类或者排序,它允许一个null键和多个null值。Hashtable类似于HashMap,但是不允许null键和null值。它也比HashMap慢,因为它是同步的。Hashtable继承自Dictionary类,而HashMap是Map接口的一个实现。两者间最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供外同步。
- 多态性可以简单地概括为“一个接口,多种方法”。在程序运行的过程中才决定调用哪个函数。多态性是面向对象编程领域的核心概念。多态性是允许你将父对象设置成为和它的一个或更多的子对象相等的技术。父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。允许将子类类型的指针赋值给父类类型的指针。
- 子类重新定义父类虚函数的做法称为覆盖(override),重载(overloiad)是指允许存在多个同名函数,而这些函数的参数表不同。
- 一个接口中的方法必须在一个类中实现后才能被使用,一个类继承实现一个接口,称为这个类实现了该接口,一个接口可以被多个类实现,一个类也可以继承多个接口。
- 对于在同一个可访问区内被声明的几个具有不同参数列(参数的类型
- 个数、顺序不同)的同名函数,程序会根据不同的参数列来确定具体调用哪个函数,这种机制叫重载,重载不关心函数的返回值类型。覆盖是指派生类中存在重新定义的函数,其函数名、参数列、返回值类型必须同父类中对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体(花括号中的部分不同) ,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫覆盖
- 静态方法不能被覆盖成非静态的方法
- 子类的构造函数如果要引用super,必须把super放在函数的首位,如果一定要引用super构造函数,则必须把super()放在前面
- 如果想用super继承父类构造的方法,但是没有放在第一行的话,那么在super之前的语句,肯定是为了满足自己想要完成某些行为的语句。但是又用了super继承父类的构造方法,那么以前所做的修改就都回到以前,就是说又成了父类的构造方法。
- This关键字使用在一个成员函数的内部,指向当前对象,当前对象指的是调用当前正在执行方法的那个对象。Super关键字是直接指向超类的构造函数,用来引用超类中的变量和方法
- 抽象类只能作为其他类的基类,它不能直接被实例化,而且对抽象类不能使用new操作符。抽象类如果含有抽象的变量或值,则它们要么是null类型,要么包含了对非抽象类的实例的引用
- 抽象类允许包含抽象成员,但这不是必须的(可以允许一个抽象类中没有任何抽象成员),抽象类中可以有非抽象方法。
- 抽象类不能同时又是final的。Final类不能同时又是抽象类
- 如果一个非抽象类从抽象类中派生,则其必须通过覆盖类实现所有继承而来的抽象成员
- 抽象类可以被抽象类所继承,结果仍是抽象类
- 抽象类允许被声明
- 有一千万条有重复的短信,以文本文件的形式保存,一行一条,也有重复。请用五分钟时间找出重复出现最多的前10条。答:用哈希表的方法。可以将1千万条短信分成若干组,进行边扫描边建散列表的方法。第一次扫描,取首字节、尾字节、中间任意两字节作为Hash Code,插入到hash table,并记录其地址,信息长度和重复次数。同hash code且等长就疑似相同,比较一下。相同记录只加1次进hash table,但将重复次数加1.一次扫描以后,已经记录各自的重复次数,进行第二次hash table的处理。用线性时间选择可在O(n)的级别上完成前10条的寻找。分组后每组中的top10必须保证各不相同,可用hash来保证,也可以直接按hash的值大小分类。
- 二叉搜索树(Binary Search Tree)或者是一棵空树,或者是具有下列性质的二叉树:对于树中的每个节点X,它的左子树中所有关键字的值都小于X的关键字值,它的右子树中的所有关键字值都大于X的关键字值。这意味着该树所有的元素都可以用某种统一的方式排序
- 二叉搜索树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉搜索树的存储结构。中序遍历二叉搜索树可得到一个关键字的有序序列,一个无序序列可以通过构造一颗二叉搜索树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。每次插入的新节点都是二叉搜索树上新的叶子节点,在进行插入操作时,不必移动其他节点,只需改动某个节点的指针,由空变为非空即可。搜索、插入、删除的复杂富等于树高,即为O(log(n))
- 一般来说,哪个需要的额外空间越多,哪个就越快
- Hash是一种典型的以空间换时间的算法
- 交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。主要排序方法有冒泡排序和快速排序
- 选择排序的原理如下:首先在未排序序列中找到最小元素,存放到排序序列的起始桅子花,然后再从剩下的排序元素中继续寻找最小元素,放到排序序列末尾。以此类推,直到所有的元素均排序完毕
- 希尔排序的中心思想是将数据进行分组,然后对每一组数据进行排序,在每一组数据都有序之后,就可以对所有的分组进行插入排序进行最后一次排序,
- 分治法的基本思想:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
- 归并排序指的是将两个已经排序的序列合并成一个序列的操作
- 泛型的本质是参数化类型,也就是说,所操作的数据类型被指定为一个参数。这种参数类型可用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
- “==”判断符号左右两个变量(Object)是否指向同一内存地址;“equals()”判断两个object是否一样(所有成员的值相同)
- New方法决定了两个不同的string被创建在内存heap区
- 常量池,在Java编译好的class文件中,有个区域称为Constant Pool,它是一个由数组组成的表,类型为cp_info constant_pool[],用来存储程序中使用的各种常量,包括Class、String、Integer等各种基本的Java数据类型。
- String使用private final char value[]来实现字符串的存储,也就是说,String对象创建之后,就不能再修改此对象中存储的字符串内容,所以说String类型是不可变的(immutable)
- String s = “hello”;JVM先根据内容”hello”查找对象,如果没有找到,则在heap上创建新对象,并将其赋予s1,否则使用已经存在的对象。
- String s = new String(“hello”); JVM直接在heap上创建新的对象,所以在heap中会出现内容相同而地址不同的String对象。
- ==是比较地址,equals是比较内容
- 不要使用new创建String
- 在编译期能够确定字符串值的情况下,使用+效率最高
- 避免使用+=来构造字符串
- Singleton模式的主要作用是保证在Java应用程序中,一个类Class只有一个实例存在
- 享元模式以共享的方式高效地支持大量的细粒度对象。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。内蕴状态是存储在享元对象内部并且不会随环境改变而改变。因此,内蕴状态可以共享。外蕴状态是随环境改变而改变不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象呗创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。
- 浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制。而深复制是对对象实例中字段引用的对象也进行复制的一种方式。执行深复制后,原来的对象和新创建的对象不会共享任何东西,改变一个对象对另外一个对象没有任何影响
- MVC包括三类对象,Model是应用对象,View是它在屏幕上的表示,Controller定义用户界面对用户输入的响应方式
- 模型是应用程序的主体部分,模型表示业务数据或者业务逻辑;视图是应用程序中用户界面相关的部分,是用户看到并与之交互的界面;控制器工作就是根据用户的输入,控制用户界面数据显示和更新model对象状态。
- 一个进程至少包含一个线程
- Sleep()是使线程停止一段时间的方法,在sleep时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其他线程可能正在运行,而且没有被调度为放弃执行,除非醒来的线程具有更高的优先级,或者正在运行的线程因为其他原因而阻塞。
- Wait(),当线程交互时,如果线程对一个同步对象x发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到
- Run()当然可以改变线程的状态,比如设置了优先级,线程可能转换为等待状态,而设置优先级的方法是setPriority()
- 启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,但并不意味着线程就会立即运行。Run()方法可以产生必须退出的标志来停止一个线程
- 线程指在程序执行过程中,能够执行程序代码的一个执行单位。每个程序至少都有一个线程,也就是程序本身。Java中的线程有4种状态,分布是运行、就绪、挂起、结束
- 第一范式,如果关系模式R的所有属性的值域中每一个值都是不可再分解的值,则称R属于第一范式模式
- 如果关系模式R为第一范式,并且R中每一个非主属性完全函数依赖于R的某个候选键,则称R为第二范式模式
- 如果关系模式R是第二范式,且每个非主属性都不依赖于R的候选键,则称R是第三范式的模式
- 设R是一个关系模式,D是R上的多值依赖集合,如果D中成立非平凡多值依赖x—》Y,X必是R的超键
- 存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其他对象的任务,用户可以调用存储过程。而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值,而且不涉及特定用户表
- 数据库事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全做,要么全不做,是一个不可分割的工作单位
- 控制反转是对组件对象控制权的转移,从程序代码本身转移到了外部容器,通过容器来实现对象组件的装备和管理
- 在Spring中,所谓依赖注入,即在运行期由容器将依赖关系注入到组件之中。就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件提供的setter方法进行设定。
- Hibernate可以理解为是一个中间件。它负责把Java程序的SQL语句接收过来并发送到数据库,而数据库返回来的信息由Hibernate接收后直接生成一个对象传给Java
- Class.forName调用该访问返回一个以字符串指定类名的类的对象
作者:Shane
出处:http://bluescorpio.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://bluescorpio.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。