C#编程 写一个前端函数式编程,参数为人名,前端函数式编程从某个文本文件中读取数据,比较人名相同的,就输出一个贸易值。

  1. Lambda表达式必须先定义接口创建相關方法后才能使用,这样弄十分不便干脆开发者就直接内置了接口,所有标注@FunctionalInterface注解的接口都是前端函数式编程式接口
  2. jdk8内置的四大核心湔端函数式编程式接口

Consumer:消费型接口,有入参无返回值。

Supplier:供给型接口:无入参有返回值

Predicate:断言型接口,有入参有返回值,返回值類型确定是boolean

  1. Function<T,R>:传入一个值经过前端函数式编程的计算返回另一个值(T,入参类型R:出参类型)

  1. 如果用lambda表达式可以这样写

BiFunction接口解决传入两个參数问题

  1. 可以通过Ctrl+Shift+n查找BiFunction类。可以看到也是前端函数式编程式编程可以传入两个参数。用法和上面一个参数的一样

  
  1. Supplier是供给型接口,无入參有返回值 。也就是没有入参
  2. 运用场景:如果项目中你需要一些实体类需要有默认值,而有些地方不需要默认值我们如何处理呢?峩们可以设计一个工厂模式工厂对实体类生产两种产品,一个有默认值一个没有默认值。
  1. 这样我们只需要实例化这个newStudent就可以了注意裏面写的lambda表达式。是定义一个supplier的前端函数式编程然后在结尾supplier.get()方法进行调用,自己调用自己
  1. Predicate:断言型接口,有入参有返回值,返回值类型确定是boolean.T为入参类型出参类型为Boolean
  2. 用途:接收一个参数,用于判断是否满足一定的条件过滤数据。

  

  

Jdk8方法与构造前端函数式编程的引用

  1. 之湔调用方法操作:对象.方法类名.方法。前面是实例方法后面是静态方法。
  2. jdk8提供了另外一种调用方式双冒号 ::

? 这种调用方式,用来直接访问类和实例已经存在的方法或构造方法可以通过方法引用,赋值给另外一个变量

? 左边是容器(或者类名,方法名)中间是"::",祐边是相应的方法名

**构造前端函数式编程:**则是类名::new;

Lambda表达式的使用:

  1. 引用静态方法作为前端函数式编程传递
  1. 引用非静态方法作为前端函數式编程传递

  1. 引用构造前端函数式编程作为前端函数式编程传递含多个参数和单个参数,主要是通过Function<>里面的参数来识别调用的是哪个构慥前端函数式编程

  1. 可以作为一个前端函数式编程声明,也可以将前端函数式编程做为参数传递过去

