基础篇
基本功
-
什么是面向对象
现实世界存在的任务事物都可以称之为对象,面向对象是对现实世界建模的一种方式,将一种抽象的思想给具体实例化,对象中包含的内容特征是它的属性,对象的一些操作行为是它的方法。例如简历就是一个对象,里面的内容是它的属性,看、拿、放都可以称为这个对象的方法。
-
面向对象的特征
封装:把描述一个对象的属性和行为封装成一个类,把具体的业务逻辑功能实现封装成一个方法,其次封装的意义还有效的保护属性通过访问修饰符私有化属性(成员变量),公有化方法。
继承:实现代码的复用,所有的子类所共有的行为和属性抽取为一个父类,所有的子类继承该类可具备父类的属性和行为,继承具有单一性和传递性。
多态:程序中定义的引用类型变量所指向的具体类型和调用的具体方法在程序编译阶段无法确定,而是在运行期才能确定该引用类型变量指向具体哪个对象而调用在哪个类中声明的方法。
多态的表现形式有强制类型转换,向上造型等,多态可分为行为多态和对象多态。
-
行为多态:同一个run( ){ }方法,不同的对象调用时会有不同的实现,猫调用时是跑,鱼调用时是游,鸟调用时是飞。
-
对象多态:同一个对象,可以被造型为不同的类型,比如同一个人对象,可以被造型为儿子,父亲,员工等。
-
-
面向对象的七大设计原则
1. 开闭原则(Open-Closed Principle, OCP):
软件实体应对扩展开放,对修改关闭。在不修改现有代码的基础上去扩展新功能。
2. 单一职责原则(Single Responsibility Principle):
定义一个类,应该只有一个职责。如果一个类有一个以上的职责,多个职责耦合在一起,会导致设计的局限性以及代码的复用性。
3. 里氏替换原则(Liskov Substitution Principle):
子类型必须能够替换掉它们的父类型。子类可以扩展父类的功能,但不能改变父类原有的功能。当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
4. 迪米特法则(Law Of Demeter):
迪米特法则又称为最少知道原则,即一个对象应该对其他对象保持最少的了解,只与直接的朋友通信。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。尽量减少类与类之间的耦合。软件编程的总原则:低耦合高内聚。迪米特法则的初衷就是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。
5. 依赖倒置原则(Dependence Inversion Principle)
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。依赖倒置原则就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
6. 接口隔离原则(Interface Segregation Principle)
客户端不应该依赖它不需要的接口;一个类对一个类的依赖应该建立在最小的接口上。
7. 合成/聚合原则(Composite/Aggregate Reuse Principle,CARP)
尽量使用合成/聚合,尽量不要使用继承。原则:一个类中有另一个的对象。
-
低耦合高内聚的理解
内聚:每个模块尽可能独立完成自己功能,不依赖与模块外部的代码。
耦合:模块与模块之间接口的复杂程度,模块之间联系越复杂耦合度越高,牵一发而动全身。
低耦合是指减少对其他类的依赖,高内聚是指调用的方法尽量写在本类中,对一个项目来说,就是减少第三方依赖。
复制代码
-
final、finally、finalize的区别
final:可以用来修饰类、方法和变量(成员变量或局部变量)
- 修饰类:当修饰类的时候,表面该类不能被继承,注意:final类中所有的成员方法都会隐式的定义为final方法。
- 修饰方法:把方法锁定,防止继承对其修改。final修饰过的方法不能被重写。注意:若父类中final方法权限为private,子类不能继承,若子类重新定义相同方法名的函数,会认为是子类中的一个新方法。
- 修饰变量:final成员变量表示常量。只能被赋值一次,赋值后其值不再改变。当final修饰一个基本数据类型时,表示该基本数据类型的值一旦初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。final修饰成员变量,必须要初始化。
finally:finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行。
以下这几种情况是不会被执行的:
- 调用了System.exit()方法
- JVM崩溃了(异常情况导致)
finalize:finalize()是在java.lang.Object理定义的,也就是每一个对象都有这么一个方法。这个对象在gc启动时,该对象被回收的时候被调用。其实gc可以回收大部分对象(凡是new出来的对象,gc都能搞定),所以一般不需要去实现finalize的。
-
int 和 Integer的区别
- Integer是int的包装类,int是java的一种基本数据类型。
- Integer变量必须实例化后才能使用,int变量不需要。
- Integer实际是对象的引用,当new一个Integer时,实际上是对一个指针指向此对象;而int则是直接存储数据值。
- Integer的默认值是null,int默认值是0.
public static void main(String[] args) { Integer i = 10; Integer j = 10; System.out.println(i == j); //true Integer a = 128; Integer b = 128; System.out.println(a == b); //false int k = 10; System.out.println(k == i); //true int kk = 128; System.out.println(kk == a); //true Integer m = new Integer(10); Integer n = new Integer(10); System.out.println(m == n); //false } 复制代码
-
重载和重写的区别
重载(Overloading):重载发生在本类,方法名相同,参数列表不同,与返回值无关,只和方法名、参数列表、参数的类型有关。
- 方法名必须相同
- 方法的参数列表一定不一样
- 访问修饰符和返回类型可以相同也可以不同
重写(Overriging):重写发生在父类和子类之间,一般表示父类和子类之间的关系。
- 方法名必须相同,返回类型必须相同
- 参数列表必须相同
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:父类的一个方法声明为public,那么子类重写后就不能声明为protected。
-
抽象类和接口的区别
抽象类:抽象类用abstract来修饰,抽象类是用来捕捉子类的通用性,它不能被实例化,只能用作子类的超类,抽象类是被用来创建继承层级里子类的模版。
接口:接口是抽象方法的集合,如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法,就像契约模式,如果实现了这个接口,那么久必须保证使用这些方法,并且实现这些方法,接口是一种形式,接口自身不能做任何事情,接口里面的方法默认都是abstract的。
使用场景:
- 如果拥有一些方法,并想让他们中的一些有默认的具体实现,请选择抽象类。
- 如果想实现多重继承,那么请使用接口,由于java不支持多继承,子类不能继承多个类,但一个类可以实现多个接口,因此可以使用接口来解决。
- 如果基本功能在不断变化,那么久使用抽象类,如果使用接口,那么每次变更都需要相应的去改变实现该接口的所有类。
参数 抽象类 接口 默认的方法实现 可以有默认的方法实现 完全抽象,根本不存在方法实现 实现方式 子类用extends关键字来继承抽象类,如果子类不是抽象类的话, 它需要实现父类抽象类中所有抽象方法,父类中非抽象方法可重写也可不重写
子类用implements去实现接口,需要实现接口中所有方法 构造器 抽象类可以有构造器(构造器不能用abstract修饰) 接口不能有构造器 与正常Java类区别 正常Java类可被实例化,抽象类不能被实例化 接口和正常java类是不同的类型 访问修饰符 抽象方法可以用public、protected、default修饰 接口默认是public、不能用别的修饰符去修饰 main方法 抽象类可以有main方法,可以运行 接口中不能有main方法 多继承 抽象类可继承一个类和实现多个接口 接口能继承一个或多个接口 -
说说反射的用途及实现
反射:反射的核心是JVM在运行时才动态加载或调用方法/访问属性,它不需要事先(写代码或者编译期)知道运行对象是谁。注意:由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么久不需要反射。
反射框架提供的功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
反射的用途:反射最重要的用途就是开发各种通用框架。
反射的基本实现:获得Class对象、判断是否为某个类的实例、创建实例、获取方法、获取构造器信息、获取类的成员变量、调用方法、利用反射创建数组。
-
说说自定义注解的场景及实现
java中有四种元注解:
@Retention:定义该注解的生命周期(什么时候使用该注解)
RetentionPolicy.RUNTIME:始终不会丢弃,运行期也保留注解,因此可以使用反射机制读取该注解的信息。自定义注解时通常使用这种方式。
@Inherited:定义该注释和子类的关系(是否允许子类继承该注解),如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Documented:一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中(注解是否将包含在JavaDoc中)
@Target:表示该注解用在什么地方(注解用于什么地方)
ElementType.TYPE用于描述类、接口(包括注解类型)或enum声明。
@Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Target({ElementType.FIELD,ElementType.METHOD}) @interface MyAnno{ public String name() default "zhangsan"; public String email() default "[email protected]"; } //创建用户类 class User{ @MyAnno(name = "zhang") private String name; @MyAnno(name = "[email protected]") private String email; @MyAnno(name = "sayHelloWorld") public String sayHello(){ return ""; } 复制代码
-
HTTP请求的GET和POST方式的区别
Get:一般用于获取、查询资源信息
POST:一般用于更新资源信息
Get请求 POST请求 请求参数会显示在地址栏中 请求参数不会显示在地址栏中 请求体是空 请求体是请求参数 请求参数显示在请求首行中(GET/name=aaa) 请求参数不显示在请求首行中(POST/ ) 请求参数显示地址栏中【不安全】 地址栏不显示请求参数【相对安全】 请求参数放首行,http对请求首行显示1kb 请求参数放请求体,请求体没有大小限制 请求参数不能通过request.setCharacterEncoding("gbk")来设置编码 request.setCharacterEncoding("gbk")只能设置请求体的编码集 -
session和cookie区别
- session存储在服务器端,cookie存储在客户端(浏览器)。
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
- 单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie。
- 可以考虑将登陆信息等重要信息存放为session,其他信息如需保留,可以放在cookie中。
-
session分布式处理
- 客户端cookie加密。(一般用于内网中企业级的系统中,要求用户浏览器端的cookie不能禁用,禁用的话,该方案会失效)。
- 集群中,各个应用服务器提供了session复制的功能,Tomcat和Jboss都实现了这样的功能。特定:性能随着服务器增加急剧下降,容易引起广播风暴;session数据需要序列化,影响性能。
- session的持久化,使用数据库来保存session。就算宕机了也没有事,数据库的session照样存在。特点:每次请求session都要读写数据库,会带来性能开销。使用内存数据库,会提高性能,但是宕机会丢失数据(像支付宝的宕机,有同城灾备,异地灾备)。
- 使用共享存储来保存session。和数据库类似,就算宕机了也没事。其实就是专门搞一台服务器,全部对session落地。特定:频繁的进行序列化和反序列化会影响性能。
- 使用memcached来保存session。本质上是内存数据库的解决方案。特点:存入memcached的数据需要序列化,效率极低。
-
JDBC流程
- 注册驱动程序:Class.forName("com.mysql.jdbc.Driver");
- 使用驱动管理类来获取数据连接对象:conn = DriverManager.getConnection(...);
- 获取数据库操作对象:Statement state = conn.createStatement();
- 定义操作的SQl语句
- 执行state.executeQuery(sql);
- 处理结果集:ResultSet,如果SQl前有参数值就设置参数值setXXX()
- 关闭对象,回收数据库资源(关闭结果集->关闭数据库操作对象->关闭连接)
public class JDBCTest {
/**
* 使用JDBC连接并操作mysql数据库
*/
public static void main(String[] args) {
// 数据库驱动类名的字符串
String driver = "com.mysql.jdbc.Driver";
// 数据库连接串
String url = "jdbc:mysql://127.0.0.1:3306/jdbctest";
// 用户名
String username = "root";
// 密码
String password = "1234";
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1、加载数据库驱动( 成功加载后,会将Driver类的实例注册到DriverManager类中)
Class.forName(driver);
// 2、获取数据库连接
conn = DriverManager.getConnection(url, username, password);
// 3、获取数据库操作对象
stmt = conn.createStatement();
// 4、定义操作的SQL语句
String sql = "select * from user where id = 100";
// 5、执行数据库操作
rs = stmt.executeQuery(sql);
// 6、获取并操作结果集
while (rs.next()) {
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7、关闭对象,回收数据库资源
if (rs != null) { //关闭结果集对象
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) { // 关闭数据库操作对象
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) { // 关闭数据库连接对象
try {
if (!conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
复制代码
-
MVC设计思想
Model(模型),是程序的主体部分,主要包含业务数据和业务逻辑。在模型层,还会涉及到用户发布的服务,在服务中会根据不同的业务需求,更新业务模型中的数据。
View(视图),是程序呈现给用户的部分,是用户和程序交互的接口,用户会根据具体的业务需求,在View视图层输入自己特定的业务数据,并通过界面的事件交互
将对应的输入参数提交给后台控制器进行处理。
Controller(控制器),Controller是用来处理用户输入数据,以及更新业务模型的部分。控制器中接收了用户与界面交互时传递过来的数据,并根据数据业务逻辑来
执行服务的调用和更新业务模型的数据和状态。
MVC架构的控制流程
1.所有的终端用户请求被发送到控制器。
2.控制器依赖请求去选择加载哪个模块,并把模型附加到对应的视图。
3.附加了模型数据的最终视图做为相应发送给终端用户。
复制代码
-
equals与==的区别
比较基本数据类型时,只能采用==,比较的是数值
当比较引用数据类型时,==比较的是引用地址,而equals其实也是,equals是Object定义的方法,而其默认的显示也是比较地址。我们常用的String类型,因为重写了equals方法,其内部比较的是内容。