JAVA 小知识
1.以下程序执行的结果是:
class X{ Y y=new Y(); public X(){ System.out.print("X"); } } class Y{ public Y(){ System.out.print("Y"); } } public class Z extends X{ Y y=new Y(); public Z(){ System.out.print("Z"); } public static void main(String[] args) { new Z(); } }
正确答案 :YXYZ
初始化过程: 1. 初始化父类中的静态成员变量和静态代码块 ; 2. 初始化子类中的静态成员变量和静态代码块 ; 3.初始化父类的普通成员变量和代码块,再执行父类的构造方法; 4.初始化子类的普通成员变量和代码块,再执行子类的构造方法; (1)初始化父类的普通成员变量和代码块,执行 Y y=new Y(); 输出Y (2)再执行父类的构造方法;输出X (3) 初始化子类的普通成员变量和代码块,执行 Y y=new Y(); 输出Y (4)再执行子类的构造方法;输出Z 所以输出YXYZ
构造方法作用就是对类进行初始化
静态代码块 java虚拟机加载类时,就会执行该块代码,并且只执行一次
程序运行时,静态初始化块首先被执行。然后执行普通初始化块。最后,执行构造方法。静态初始化块只在类加载的时候执行一次,因此第二次创建对象的时候静态初始化块并没有被执行。
package cn.edu.aynu; public class Z extends X{ Y y=new Y(); public Z(){ System.out.println("执行Z的构造方法"); System.out.println("Z"); } public static void main(String[] args) { System.out.println("执行Z的主方法"); new Z(); } } ============================== package cn.edu.aynu; public class Y { public Y(){ System.out.println("执行Y的构造方法"); System.out.println("Y"); } } ============================== package cn.edu.aynu; public class X { Y y=new Y(); public X(){ System.out.println("执行X的构造方法"); System.out.println("X"); } } ================================ 执行结果 执行Z的主方法 执行Y的构造方法 Y 执行X的构造方法 X 执行Y的构造方法 Y 执行Z的构造方法 Z
第二题:做到这题真是感觉我是白学了java了吗?我还真是一个蚂蚱
public static void main(String []args) { String s = "hello"; String t = "hello"; char c[] = {'h','e','l','l','o'} ; System.out.println(s.equals(t)); System.out.println(t.equals(c)); System.out.println(s==t); System.out.println(t.equals(new String("hello"))); //这个不相等,因为语句中new的字符串不在常量池,是在堆 System.out.println(t==new String("hello")); //这样可以判断字符数组与字符串是否包含同样的字符序列 System.out.println(t.equals(new String(c))); }
equals 是对字符串内容的比较,JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池。代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。所以返回true。
“==”是对引用地址的比较,同上也属于常量池的同一个字符串地址
而常量池中的字符串,只有变量名不同是可以用双等号判断是否相等的,内存都是常量池中的字符串。
但是new出来的字符串,只能用equals,用双等号是不相等的,因为是两个内存对象。
第三题:
第四题:
使用transient修饰的变量不会被序列化
对象序列化的所属类需要实现Serializable接口
依赖注入目的是减少组件之间的耦合度,使开发变得简单。
执行结果
is 1005 105 is is105
在java中,“+” 和 “+=” 是经过重载的运算符,而java不允许程序员进行运算符的重载。如果 “+” 之前是String,那么此时,“+” 的作用就是连接两个字符串;若此时 “+” 后面是基本数据类型的话,可以直接进行连接,若是引用数据类型的话,则会调用该对象的toString()方法。
面向对象的五大基本原则
单一职责原则(SRP)
开放封闭原则(OCP)
里氏替换原则(LSP)
依赖倒置原则(DIP)
接口隔离原则(ISP)
面向对象的,它的三大特性,封装、继承、多态
面向对象设计模式有5大基本原则:单一职责原则、开发封闭原则、依赖倒置原则、接口隔离原则、Liskov替换原则。
而设计模式都是在面向对象的特性以及5大基本原则的基础上衍生而来的具体实现
1、单一职责原则(SRP): 1.1,SRP(Single Responsibilities Principle)的定义:就一个类而言,应该仅有一个引起它变化的原因。简而言之,就是功能要单一。 1.2,如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。(敏捷软件开发) 1.3,软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。(敏捷软件开发) 小结: 单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这样导致职责依赖,相互之间就会产生原因,大大损伤其内聚性和耦合度。 2、开放-封闭原则(OCP): 2.1,OCP(Open-Close Principle)的定义:就是说软件实体(类,方法等等)应该可以扩展,但是不能修改。它是软件设计中也是最重要的一种设计原则。 2.2,OCP的两个特征: 2.2.1> 对于扩展是开放的。 2.2.2> 对于修改是封闭的。 2.3,什么时候应用OCP原则呢? 在我们最初编写代码时,假设变化不会发生,当变化发生时,我们就创建抽象(比如抽象类,接口等等)来隔离以后发生的同类变化。 2.4,开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护,可扩展,可复用,灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。 2.5,OCP的UML图: 小结: 开放封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处:可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样也不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。“需求总是变化”没有不变的软件,所以需要用开放封闭原则来封闭变化满足需求,同时还能保持软件内部的封装体系稳定,不被需求的变化影响。 3、依赖倒转原则(DIP): 3.1,DIP(Dependence Inversion Principle)的定义:抽象不应该依赖细节,细节应该依赖于抽象。简单说就是,我们要针对接口编程,而不要针对实现编程。 3.1. 1 高层模块不应该依赖低层模块。两个都应该依赖抽象。 3.1.2 抽象不应该依赖具体(细节)。具体(细节)应该依赖抽象。 3.2、反面例子UML图: 缺点:高层模块太依赖低层模块,耦合太紧密。低层模块发生变化会影响到高层模块。 解决方法:利用依赖倒置原则使高层模块和低层模块都依赖于抽象(接口或抽象类)。 3.3、修改后的UML图如下: 优点:这样的话修改低层模块不会影响到高层模块,减小了它们之间的耦合度,增强系统的稳定性。 小结: 依赖倒置原则其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。 4、接口隔离原则: 使用多个专门的接口比使用单一的总接口要好。 一个类对另外一个类的依赖性应当是建立在最小的接口上的。 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。 “不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。 小结: 接口隔离的方法有两种(分享客户就是分离接口): 1、使用委托(此委托非.net委托[delegate])分离接口 使用委托即,创建一个委托类,用此类去实现分离后的其它接口中的方法。 2、使用多重继承分离接口、 此方法,即将现有“胖”接口分成供不同客户程序调用的两个或多个接口,而需要实现多个接口的客户程序,则使用多重继承来实现。 5、Liskov(里氏)替换原则(LSP): 5.1,LSP(Liskov Substitution Principle)的定义:子类型必须能够替换掉它们的父类型。简单地说,这是因为子类型继承了父类,所以子类可以以父类的身份出现。 实例UML图: C#代码: View Code 小结: 任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 补充: 迪米特法则(LoD): 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。 迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。 1,LoD(Law of Demeter)的定义:如果两个类不必彼此直接通信,那么这两个类就不应当直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 2,在类的结构设计上,每一个类都应当尽量降低成员的访问权限,也就是说,一个类包装好自己的private状态,不需要让别的类知道的字段或行为(方法)就尽量不要公开。 定义:一个对象应该对其他对象保持最少的了解。 问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 解决方案:尽量降低类与类之间的耦合。 举例:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。 C#代码如下: View Code 现在这个设计的主要问题出在CompanyManager中,根据迪米特法则,只与直接的朋友发生通信,而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。 修改后的C#代码如下: View Code 修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。 小结: 迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
==============================================
Servlet的生命周期一般可以用三个方法来表示:
- init():仅执行一次,负责在装载Servlet时初始化Servlet对象
- service() :核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
- destory():在停止并且卸载Servlet时执行,负责释放资源
初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。所以选ACD。B是在调用service方法时才构造的
运行时异常故名思议就是程序在运行的时候出现异常,隐含的一个前提就是程序在编译时是检测不到异常的存在,作者本人也并不知道是否自己的代码中含有运行时异常,所以根本也不可能提前使用try{}catch{}捕获.
List 复制之 浅拷贝与深拷贝
详细连接https://blog.csdn.net/never_tears/article/details/79067245
java中判断字符串是否为数字的方法的几种方法
https://blog.csdn.net/u013066244/article/details/53197756
java 基本类型详解 及 常见问题
https://blog.csdn.net/sunshoupo211/article/details/23001137
浮点数在计算机中存储方式
http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html