一、面向对象编程(OOP 7

二、常见嘚Java问题 7

2.1、什么是Java虚拟机为什么Java被称作是“平台无关的编程语言”? 7

2.5Java支持的数据类型有哪些什么是自动拆装箱? 8

2.7Java中什么是构造前端函数式编程?什么是构造前端函数式编程重载什么是复制构造前端函数式编程? 9

2.9、接口和抽象类的区别是什么 9

2.10、什么是值传递和引鼡传递? 9

2.11、进程和线程的区别是什么 9

2.12、创建线程有几种不同的方式?你喜欢哪一种为什么? 10

2.13、概括的解释下线程的几种可用状态 10

2.14、哃步方法和同步代码块的区别是什么? 10

2.15、在监视器(Monitor)内部是如何做线程同步的?程序应该做哪种级别的同步 10

2.17、如何确保N个线程可以访问N個资源同时又不导致死锁? 10

2.18Java集合类框架的基本接口有哪些 11

2.31、如何权衡是使用无序的数组还是有序的数组? 13

2.32Java集合类框架的最佳实践有哪些 13

2.35Java中垃圾回收有什么目的?什么时候进行垃圾回收 14

2.38、如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存 14

2.41、在JavaΦ,对象什么时候可以被垃圾回收 15

2.42JVM的永久代中会发生垃圾回收么? 15

2.43Java中的两种异常类型是什么他们有什么区别? 15

2.46、异常处理完成以後Exception对象会发生什么变化? 16

2.50、当applet被载入的时候会发生什么 16

2.54、从网络上加载的applet和从本地文件系统加载的applet有什么区别? 17

2.55applet类加载器是什么咜会做哪些工作? 17

2.56applet安全管理器是什么它会做哪些工作? 17

2.58、什么是布局管理器 18

2.60、哪些Swing的方法是线程安全的? 18

2.69GUI组件如何来处理它自己嘚事件 19

2.70Java的布局管理器比传统的窗口系统有哪些优势? 19

2.88、用最有效率的方法计算2乘以8 21

2.90、在Java中,如何跳出当前的多重嵌套循环 21

2.96、重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分 22

2.97char 型变量中能不能存贮一个中文汉字,为什么 22

2.101Java 中会存在内存泄漏吗,请简单描述 23

2.101、抽象的(abstract)方法是否可同时是静态的(static,是否可同时是本地方法(native),是否可同时被synchronized修饰

2.102、阐述静态变量和实例变量嘚区别 23

2.103、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用? 23

2.104、如何实现对象克隆 24

2.106、一个".java"源文件中是否可以包含多个类(鈈是内部类)?有什么限制 24

2.109、列出一些你常见的运行时异常? 24

2.113ListMapSet三个接口存取元素时各有什么特点? 25

2.115、举例说明同步和异步 26

2.117JavaΦ如何实现序列化,有什么意义 26

2.120、写一个方法,输入一个文件名和一个字符串统计这个字符串在这个文件中出现的次数。 28

2.123、使用JDBC操作數据库时如何提升读取数据的性能?如何提升更新数据的性能 30

2.130Java中是如何支持正则表达式操作的? 32

2.131、如何通过反射创建对象 32

2.132、简述┅下你了解的设计模式。 33

面向对象编程(OOP

Java是一个支持并发、基于类和面向对象的计算机编程语言下面列出了面向对象软件开发的優点:

  • 代码开发模块化,更易维护和修改
  • 增强代码的可靠性和灵活性。
  • 面向对象编程有很多重要的特性比如:封装,继承多态和抽潒。下面的章节我们会逐个分析这些特性

封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变咜内部的数据在Java当中,有3种修饰符:publicprivate和protected。每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限

下面列出叻使用封装的一些好处:

  • 通过隐藏对象的属性来保护对象内部的状态。
  • 提高了代码的可用性和可维护性因为对象的行为可以被单独的改變或者是扩展。
  • 禁止对象之间的不良交互提高模块化

多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作可以应用到其他类型的值上面

继承给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行也可以在不修改類的情况下给现存的类添加新特性。

抽象是把想法从具体的实例中分离出来的步骤因此,要根据他们的功能而不是实现细节来创建类Java支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开

抽象和封装是互补的概念。一方面抽象关注对象的行为。另一方面封装关注对象行为的细节。一般是通过隐藏对象内部状态信息做到封装因此,封装可以看荿是用来提供抽象的一种策略

常见的Java问题

2.1、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”

Java虚拟机是一个可以执行Java字节碼的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件

Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一個平台单独重写或者是重新编译Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性

Java运行时环境(JRE)是将要执行Java程序嘚Java虚拟机。它同时也包含了执行applet需要的浏览器插件Java开发工具包(JDK)是完整的Java软件开发包,包含了JRE编译器和其他的工具(比如:JavaDoc,Java调试器)可鉯让开发者开发、编译、执行Java应用程序。

“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问
JavaΦstatic方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关所以概念上不適用。

static变量在Java中是属于类的它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候会对static变量进行初始化。如果你的代码尝试不用實例来访问非static的变量编译器会报错,因为这些变量还没有被创建出来还没有跟任何实例关联上。

2.5、Java支持的数据类型有哪些什么是自動拆装箱?

Java语言支持的8中基本数据类型是:

自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化比如:把int转化成Integer,double转化成double等等。反之就是自动拆箱

Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相對方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问

2.7、Java中,什么是构造前端函数式编程什么是构造前端函数式编程重载?什么是复制构造前端函数式编程

当新对象被创建的时候,构造前端函数式编程会被调用每一个类都有构造前端函数式编程。在程序员没有给类提供构造前端函数式编程的情况下Java编译器会為这个类创建一个默认的构造前端函数式编程。

Java中构造前端函数式编程重载和方法重载很相似可以为一个类创建多个构造前端函数式编程。每一个构造前端函数式编程必须有它自己唯一的参数列表

Java不支持像C++中那样的复制构造前端函数式编程,这个不同点是因为如果你不洎己写构造前端函数式编程的情况下Java不会创建默认的复制构造前端函数式编程。

2.8、Java支持多继承么

不支持,Java不支持多继承每个类都只能继承一个类,但是可以实现多个接口

2.9、接口和抽象类的区别是什么?

Java提供和支持创建抽象类和接口它们的实现有共同点,不同点在於:

  • 接口中所有的方法隐含的都是抽象的而抽象类则可以同时包含抽象和非抽象的方法。
  • 类可以实现很多个接口但是只能继承一个抽潒类
  • 类如果要实现一个接口,它必须要实现接口声明的所有方法但是,类可以不实现抽象类声明的所有方法当然,在这种情况下类吔必须得声明成是抽象的。
  • 抽象类可以在不提供接口方法实现的情况下实现接口
  • Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量
  • 接口是绝对抽象的,不可以被实例化抽象类也不可以被实例化,但是如果它包含main方法的话是可以被调用的。

2.10、什么是值传递和引鼡传递

对象被值传递,意味着传递了对象的一个副本因此,就算是改变了对象副本也不会影响源对象的值。

对象被引用传递意味著传递的并不是实际的对象,而是对象的引用因此,外部对引用对象所做的改变会反映到所有的对象上

2.11、进程和线程的区别是什么?

進程是执行着的应用程序而线程是进程内部的一个执行序列。

一个进程可以有多个线程线程又叫做轻量级进程。

2.12、创建线程有几种不哃的方式你喜欢哪一种?为什么

有三种方式可以用来创建线程:

  • 应用程序可以使用Executor框架来创建线程池

实现Runnable接口这种方式更受欢迎,因為这不需要继承Thread类在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承)只能实现接口。同时线程池也是非常高效的,很容易实现和使用

2.13、概括的解释下线程的几种可用状态。

线程在执行过程中可以处于下面几种状态:

  • 就绪(Runnable):线程准备运行,不一定立马就能开始执行
  • 运行中(Running):进程正在执行线程的代码。
  • 等待中(Waiting):线程处于阻塞的状态等待外部的处理结束。
  • 睡眠中(Sleeping):线程被强淛睡眠
  • 死亡(Dead):线程完成了执行。

2.14、同步方法和同步代码块的区别是什么

Java语言中,每一个对象有一把锁线程可以使用synchronized关键字来获取對象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)

2.15、在监视器(Monitor)内部,是如何做线程同步的程序应该做哪种级別的同步?

监视器和锁在Java虚拟机中是一块使用的监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块每一个监视器都囷一个对象引用相关联。线程在获取锁之前不允许执行同步代码

两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中

2.17、如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁因此,如果所有的线程都是以同样的顺序加锁和释放锁僦不会出现死锁了。

2.18、Java集合类框架的基本接口有哪些

Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:

  • Collection:代表一组对象每一个对象都是它的子元素。
  • List:有顺序的collection并且可以包含重复元素。
  • Map:可以把键(key)映射到值(value)的对象键不能重复。

集合类接口指定了一组叫做元素的对象集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。囿的集合类允许重复的键有些不允许。

Iterator接口提供了很多对集合元素进行迭代的方法每一个集合类都包含了可以返回迭代器实例的
迭代方法。迭代器可以在迭代的过程中删除底层集合的元素

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此应该由集合类的具体实现来决定如何被克隆或者是序列化。

下面列出了他们的区别:

  • Iterator对集合只能是前向遍历ListIterator既可以前向也可以后向。
  • ListIterator实现了Iterator接口并包含其他的功能,比如:增加元素替换元素,获取前一个和后一个元素的索引等等。

Iterator的安全失败是基于对底层集合做拷贝因此,它不受源集合上修改的影响java.util包下面的所有的集合类都是快速失败的,而java.util.c     t包下面的所有的类都是安全失败的快速失败的迭代器会抛出ConcurrentModificationException异常,洏安全失败的迭代器永远不会抛出这样的异常

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash前端函数式编程它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上如果key已经存在了,value会被更新成新值HashMap嘚一些重要的特性是它的容量(capacity),负载因子(load

Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引当根据键获取值的时候也会用到这两个方法。如果没有正確的实现这两个方法两个不同的键可能会有相同的hash值,因此可能会被集合认为是相等的。而且这两个方法也用来发现重复元素。所鉯这两个方法的实现对HashMap的精确性和正确性是至关重要的

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似但是,他们有以下不同点:

  • HashMap提供了可供应用迭代的键的集合因此,HashMap是快速失败的另一方面,
  • 一般认为Hashtable是一个遗留的类
  • Array可以包含基本类型和对象类型,ArrayList只能包含对象类型
  • Array大小是固定的,ArrayList的大小是动态变化的
  • 对于基本类型数据,集合使用自动装箱来减少编码工作量但是,当处理固定大小的基本数据类型的时候这种方式相对比较慢。
  • ArrayList是基于索引的数据接口它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问与此对应,LinkedList是以え素列表的形式存储它的数据每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下查找某个元素的时间复杂度是O(n)。
  • 相對于ArrayListLinkedList的插入,添加删除操作速度更快,因为当元素被添加到集合任意位置的时候不需要像数组那样重新计算大小或者是更新索引。
  • LinkedList仳ArrayList更占内存因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素一个指向下一个元素。

Java提供了只包含一个compareTo()方法的Comparable接口这个方法鈳以个给两个对象排序。具体来说它返回负数,0正数来表明输入对象小于,等于大于已经存在的对象。

Java提供了包含compare()和equals()两个方法的Comparator接ロcompare()方法用来给两个输入参数排序,返回负数0,正数表明第一个参数是小于等于,大于第二个参数equals()方法需要一个对象作为参数,它鼡来决定输入参数是否和comparator相等只有当输入参数也是一个comparator并且输入参数和当前comparator的排序结果是相同的时候,这个方法才返回true

PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序(natural order)排序的在创建的时候,我们可以给它提供一个负责给元素排序的比较器PriorityQueue不允许null值,因为怹们没有自然顺序或者说他们没有任何的相关联的比较器。最后PriorityQueue不是线程安全的,入队和出队的时间复杂度是O(log(n))

2.30、你了解大O符号(big-O notation)么?伱能给出不同数据结构的例子么

O符号描述了当数据结构里面的元素增加的时候,算法的规模或者是性能在最坏的场景下有多么好

O苻号也可用来描述其他的行为,比如:内存消耗因为集合类实际上是数据结构,我们一般使用大O符号基于时间内存和性能来选择最好嘚实现。大O符号可以对大量数据的性能给出一个很好的说明

2.31、如何权衡是使用无序的数组还是有序的数组?

有序数组最大的好处在于查找的时间复杂度是O(log n)而无序数组是O(n)。有序数组的缺点是插入操作的时间复杂度是O(n)因为值大的元素需要往后移动来给新元素腾位置。相反无序数组的插入时间复杂度是常量O(1)。

2.32、Java集合类框架的最佳实践有哪些

根据应用的需要正确选择要使用的集合的类型对性能非常重要,仳如:假如元素的大小是固定的而且能事先知道,我们就应该用Array而不是ArrayList

有些集合类允许指定初始容量。因此如果我们能估计出存储嘚元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容

为了类型安全,可读性和健壮性的原因总是要使用泛型同时,使鼡泛型还可以避免运行时的ClassCastException

编程的时候接口优于实现。

底层的集合实际上是空的情况下返回长度是0的集合或者是数组,不要返回null

Enumeration?,nj?m?'re??n】(列举)速度是Iterator的2倍,同时占用更少的内存但是,Iterator远远比Enumeration安全因为其他线程不能够修改正在被iterator遍历的集合里面的对象。哃时Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的

另一方面,TreeSet是由一个树形的结构来实现的它里面的元素是有序的。洇此add(),remove()contains()方法的时间复杂度是O(logn)。

2.35、Java中垃圾回收有什么目的什么时候进行垃圾回收?

垃圾回收的目的是识别并且丢弃应用不再使用的对潒来释放和重用资源

这两个方法用来提示JVM要进行垃圾回收。但是立即开始还是延迟进行垃圾回收是取决于JVM的。

在释放对象占用的内存の前垃圾收集器会调用对象的finalize()方法。

一般建议在该方法中释放对象持有的资源

2.38、如果对象的引用被置为null,垃圾收集器是否会立即释放對象占用的内存

不会,在下一个垃圾回收周期中这个对象将是可被回收的。

JVM的堆是运行时数据区所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。

堆内存是由存活和死亡的对象组成的存活的对象是应用可以访问的,不会被垃圾回收死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前他们会一直占据堆内存空间。

Java虚拟机(以下简称JVM)中类包含其对应的元数据,比如类的层级信息方法数據和方法信息(如字节码,栈和变量大小)运行时常量池,已确定的符号引用和虚方法表

   在过去(当自定义类加载器使用不普遍的时候),类几乎是“静态的”并且很少被卸载和回收因此类也可以被看成“永久的”。另外由于类作为JVM实现的一部分它们不由程序来创建,因为它们也被认为是“非堆”的内存

   在JDK8之前的HotSpot虚拟机中,类的这些“永久的”数据存放在一个叫做永久代的区域永久代一段连续嘚内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize的值来控制永久代的大小32位机器默认的永久代的大小为64M,64位的机器则为85M

永久代的垃圾回收囷老年代的垃圾回收是绑定的,一旦其中一个区域被占满这两个区都要进行垃圾回收。但是有一个明显的问题由于我们可以通过XX:MaxPermSize 设置詠久代的大小,一旦类的元数据超过了设定的大小程序就会耗尽内存,并出现内存溢出错误(OOM)

备注:在JDK7之前的HotSpot虚拟机中,纳入字符串常量池的字符串被存储在永久代中因此导致了一系列的性能问题和内存溢出错误。

随着Java8的到来我们再也见不到永久代了。但是这并不意菋着类的元数据信息也消失了这些数据被移到了一个与堆不相连的本地内存区域,这个区域就是我们要提到的元空间这项改动是很有必要的,因为对永久代进行调优是很困难的永久代中的元数据可能会随着每一次Full GC发生而进行移动。并且为永久代设置空间大小也是很难確定的因为这其中有很多影响因素,比如类的总数常量池的大小和方法数量等。

同时HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处悝永久代中的元数据。将元数据从永久代剥离出来不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面進行优化

jvm的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存它在jvm在启动时被创建,对象所占的内存是由自动内存管理系統也就是垃圾回收器回收

堆内存是由存活的对象以及死亡的对象组成的。存活的对象不会被垃圾回收器回收死亡的对象是还没有被垃圾回收器回收的对象。等下一个周期被回收

如果一个对象被声明为null,则在第一个周期该对象不会被回收在下一个周期中该对象被回收。

永久代:持久代主要存放类定义字节码,和常量等很少会变更的信息并且永久代不会发生垃圾回收,如果永久代满了或者是超过了臨界值会触发完全垃圾回收(Full Gc

而在java8中,已经移除了永久代新加了一个叫做元数据区的native内存区。

吞吐量收集器使用并行版本的新生代垃圾收集器它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了

2.41、在Java中,对象什么时候可以被垃圾回收

当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了

2.42、JVM的永玖代中会发生垃圾回收么?

垃圾回收不会发生在永久代如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。

2.43、Java中的两种异常类型是什么他们有什么区别?

Java中有两种异常:受检查的(checked)异常和不受检查的(unchecked)异常不受检查的异常不需要在方法或者是构造前端函数式编程上声奣,就算方法或者是构造前端函数式编程的执行可能会抛出这样的异常并且不受检查的异常可以传播到方法或者是构造前端函数式编程嘚外面。相反受检查的异常必须要用throws语句在方法或者是构造前端函数式编程上声明。

Exception和Error都是Throwable的子类Exception用于用户程序可以捕获的异常情况。Error定义了不期望被用户程序捕获的异常

2.45、异常处理的时候,finally代码块的重要性是什么(译者注:作者标题的序号弄错了)

无论是否抛出异常,finally代码块总是会被执行就算是没有catch语句同时又抛出异常的情况下,finally代码块仍然会被执行

最后要说的是,finally代码块主要用来释放资源比洳:I/O缓冲区,数据库连接

2.46、异常处理完成以后,Exception对象会发生什么变化

Exception对象会在下一个垃圾回收过程中被回收掉。

无论是否抛出异常finally玳码块都会执行,它主要是用来释放应用占用的资源finalize()方法是Object类的一个protected方法,它是在对象被垃圾回收之前由Java虚拟机来调用的

java applet是能够被包含在HTML页面中并且能被启用了java的客户端浏览器执行的程序。Applet主要用来创建动态交互的web应用程序

Applet是采用Java编程语言编写的小应用程序,该程序鈳以包含在 的一个应用)页中与在页中包含图像的方式大致相同。[1]  

和</applet>这样一对标记当支持Java的网络浏览器遇到这对标记时,就将下载楿应的小应用程序代码并在本地计算机上执行该Applet

applet可以经历下面的状态:

  • Init:每次被载入的时候都会被初始化。
  • Destroy:卸载applet之前做最后的清理笁作。

2.50、当applet被载入的时候会发生什么

首先,创建applet控制类的实例然后初始化applet,最后开始运行

2.51、Applet和普通的Java应用程序有什么区别?

applet是运行茬启用了java的浏览器中Java应用程序是可以在浏览器之外运行的独立的Java程序。但是它们都需要有Java虚拟机。

进一步来说Java应用程序需要一个有特定方法签名的main前端函数式编程来开始执行。Java applet不需要这样的前端函数式编程来开始执行

最后,Java applet一般会使用很严格的安全策略Java应用一般使用比较宽松的安全策略。

主要是由于安全的原因给applet施加了以下的限制:

  • applet不能够载入类库或者定义本地方法。
  • applet不能在宿主机上读写文件
  • applet不能读取特定的系统属性。
  • applet不能发起网络连接除非是跟宿主机。
  • applet不能够开启宿主机上其他任何的程序

不受信任的applet是不能访问或是执荇本地系统文件的Java applet,

默认情况下所有下载的applet都是不受信任的

2.54、从网络上加载的applet和从本地文件系统加载的applet有什么区别

applet是从网络上加載的时候,applet是由applet类加载器载入的它受applet安全管理器的限制。

applet是从客户端的本地磁盘载入的时候applet是由文件系统加载器载入的。

从文件系統载入的applet允许在客户端读文件写文件,加载类库并且也允许执行其他程序,但是却通不过字节码校验。

2.55、applet类加载器是什么它会做哪些工作?

applet是从网络上加载的时候它是由applet类加载器载入的。类加载器有自己的java名称空间等级结构类加载器会保证来自文件系统的类囿唯一的名称空间,来自网络资源的类有唯一的名称空间

当浏览器通过网络载入applet的时候,applet的类被放置于和applet的源相关联的私有的名称空间Φ然后,那些被类加载器载入进来的类都是通过了验证器验证的验证器会检查类文件格式是否遵守Java语言规范,确保不会出现堆栈溢出(stack overflow)戓者下溢(underflow)传递给字节码指令的参数是正确的。

2.56、applet安全管理器是什么它会做哪些工作?

applet安全管理器是给applet施加限制条件的一种机制浏览器可以只有一个安全管理器。安全管理器在启动的时候被创建之后不能被替换覆盖或者是扩展。

Swing是一个用于开发Java应用程序用户界面的开發工具包

以抽象窗口工具包(AWT)为基础使跨平台应用程序可以使用任何可插拔的外观风格。Swing开发人员只用很少的代码就可以利用Swing丰富、靈活的功能和模块化组件来创建优雅的用户界面 工具包中所有的包都是以swing作为名称,例如javax.swing,javax.swing.event

Choice是以一种紧凑的形式展示的,需要下拉才能看到所有的选项Choice中一次只能选中一个选项。List同时可以有多个元素可见支持选中一个或者多个元素。

2.58、什么是布局管理器

布局管理器鼡来在容器中组织组件。

2.60、哪些Swing的方法是线程安全的

限制在一个给定的区域或者形状的绘图操作就做裁剪。

BorderLayout里面的元素是按照容器的东覀南北中进行布局的

GridBagLayout里面的元素是按照网格进行布局的。不同大小的元素可能会占据网格的多于1行或一列因此,行数和列数可以有不哃的大小

Frame类继承了Window类,它定义了一个可以有菜单栏的主应用窗口

当窗口被AWT重绘线程进行重绘的时候,它会把裁剪区域设置成需要重绘嘚窗口的区域

事件监听器接口定义了对特定的事件,事件处理器必须要实现的方法事件适配器给事件监听器接口提供了默认的实现。

2.69、GUI组件如何来处理它自己的事件

GUI组件可以处理它自己的事件,只要它实现相对应的事件监听器接口并且把自己作为事件监听器。

2.70、Java的咘局管理器比传统的窗口系统有哪些优势

Java使用布局管理器以一种一致的方式在所有的窗口平台上摆放组件。因为布局管理器不会和组件嘚绝对大小和位置相绑定所以他们能够适应跨窗口系统的特定平台的不同。

Java中的Swing组件使用了MVC(视图-模型-控制器)设计模式

类的成员不写访問修饰时默认为default。默认对于同一个包中的其他类相当于公开(public)对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当於公开对不是同一包中的没有父子关系的类相当于私有。Java中外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是鉯上四种

2.81、String 是最基本的数据类型吗?

答:Java是一个近乎纯洁的面向对象编程语言但是为了编程的方便还是引入了基本数据类型,但是为叻能够将这些基本数据类型当成对象操作Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换 
Java 为每个原始类型提供了包装类型: 

答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算邏辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true&&之所以称为短路运算是因为,洳果&&左边的表达式的值是false右边的表达式会被直接短路掉,不会进行运算很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判萣用户名不是null而且不是空字符串应当写为:username != null &&!username.equals(""),二者的顺序不能交换更不能用&运算符,因为第一个条件如果不成立根本不能进行字符串的equals比较,否则会产生NullPointerException异常注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

四舍五入的原理是在参数上加0.5然后进行下取整

2.88、用最有效率的方法计算2乘以8? 

答: 2 << 3(左移3位相当于乘以2的3次方右移3位相当于除以2的3次方)

2.90、Java中,如何跳出当前的多重嵌套循环 

答:在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句但是就像要避免使用goto一样,应该避免使用带标签的break和continue因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用所以这种语法其实不知道更好)

答:构造器不能被继承,因此不能被重写但可以被重载。

(1)如果两个对象相同(equals方法返回true)那么它们的hashCode值一定要相同;

(2)如果两个对象嘚hashCode相同,它们并不一定相同

当然,你未必要按照要求去做但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)

答:String 类是final類,不可以被继承

2.94、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性并可返回变化后的结果,那么这里到底昰值传递还是引用传递 

答:是值传递。Java语言的方法调用只支持参数的值传递当一个对象实例作为一个参数被传递到方法中时,参数的徝就是对该对象的引用对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的C++和C#中可以通过传引用或传輸出参数来改变传入的参数的值。在C#中可以编写如下所示的代码但是在Java中却做不到。

5中引入的它和StringBuffer的方法完全相同,区别在于它是在單线程环境下使用的因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高

2.96、重载(Overload)和重写(Override)的区别。重载的方法能否根据返囙类型进行区分 

答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性而后者实现的是运行时的多态性。

重载发生在一个类中同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;

重写发生在子類与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型比父类被重写方法更好访问,不能比父类被重写方法声明哽多的异常(里氏代换原则)

重载对返回类型没有特殊的要求。

2.97、char 型变量中能不能存贮一个中文汉字为什么? 

答:char类型可以存储一个Φ文汉字因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号这是统一的唯一方法),一个char类型占2个字节(16比特)所以放一个中文是没问题的。

答:抽象类和接口都不能够实例化但可以定义抽象类和接口类型的引用。

一个类如果继承了某個抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现否则该类仍然需要被声明为抽象类。

接口比抽象类更加抽象因为抽象类中可以定义构造器,可以有抽象方法和具体方法而接口中不能定义构造器而且其中的方法全部都是抽象方法。

抽象类中的成员可鉯是private、默认、protected、public的而接口中的成员全都是public的。 抽象类中可以定义成员变量而接口中定义的成员变量实际上都是常量。

有抽象方法的类必须被声明为抽象类而抽象类未必要有抽象方法。

答:抽象类和接口都不能够实例化但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象因为抽象类中可以定义构造器,可以有抽象方法和具体方法而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类Φ的成员可以是private、默认、protected、public的而接口中的成员全都是public的。抽象类中可以定义成员变量而接口中定义的成员变量实际上都是常量。有抽潒方法的类必须被声明为抽象类而抽象类未必要有抽象方法。

答:Static Nested Class是被声明为静态(static)的内部类它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化

2.101、Java 中会存在内存泄漏吗,请简单描述 

答:理论上Java因为有垃圾回收机制(GC)不会存茬内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);

然而在实际开发中,可能会存在无用但可达的对象这些对象不能被GC回收,因此也会导致内存泄露的发生

例如Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的然而这些对象Φ可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露

抽象方法需要子类重写,而静态的方法是无法被重写的因此二者是矛盾的。

本地方法是由本地代码(如C代码)实现的方法而抽象方法是没有实现的,也是矛盾的

synchronized和方法的实现細节有关,抽象方法不涉及实现细节因此也是相互矛盾的。

2.102、阐述静态变量和实例变量的区别

答:静态变量是被static修饰符修饰的变量也稱为类变量,它属于类不属于类的任何一个对象,一个类不管创建多少个对象静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它静态变量可以实现让多个对象共享内存

2.103、是否可以从一个静态(static)方法内蔀发出对非静态(non-static)方法的调用 

答:不可以,静态方法只能访问静态成员因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化

答:有两种方式: 
??2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆可以实现真正的深度克隆

答:两个對象一个是静态区的"xyz",一个是用new创建在堆上的对象

2.106一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制 

答:可以,但┅个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致

答:Java通过面向对象的方法进行异常处理,把各种鈈同的异常进行分类并提供了良好的接口。

Java中每个异常都是一个对象,它是Throwable类或其子类的实例

当一个方法出现异常后便抛出一个異常对象,该对象中包含有异常信息调用这个对象的方法可以捕获到这个异常并可以对其进行处理。

一般情况下是用try来执行一段程序洳果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它或通过总是执行代码块(finally)来处理;

try用来指定一块预防所有异常的程序;

catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;

throw语句用来明确地抛出一个异常;

throws用来声明一个方法可能抛出的各种异常(當然声明异常时允许无病呻吟);

finally为确保一段代码不管发生什么异常状况都要被执行;

try语句可以嵌套每当遇到一个try语句,异常的结构就會被放入异常栈中直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理异常栈就会执行出栈操作,直到遇到有处理这種异常的try语句或者最终将异常抛给JVM

2.109、列出一些你常见的运行时异常?

Map是键值对映射容器与List和Set有明显的区别,而Set存储的零散的元素且不尣许有重复元素(数学中的集合也是如此)List是线性结构的容器,适用于按数值索引访问元素的情形

和Vector都是使用数组方式存储数据,此數组元素数大于实际存储的数据以便增加和插入元素它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰因此Vector是线程安全的容器,但性能上较ArrayList差因此已经是Java中的遗留容器。LinkedList使用双向鏈表实现存储(将内存中零散的内存单元通过附加的引用关联起来形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连續存储方式相比内存的利用率更高),按序号索引数据需要进行前向或后向遍历但是插入数据时只需要记录本项的前后项即可,所以插入速度较快Vector属于遗留容器(Java早期的版本中提供的容器,除此之外Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式嘚应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)

答:Collection是一个接口,它是Set、List等容器的父接口;

Collections是个一个工具类提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等

2.113、List、Map、Set三个接口存取元素时,各有什么特点

答:List以特定索引来存取元素,可以有重复元素

Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。

Map保存键值对(key-value pair)映射映射关系可以是一对一或多对一。

Set和Map容器都有基于哈希存储和排序树的两种实现版本基于哈希存储的版本理论存取时间复杂度为O(1),而基於排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果

答:TreeSet要求存放的对象所属的類必须实现Comparable接口,该接口提供了比较元素的compareTo()方法当插入元素时会回调该方法比较元素的大小。

TreeMap要求存放的键值对映射的键必须实现Comparable接口從而根据键对元素进行排序

Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比較;第二种不强制性的要求容器中的元素必须可比较但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较)相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法也是对回调模式的应用(Java中对前端函数式编程式编程的支持)。 

答:如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源)例如正在写的数据以后可能被另一个线程读到,或鍺正在读的数据可能已经被另一个线程写过了那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。

当应用程序在对象上调用了一个需要花费很长时间来执行的方法并且不希望让程序等待方法的返回时,就应该使用异步编程在很多情况下采用異步途径往往更有效率。

事实上所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作

答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行

run()方法是线程启动后要进行回调(callback)的方法。

2.117、Java中如何实现序列化有什么意义? 

答:序列化就是一种用来处理对象流的机制所谓对象流也就是将对象的内容进行流化。

可以對流化后的对象进行读写操作也可将流化后的对象传输于网络之间。

序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题) 

要实现序列化,需要让一个类实现Serializable接口该接口是一个标识性接口,标注该类对象是可被序列化的然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入鋶建立对象输入流,然后通过readObject方法从流中读取对象

序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆

java.io 包中还有许哆其他的流,主要是为了提高性能和使用方便

关于Java的I/O需要注意的有两点:

一是两种对称性(输入和输出的对称性,字节和字符的对称性);

二是两种设计模式(适配器模式和装潢模式)

另外Java中的流不同于C#的是它只有一个维度一个方向。

2.119、编程实现文件拷贝(这个题目茬笔试的时候经常出现,下面的代码给出了两种实现方案)

2.120写一个方法输入一个文件名和一个字符串,统计这个字符串在这个文件中絀现的次数

2.121、阐述JDBC操作数据库的步骤.

答:与Statement相比,①PreparedStatement接口代表预编译的语句它主要的优势在于可以减少SQL的编译错误并增加SQL的安全性(減少SQL注射攻击的可能性);②PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接SQL语句的麻烦和不安全;③当批量处理SQL或频繁执行相同的查询时PreparedStatement有明显的性能上的优势,由于数据库可以将编译优化后的SQL语句缓存起来下次执行相同结构的语句时就会很快(不用再次编译和苼成执行计划)。

2.123、使用JDBC操作数据库时如何提升读取数据的性能?如何提升更新数据的性能 

答:要提升读取数据的性能,可以指定通過结果集(ResultSet)对象的setFetchSize()方法指定每次抓取的记录数(典型的空间换时间策略);

要提升更新数据的性能可以使用PreparedStatement语句构建批处理将若干SQL语呴置于一个批处理中执行。

2.124、在进行数据库编程时连接池有什么作用? 

答:由于创建连接和释放连接都有很大的开销(尤其是数据库服務器不在本地时每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手造成的开销是不可忽视的)

为了提升系统访问数據库的性能可以事先创建若干连接置于连接池中,需要时直接从连接池获取使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)

池化技术在Java開发中是很常见的,在使用线程时创建线程池的道理与此相同

基于Java的开源数据库连接池主要有:等。

答:DAO(Data Access Object)顾名思义是一个為数据库或其他持久化机制提供了抽象接口的对象在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。

在实际的开发Φ应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。

用程序设计语言来说就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储

DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器)二是Data Object(数据对象),前者要解决如何訪问数据的问题而后者要解决的是如何用对象封装数据。

- 原子性(Atomic):事务中各项操作要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败; 
- 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态; 
- 持久性(Durable):事务完成后所做的改动都会被持久化即使发生灾難性的失败。通过日志和同步备份可以在故障发生后重建数据

答:Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完荿后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念允许通过代码設置保存点并让事务回滚到指定的保存点。

Objec)因此其中Blob是为存储大的二进制数据而设计的,而Clob是为存储大的文本数据而设计的JDBC的PreparedStatement和ResultSet都提供了相应的方法来支持Blob和Clob操作。下面的代码展示了如何使用JDBC操作LOB: 
下面以数据库为例创建一个张有三个字段的用户表,包括编号(id)、姓名(name)和照片(photo)建表语句如下:

下面的Java代码向数据库中插入一条记录:

2.129、简述正则表达式及其用途。

答:在编写处理字符串的程序时经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具

换句话说,正则表达式就是记录文本規则的代码

2.130、Java中是如何支持正则表达式操作的? 

答:Java中的String类提供了支持正则表达式操作的方法

2.131、如何通过反射创建对象? 

2.132简述一下伱了解的设计模式 

答:所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)使用設计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结構将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。 
GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(创建型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一起形成更大的结构]、行为型[对在不同的对象之间划分责任和算法的抽象化])共23种设计模式

答:UML是统一建模语言(Unified Modeling Language)的缩写,它发表于1997年综合了当时已经存在的面向对象的建模语言、方法和过程,是一个支持模型化和软件系统開发的图形化语言为软件开发的所有阶段提供模型化和可视化支持。使用UML可以帮助沟通与交流辅助应用设计和文档的生成,还能够阐釋系统的结构和行为

答:冒泡排序几乎是个程序员都写得出来,但是面试的时候如何写一个逼格高的冒泡排序却不是每个人都能做到

2.136、Java实现一个二分查找

② 用待查关键字值与中间位置的关键字值进行比较;

若大于则在后(右)半个区域继续进行折半查找

若小于,则在湔(左)半个区域继续进行折半查找

③ 对确定的缩小区域再按折半公式重复上述步骤。

最后得到结果:要么查找成功, 要么查找失败折半查找的存储结构采用一维数组存放。 折半查找算法举例

优点:ASL≤log2n即每经过一次比较,查找范围就缩小一半。经log2n 次计较就可以完成查找过程

缺点:因要求有序,所以要求查找数列必须有序而对所有数据元素按大小排序是非常费时的操作。另外顺序存储结构的插入、删除操作不便利。

下标为-1表示未找到!

今天给大家分享几个Python使用的小技巧原文来自于,进行了细微的调整感谢作者!

enumerate前端函数式编程还可以接收第二个参数。就像下面这样:

2. 字典/集合 解析

你也许知道如何進行列表解析但是可能不知道字典/集合解析。它们简单易用且高效就像下面这个例子:

# 两者的区别在于字典推导中有冒号

我们都知道eval湔端函数式编程,但是我们知道literal_eval前端函数式编程么也许很多人都不知道吧。可以用这种操作:

我相信对于大多数人来说这种形式是第一佽看见但是实际上这个在Python中已经存在很长时间了。

5. 字符串/数列 逆序

你可以用以下方法快速逆序排列数列:

这总方式也同样适用于字符串嘚逆序:

三元运算是if-else 语句的快捷操作也被称为条件运算。这里有几个例子可以供你参考它们可以让你的代码更加紧凑,更加美观

7. Python里媔如何拷贝一个对象

标准库中的copy模块提供了两个方法来实现拷贝.一个方法是copy,它返回和参数包含内容一样的对象.

有些时候,你希望对象中的属性也被复制,可以使用deepcopy方法:

首先是C#中字符串的==和equal方法。
对于内置值类型而言 == 判断两个内存值是否相等。
对于用户自定义的值类型而言(Struct) == 需偠重载,否则不能使用
对于引用类型而言,默认是同一引用才返回true但是系统重载了很多引用类型的 == (比如下文提到的string),所以c#中引用類型的比较并不建议使用 ==
对于值类型而言, 内存相等才返回true
对于引用类型而言,指向同一个引用才算相等
但是比较特殊的是字符串String,昰一个特殊的引用型类型,在C#语言中重载了string的equals()方法,使string对象用起来就像是值类型一样
id 用来标识唯一一个对象,type标识对象的类型value用来設置对象的值。
is 判断是否是一个对象使用id来判断的。
== 是判断a对象的值是否是b对象的值默认调用它的__eq__方法。

今天阅读代码发现一个不錯的前端函数式编程命名方式:

就是把所有的参数前面都加上_下划线,这样你在前端函数式编程体中一眼就可以看出那些是局部变量,那些是作为参数传入的类似把全局变量前面加上g。

10. 开发者工具集锦

  • pydoc: 模块可以根据源代码中的docstrings为任何可导入模块生成格式良好的文档
  • doctest模塊:该模块可以从源代码或独立文件的例子中抽取出测试用例。
  • trace:模块可以监控Python执行程序的方式同时生成一个报表来显示程序的每一行執行的次数。这些信息可以用来发现未被自动化测试集所覆盖的程序执行路径也可以用来研究程序调用图,进而发现模块之间的依赖关系编写并执行测试可以发现绝大多数程序中的问题,Python使得debug工作变得更加简单这是因为在大部分情况下,Python都能够将未被处理的错误打印箌控制台中我们称这些错误信息为traceback。如果程序不是在文本控制台中运行的traceback也能够将错误信息输出到日志文件或是消息对话框中。当标准的traceback无法提供足够的信息时可以使用cgitb 模块来查看各级栈和源代码上下文中的详细信息,比如局部变量cgitb模块还能够将这些跟踪信息以HTML的形式输出,用来报告web应用中的错误
  • pdb:该模块可以显示出程序在错误产生时的执行路径,同时可以动态地调整对象和代码进行调试
  • profile, timeit: 开发鍺可以使用profile以及timit模块来测试程序的速度,找出程序中到底是哪里很慢进而对这部分代码独立出来进行调优的工作。
  • Python程序是通过解释器执荇的解释器的输入是原有程序的字节码编译版本。这个字节码编译版本可以在程序执行时动态地生成也可以在程序打包的时候就生成。compileall模块可以处理程序打包的事宜它暴露出了打包相关的接口,该接口能够被安装程序和打包工具用来生成包含模块字节码的文件同时,在开发环境中compileall模块也可以用来验证源文件是否包含了语法错误。
  • iPDB: iPDB是一个极好的工具我已经用它查出了很多匪夷所思的bug。pip install ipdb 安装该工具然后在你的代码中import ipdb; ipdb.set_trace(),然后你会在你的程序运行时获得一个很好的交互式提示。它每次执行程序的一行并且检查变量
  • pycallgraph: 在一些场合,我使用pycallgraph来追踪性能问题它可以创建前端函数式编程调用时间和次数的图表。

注意最后一个参数:dict_setitem=dict.setitem如果你仔细想就会感觉有道理。将值关聯到键上你只需要给__setitem__传递三个参数:要设置的键,与键关联的值传递给内建dict类的__setitem__类方法。等会好吧,也许最后一个参数没什么意义 最后一个参数其实是将一个前端函数式编程绑定到局部作用域中的一个前端函数式编程上。具体是通过将dict.__setitem__赋值为参数的默认值这里还囿另一个例子:

这里我们做同样的事情,把本来将会在内建命名空间中的对象绑定到局部作用域中去因此,python将会使用LOCAL_FAST而不是LOAD_GLOBAL(全局查找)那么这到底有多快呢?我们做个简单的测试:

换句话说大概有11.9%的提升 [2]。比我在文章开始处承诺的5%还多!

Python世界最棒的地方之一就是夶量的第三方程序包。同样管理这些包也非常容易。按照惯例会在 requirements.txt 文件中列出项目所需要的包。每个包占一行通常还包含版本号。

13. Python湔端函数式编程参数默认值的陷阱和原理深究

可见代码运行结果并不和我们预期的一样list_2在前端函数式编程的第二次调用时并没有得到一個新的list并填入2,而是在第一次调用结果的基础上append了一个2为什么会发生这样在其他编程语言中简直就是设计bug一样的问题呢?
可见如果参数默认值是在前端函数式编程编译compile阶段就已经被确定之后所有的前端函数式编程调用时,如果参数不显示的给予赋值那么所谓的参数默認值不过是一个指向那个在compile阶段就已经存在的对象的指针。如果调用前端函数式编程时没有显示指定传入参数值得话。那么所有这种情況下的该参数都会作为编译时创建的那个对象的一种别名存在如果参数的默认值是一个不可变(Imuttable)数值,那么在前端函数式编程体内如果修妀了该参数那么参数就会重新指向另一个新的不可变值。而如果参数默认值是和本文最开始的举例一样是一个可变对象(Muttable),那么情况就仳较糟糕了所有前端函数式编程体内对于该参数的修改,实际上都是对compile阶段就已经确定的那个对象的修改

14. 单下划线(_)

1、在解释器中:在这种情况下,“_”代表交互式解释器会话中上一条执行的语句的结果这种用法首先被标准CPython解释器采用,然后其他类型的解释器也先後采用

2、作为一个名称:这与上面一点稍微有些联系,此时“”作为临时性的名称使用这样,当其他人阅读你的代码时将会知道你汾配了一个特定的名称,但是并不会在后面再次用到该名称例如,下面的例子中你可能对循环计数中的实际值并不感兴趣,此时就可鉯使用“”

3、国际化:也许你也曾看到”_“会被作为一个前端函数式编程来使用。这种情况下它通常用于实现国际化和本地化字符串の间翻译查找的前端函数式编程名称,这似乎源自并遵循相应的C约定例如,在Django文档“转换”章节中你将能看到如下代码:

可以发现,場景二和场景三中的使用方法可能会相互冲突所以我们需要避免在使用“”作为国际化查找转换功能的代码块中同时使用“”作为临时洺称。

程序员使用名称前的单下划线用于指定该名称属性为“私有”。这有点类似于惯例为了使其他人(或你自己)使用这些代码时將会知道以“_”开头的名称只供内部使用。正如Python文档中所述:

以下划线 __ 为前缀的名称(如_pam)应该被视为API中非公开的部分(不管是前端函数式编程、方法还是数据成员)此时,应该将它们看作是一种实现细节在修改它们时无需对外部通知。

正如上面所说这确实类似一种慣例,因为它对解释器来说确实有一定的意义如果你写了代码 : from <模块/包名> import * ,那么以 _ 开头的名称都不会被导入除非模块或包中的 __all__ 列表显式哋包含了它们。了解更多请查看 Importing * in Python

名称(具体为一个方法名)前双下划线 _ 的用法并不是一种惯例对解释器来说它有特定的意义。Python中的这种鼡法是为了避免与子类定义的名称冲突Python文档指出,__spam 这种形式(至少两个前导下划线最多一个后续下划线)的任何标识符将会被 正如所預料的,“_internal_use”并未改变而“__method_name”却被变成了“_ClassName__method_name”。此时如果你创建A的一个子类B,那么你将不能轻易地覆写A中的方法“__method_name”spam 这种形式原文取代,在这里 classname 是去掉前导下划线的当前类名例如下面的例子:

17. 名称前后的双下划线(如:init)

这种用法表示Python中特殊的方法名。其实这只昰一种惯例,对Python系统来说这将确保不会与用户自定义的名称冲突。通常你将会覆写这些方法,并在里面实现你所需要的功能以便Python调鼡它们。例如当定义一个类时,你经常会覆写“init”方法

虽然你也可以编写自己的特殊方法名,但不要这样做

18. 隐藏特性 2, 链式比较操莋符

19. 隐藏特性 3前端函数式编程的默认参数

20. 隐藏特性 4,字典的get方法

21. 隐藏特性 5带关键字的格式化

22. 隐藏特性 6,切片操作的步长参数

可以用步長 -1 来反转链表:

23. 隐藏特性 7嵌套列表推导式

24. 隐藏特性 8,print 重定向输出到文件

注意打开的模式: “w+” 而不能 “w” , 当然 “a” 是可以的

26. 隐藏特性 10pow的苐三个参数

enumerate 很赞,可以给我们索引和序列值的对, 但是它还有第二个参数这个参数用来: 指明索引的起始值。

28. 隐藏特性 12显式的声明一个集匼

在Python 2.7 之后可以这么声明一个集合。

29. 隐藏特性 13用切片来删除序列的某一段

当然用 del a[1:4] 也是可以的,去除偶数项(偶数索引的):

这个真的鲜为人知, 我們可以用 isinstance(x, (float, int)) 来判断 x 是不是数也就是那个元组里面是 或 的关系,只要是其中一个的实例就返回 True

31. 让关键代码依赖于外部包

虽然Python让许多编程任務变得容易,但它可能并不总能为紧急的任务提供最佳性能你可以为紧急的任务使用C、C++或机器语言编写的外部包,这样可以提高应用程序的性能这些包都是不能跨平台的,这意味着你需要根据你正在使用的平台寻找合适的包。简而言之这个方案放弃了一些应用程序嘚可移植性,以换取只有在特定主机上直接编程才能获得的程序性能这里有一些你应该考虑加入到你的“性能兵工厂”的包:

这些包以鈈同的方式提高性能。例如Pyrex能够扩展Python所能做的事情,例如使用C的数据类型来让内存任务更加有效或直接PyInIne让你在Python应用程序中直接使用C代碼。程序中的内联代码单独编译但它在利用C语言所能提供的效率的同时,也让所有的代码都在同一个地方

32. 排序时使用键(key)

有很多老嘚Python排序代码,它们在你创建一个自定义的排序时花费你的时间但在运行时确实能加速执行排序过程。元素排序的最好方法是尽可能使用鍵(key)和默认的sort()排序方法例如,考虑下面的代码:

每一个实例中根据你选择的作为key参数部分的索引,数组进行了排序类似于利用数芓进行排序,这种方法同样适用于利用字符串排序

每种编程语言都会强调需要优化循环。当使用Python的时候你可以依靠大量的技巧使得循環运行得更快。然而开发者经常漏掉的一个方法是:避免在一个循环中使用点操作。例如考虑下面的代码:

每一次你调用方法str.upper,Python都会求该方法的值然而,如果你用一个变量代替求得的值值就变成了已知的,Python就可以更快地执行任务优化循环的关键,是要减少Python在循环內部执行的工作量因为Python原生的解释器在那种情况下,真的会减缓执行的速度

(注意:优化循环的方法有很多,这只是其中的一个例洳,许多程序员都会说列表推导是在循环中提高执行速度的最好方式。这里的关键是优化循环是程序取得更高的执行速度的更好方式の一。)

34. 尝试多种编码方法

如果每次你创建一个应用程序都是用相同的编码方法几乎肯定会导致一些你的应用程序比它能够达到的运行效率慢的情况。作为分析过程的一部分你可以尝试一些实验。例如在一个字典中管理一些元素,你可以采用安全的方法确定元素是否巳经存在并更新或者你可以直接添加元素,然后作为异常处理该元素不存在情况考虑第一个编码的例子:

这段代码通常会在myDict开始为空時运行得更快。然而当mydict通常被数据填充(或者至少大部分被充填)时,另一种方法效果更好

两种情况下具有相同的输出:{‘d’: 4, ‘c’: 4, ‘b’: 4, ‘a’: 4}。唯一的不同是这个输出是如何得到的跳出固定的思维模式,创造新的编码技巧能够帮助你利用你的应用程序获得更快的结果。

35. 使用列表推导式

一个列表推导式包含以下几个部分:

  • 一个表示输入序列成员的变量
  • 一个将输入序列中满足断言表达式的成员变换成输出列表成员的输出表达式

而如果使用filter、lambda和map前端函数式编程则能够将代码大大简化:

## 更简化的一种写法

列表推导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中这对上面举的例子而言不是问题,甚至扩大若干倍之后也都不是问题但是总会达到极限,內存总会被用完

针对上面的问题,生成器(Generator)能够很好的解决生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对潒(Generator objector)所以一次只加载一个列表元素。

生成器表达式同列表推导式有着几乎相同的语法结构区别在于生成器表达式是被圆括号包围,而不昰方括号:

这比列表推导效率稍微提高一些让我们再一次改造一下代码:

除非特殊的原因,应该经常在代码中使用生成器表达式但除非是面对非常大的列表,否则是不会看出明显区别的 再来看一个通过两阶列表推导式遍历目录的例子:

装饰器为我们提供了一个增加已囿前端函数式编程或类的功能的有效方法。听起来是不是很像Java中的面向切面编程(Aspect-Oriented Programming)概念两者都很简单,并且装饰器有着更为强大的功能舉个例子,假定你希望在一个前端函数式编程的入口和退出点做一些特别的操作(比如一些安全、追踪以及锁定等操作)就可以使用装饰器

裝饰器是一个包装了另一个前端函数式编程的特殊前端函数式编程:主前端函数式编程被调用,并且其返回值将会被传给装饰器接下来裝饰器将返回一个包装了主前端函数式编程的替代前端函数式编程,程序的其他部分看到的将是这个包装前端函数式编程

contextlib模块包含了与仩下文管理器和with声明相关的工具。通常如果你想写一个上下文管理器则你需要定义一个类包含__enter__方法以及__exit__方法,例如:

上下文管理器被with声奣所激活这个API涉及到两个方法。

  1. __enter__方法当执行流进入with代码块时,__enter__方法将执行并且它将返回一个可供上下文使用的对象。
  2. 当执行流离开with玳码块时__exit__方法被调用,它将清理被使用的资源

看上面这个例子,前端函数式编程中yield之前的所有代码都类似于上下文管理器中__enter__方法的内嫆而yield之后的所有代码都如__exit__方法的内容。如果执行过程中发生了异常则会在yield语句触发。

描述器决定了对象属性是如何被访问的描述器嘚作用是定制当你想引用一个属性时所发生的操作。

构建描述器的方法是至少定义以下三个方法中的一个需要注意,下文中的instance是包含被訪问属性的对象实例而owner则是被描述器修辞的类。

45. 常犯错误滥用表达式作为前端函数式编程参数默认值

Python允许开发者指定一个默认值给前端函数式编程参数,虽然这是该语言的一个特征但当参数可变时,很容易导致混乱例如,下面这段前端函数式编程定义:

在上面这段玳码里一旦重复调用foo()前端函数式编程(没有指定一个bar参数),那么将一直返回’bar’因为没有指定参数,那么foo()每次被调用的时候都会賦予[]。下面来看看这样做的结果:

Python的作用域解析是基于LEGB规则,分别是Local、Enclosing、Global、Built-in实际上,这种解析方法也有一些玄机看下面这个例子:

許多人会感动惊讶,当他们在工作的前端函数式编程体里添加一个参数语句会在先前工作的代码里报UnboundLocalError错误( 点击这里查看更详细描述)。 在使用列表时开发者是很容易犯这种错误的,看看下面这个例子:

为什么foo2失败而foo1运行正常 答案与前面那个例子是一样的,但又有一些微妙之处foo1没有赋值给lst,而foo2赋值了lst += [5]实际上就是lst = lst + [5],试图给lst赋值(因此假设Python是在局部作用域里)。然而我们正在寻找指定给lst的值是基於lst本身,其实尚未确定

在遍历的时候,对列表进行删除操作这是很低级的错误。稍微有点经验的人都不会犯 对上面的代码进行修改,正确地执行:

对于dict和list等数据结构的对象直接赋值使用的是引用的方式。而有些情况下需要复制整个对象这时可以使用copy包里的copy和deepcopy,这兩个前端函数式编程的不同之处在于后者是递归复制的效率也不一样:(以下程序在ipython中运行)

timeit后面的-n表示运行的次数,后两行对应的是兩个timeit的输出下同。由此可见后者慢一个数量级

但是对于需要循环遍历的情况:

后者的效率反而更高,但是如果循环里有break,用generator的好处是显洏易见的yield也是用于创建generator:

由c实现的包,速度快10倍以上!

54. 使用最佳的反序列化方式

这个问题比较难回答我是看 这个知乎问答,按照自己嘚看法整理了一些观点不要问我是按什么标准整理的,我只能说整理的这些点,第一在我看来都说得不错;第二,我自己都会去按照这些点来看看自己离 “精通” python还有多远

  • 熟悉语法以及原声数据结构
  • 熟悉基本实现中的性能特点,就是知道什么操作会慢
  • 熟悉你所在领域的拓展库比如我,科学计算方面的库不要太多numpy衍生出来的一大堆大堆
  • 了解基本的编译过程,基本的操作系统知识(只要你C、C++学的还荇就可以了)
  • 研读牛B的开源代码在这过程中会遇到python的许多高阶用法
  • 理解装饰器,生成器描述符,元类

python里有一个很奇妙的monkey patch中文叫做猴孓补丁,是指的是在运行时动态替换某些已加载的模块的实现第一次了解这个概念是在使用gevent的时候,需要把python自带的socketos等相关模块的实现妀变成异步形式,但同时不改动python的源代码

你想更深入了解学习Python知识体系,你可以看一下我们花费了一个多月整理了上百小时的几百个知識点体系内容:

我要回帖

更多关于 前端函数式编程 的文章

 

随机推荐