Java DataBase Connectivity是一种用于执行SQL语句的Java API,它由一组用Java语言编写的类和接口组成。通过这些类和接口,JDBC把SQL语句发送给不同类型的数据库进行处理并接收处理结果。
提供java 操作不同数据库的技术
对Java开发人员而言是API,对数据库提供商而言是接口。
面向开发人员:作为API,JDBC为程序开发提供标准的接口。
面向数据库厂商:作为接口,让数据库厂商按标准方法来实现数据库连接与操作(数据库驱动程序)。
下载jarhttps://blog.csdn.net/qq_35368140/article/details/131246017
引入jarhttps://blog.csdn.net/qq_43599841/article/details/127368168%5D
JDBC 使用步骤
? 1. 加载驱动 (只会运行一次:static)
? 2. 获得链接对象 Connection
? 3. 获得执行对象 Statement
? 4. 执行sql并获得结果集(ResultSet)
? 5. 处理结果集
6.释放资源
public static void main(String[] args) throws SQLException {
//1.注册驱动 告诉jdbc我们使用哪一个数据库厂商的驱动
//驱动管理器专门注册驱动(需要传递一个驱动对象)
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2. 建立驱动连接
//url:链接数据库的地址 jdbc:mysql://localhost:3306/数据库的名字
//user:用户名
//password:密码
//面向接口编程
Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/j352", "root", "");
//3.创建向数据库发送sql的statement对象
Statement st = connection.createStatement();
//4.发送sql后获得一个封装了查询结果的ResultSet对象
ResultSet rs = st.executeQuery("select * from student limit 1");
//5.解析ResultSet对象获得结果
if(rs.next()) {
System.out.println("id:"+rs.getObject("Id"));
System.out.println("name:"+rs.getObject("StudentName"));
System.out.println("age:"+rs.getObject("age"));
}
//释放资源
rs.close();
st.close();
connection.close();
}
DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用
原因有2个:
>导致驱动被注册2次。
>强烈依赖数据库的驱动jar
解决办法:
注册驱动的第二种方式
Class.forName(“com.mysql.jdbc.Driver”);
static Connection getConnection(String url, String user, String password)
试图建立到给定数据库 URL 的连接。
参数说明:url 需要连接数据库的位置(网址) user用户名 password 密码
例如:getConnection("jdbc:mysql://localhost:3306/数据库名称", "root", "");
URL:SUN公司与数据库厂商之间的一种协议。
jdbc:mysql://localhost:3306/数据库名称
协议 子协议 IP : 端口号 数据库
常用数据库URL地址的写法:
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql jdbc:mysql://localhost:3306/sid
Mysql的url地址的简写形式: jdbc:mysql:///sid
常用属性:useUnicode=true&characterEncoding=UTF-8
接口的实现在数据库驱动中。所有与数据库交互都是基于连接对象的。
Statement createStatement(); //创建操作sql语句的对象
String sql = “某SQL语句”;
获取Statement语句执行平台:Statement stmt = con.createStatement();
常用方法:
int executeUpdate(String sql); --执行insert update delete语句.
ResultSet executeQuery(String sql); --执行select语句.
boolean execute(String sql); --仅当执行select并且有结果时才返回true,执行其他的语句返回false.
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取链接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","root");
//3.获取代表向数据库发送sql的statement对象
Statement st = conn.createStatement();
String sql="insert into student(name,age) values('奥利给','20')";
//4.发送sql
//返回影响的行数 大于0代表执行成功
int result = st.executeUpdate(sql);
if(result>0) {
System.out.println("插入成功");
}
//5.释放资源
st.close();
conn.close();
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法
时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1
开始)来获取指定列的数据:
rs.next();//指向第下一行
rs.getObject(1);//获取第一行第一列的数据
常用方法:
n Object getObject(int index) / Object getObject(String name) 获得任意对象
n String getString(int index)/ String getString(String name) 获得字符串
n int getInt(int index)/int getInt(String name) 获得整形
n double getDouble(int index)/ double getDouble(String name) 获得双精度浮点型
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close(); //结果集
stmt.close(); //statement对象
con.close(); //连接对象
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取链接
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
//2.获取链接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","root");
//3.获取代表向数据库发送sql的statement对象
st = conn.createStatement();
String sql="select id,name,age from student";
//4.发送sql
rs = st.executeQuery(sql);
//定义集合封装Student数据
List<Student> list=new ArrayList<Student>();
while(rs.next()) {
//查询出来的结果封装到对象中
Student s=new Student();
s.setId(rs.getInt("id"));
s.setAge(rs.getInt("age"));
s.setName(rs.getString("name"));
list.add(s);
}
System.out.println(list);
}finally {
//5.释放资源
//关闭资源之前一定要判断
if(rs!=null) {
try {
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
//让jvm回收没有被关闭的rs对象
rs=null;
}
if(st!=null) {
try {
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st=null;
}
if(conn!=null) {
try {
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
conn=null;
}
}
}
}
步骤1,步骤2:
public class JdbcUtils {
static {
//1.注册驱动 告诉jdbc我们使用哪一个数据库厂商的驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
// 项目一旦交付
// 公司(乙) 做项目(产品) =》 给别人做项目(甲)
// 部署(运维)-》 配置文件
public static Connection GetConnection() throws ClassNotFoundException, SQLException {
//2.通过驱动管理器获取一个链接
Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/j352", "root", "");
return connection;
}
public static void release(Connection conn, Statement sm , ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs =null; //让jvm来回收它
}
if(sm!=null){
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
sm =null; //让jvm来回收它
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn =null; //让jvm来回收它
}
}
}
步骤3: 将数据库连接url, 用户名,密码,放到文件中,把它变成可以配置的
使用properties后的代码
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/j352
username=root
password=
public class JdbcUtils {
private static Properties properties = new Properties();
static {
//1.注册驱动 告诉jdbc我们使用哪一个数据库厂商的驱动
try {
//1.1 读取文件中的信息
FileInputStream in = null;
try {
in = new FileInputStream("src\\jdbc.properties");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
// 1.2 Properties对象中有一个load方法
properties.load(in); //将文件相关的信息加载到properties 对象中
Class.forName(properties.getProperty("driverClassName"));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 项目一旦交付
// 公司(乙) 做项目(产品) =》 给别人做项目(甲)
// 部署(运维)-》 配置文件
public static Connection GetConnection() throws ClassNotFoundException, SQLException, IOException {
// 读取文件中的数据 jdbc.properties ,进行使用
//String getProperty(String key)
//2.通过驱动管理器获取一个链接
Connection connection = (Connection) DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("username"), properties.getProperty("password"));
return connection;
}
public static void release(Connection conn, Statement sm , ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs =null; //让jvm来回收它
}
if(sm!=null){
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
sm =null; //让jvm来回收它
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn =null; //让jvm来回收它
}
}
}
如果执行的sql语句,是用字符串进行拼接, 就容易出sql 注入
输入时,不让它输入特殊字符 ,正则(只输入字母或数字 )
执行sql时不用sql拼接,而使用参数化查询
public int login(String username, String pwd) {
int result = 0;
Connection conn = null; //创建连接
// Statement statement = null;
PreparedStatement pst = null;
ResultSet resultSet = null;
try {
conn = JdbcUtils.GetConnection();
// 查询
String sql = "select * from user where username=? and pwd=? ";
pst = conn.prepareStatement(sql);
pst.setString(1,username);
pst.setString(2,pwd);
System.out.println(sql);
resultSet = pst.executeQuery(sql);
if (resultSet.next()) {
result = 1;
}
} catch (ClassNotFoundException e) {
result = 2;
e.printStackTrace();
// throw new RuntimeException(e);
} catch (SQLException e) {
result = 3;
e.printStackTrace();
} catch (IOException e) {
result = 4;
e.printStackTrace();
}finally {
JdbcUtils.release(conn,pst,resultSet);
}
return result;
}
conn.setAutoCommit(false) == start transaction;
conn.commit() == commit;
conn.rollback(); == rollback;
public class TransactionDemo {
public static void main(String[] args) {
// 1. 获取连接
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try{
connection = JdbcUtils.GetConnection();
//2. 事务开启
connection.setAutoCommit(false);
String sql = "update user set age = 18 where username='admin'";
statement = connection.prepareStatement(sql);
statement.executeUpdate(); //执行sql
// int x= 1/ 0 ;
String sql2 = "update user set age = 28 where username='doubleyong'";
statement = connection.prepareStatement(sql2);
statement.executeUpdate(); //执行sql
// 3。提交事务
connection.commit();
}catch (Exception e){
//关闭事务
try {
connection.rollback();
// connection.commit();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
e.printStackTrace();
}
finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
回滚到指定位置
savepoint = conn.setSavepoint();
public static void main(String[] args) {
// 1. 获取连接
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
Savepoint savepoint=null;
try{
connection = JdbcUtils.GetConnection();
//2. 事务开启
connection.setAutoCommit(false);
String sql = "update user set age = 66 where username='admin'";
statement = connection.prepareStatement(sql);
statement.executeUpdate(); //执行sql
//设置一个事务的回滚点
savepoint = connection.setSavepoint();
int x= 1/ 0 ;
String sql2 = "update user set age = 77 where username='doubleyong'";
statement = connection.prepareStatement(sql2);
statement.executeUpdate(); //执行sql
// 3。提交事务
connection.commit();
}catch (Exception e){
//关闭事务
try {
connection.rollback(savepoint); //回滚到指定事物点
connection.commit(); //回滚后,如何 还是有sql执行,必须 加上commit
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
e.printStackTrace();
}
finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
反射: 加载类,反射出类的各个组成部分 构造方法,属性(非静态属性和静态属性),方法(非静态方法,静态方法)
java 反射机制: 是在运行状态中(Class对象), 对于任何类,都能能够知道这个类的所有的属性和方法; 对于任意一个对象,能够调用它的任意属性和方法;这种动态获取信息的方式,就称之反射.
当程序要使用某个类时,如果这个类还没有加载内存中,则系统会通过加载,连接,初始化三个步骤来实现对这个类的初始化。
加载 :
? 将class 文件加载到内存中,并为之创建一个Class对象(将class文件的内容放到一个对象中,而对象的名字刚好就是Class)
? 任何类被使用时,系统都会建立一个Class对象.
连接
? 验证: 是否有正确的内部结构 ,并和其它协调一致
? 准备 : 负责为类的静态成员分配内存,并设置默认初始化值
? 解析: 将类的二进制数据中的符号引用替换成直接引用
初始化
? 就是之前的初始化步骤
创建类的实例
访问类的静态变量或给静态变量赋值
调用类的静态方法
初始化某个类的子类
java命令,运行某个类
使用反射方式强制创建某个类或接口对应的Class对象
负责将class 文件加载到内存中,并为之创建一个Class对象,如果了解类加载器的机制,可以的更好的理解程序的运行
类加载器的组成:
根类加载器: bootstrap classLoader
? 也被称为引导类加载类,负责Java核心类的加载
? 比如: System, String 等,在 JDK 中的JRE 中 lib 中的 rt.jar文件中
扩展类加载器: extension classLoader
? 负责jre的扩展目录中的jar的加载
系统类加载器: System classLoader
? 负责在JVM启动时加载来自java 命令的class文件
开发: 使用第三种.
? 为什么? 因为第三种是一个字符 串,而不是具体类名,这种的话就可以将这个值放到配置文件中,方便对它修改.
?
//方法1:
Person p = new Person();
Class c = p.getClass();
Person p1 = new Person();
Class c1 = p.getClass();
System.out.println(p==p1); //false
System.out.println(c==c1); //true
//方法2:
Class c2 = Person.class;
System.out.println(c == c2); //true
// 方法3
// Class对象中的静态方法
//ClassNotFoundException 必须写包名
Class c3 = Class.forName("com.demo1.Person");
System.out.println(c==c3);
//1. 如何获取 类的加载器
//1.1 首先得到Class对象
Class c = Person.class;
//1.2 获取类的加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println(classLoader);
注:小心路径问题,最好是将方法放到 resource 目录下
https://blog.csdn.net/weixin_48052161/article/details/115151874
Class c = User.class;
//1.2 获取类的加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println(classLoader);
// 类加载器加载其它的文件
InputStream in = classLoader.getResourceAsStream("jdbc.properties");
System.out.println(in);
类加载是Java虚拟机(JVM)对类文件进行加载、验证、准备、解析和初始化等一系列操作的过程。类加载的原理包括以下几个步骤:
1.加载:在类加载过程中,JVM会根据类的全限定名来获取类的字节码文件,并将其读取到内存中。
2.验证:在验证阶段,JVM会对字节码文件进行验证,确保其符合Java语言规范和安全要求。验证包括文件格式验证、元数据验证、字节码验证和符号引用验证等。
3.准备:在准备阶段,JVM会为类的静态变量分配内存,并设置默认初始值(例如0、null等)。
4.解析:在解析阶段,JVM会将类的符号引用转换为直接引用,以便于后续的内存访问。解析过程包括类、接口、字段和方法等的解析。
5.初始化:在初始化阶段,JVM会执行类的初始化代码,包括静态变量的赋值和静态代码块的执行。在这个阶段,类的初始化按照严格的顺序进行,并且只会执行一次。
类加载过程是按需进行的,即当程序需要使用某个类时,JVM才会进行相应的类加载操作。同时,JVM还采用了双亲委派模型来进行类加载,即先委派给父类加载器尝试加载,只有在父类加载器无法加载时,才由子类加载器尝试加载。
通过类加载的原理,Java实现了动态扩展和灵活的类加载机制,使得开发人员可以根据需要动态加载和使用类,实现了面向对象编程的核心特性之一:封装和复用
先做了解,第四阶段着重讲
反射构造器: 通过Class对象,获取构造方法
Class c = Person.class;
//获取构造方法
// Constructor<?>[] getConstructors() : 返回所有的构造方法 Constructor 类的对象 只能获取 public修饰的构造方法
//返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
// Constructor[] constructors = c.getConstructors();
//c.getDeclaredConstructors(); 获取 所有构造方法
// Constructor[] constructors = c.getDeclaredConstructors();
// for(Constructor constructor:constructors){
// System.out.println(constructor);
// }
//获取单个构造方法
// getConstructor(类<?>... parameterTypes): 只能得到public修饰
// Constructor constructor = c.getConstructor(String.class);
Constructor constructor = c.getDeclaredConstructor(String.class);
System.out.println(constructor);
//Constructor<T> getConstructor(类<?>... parameterTypes)
//返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
// 根据构造方法,创建实例对象
//java.lang.IllegalAccessException 构造方法是私有的,创建实现,会报非法异常
// 解决方案:
constructor.setAccessible(true); //跳过java语法检查
Object obj = constructor.newInstance("张三");
System.out.println(obj);
反射构造器: 通过Class对象,获取成员属性
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class c = Person.class;
//public Field[] getFields() 返回包含一个数组Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
// Field[] fields = c.getFields();
// for(Field field : fields){
// System.out.println(field);
// }
//public Field[] getDeclaredFields() : 获取所有属性
// Field[] fields = c.getDeclaredFields();
// for(Field field : fields){
// System.out.println(field);
// }
//NoSuchFieldException
// Field field= c.getField("name");
// System.out.println(field);
// Field field= c.getDeclaredField("name");
// System.out.println(field);
// 没有对象就没有属性
// 要使用属性,必须 要有对象
Constructor constructor = c.getDeclaredConstructor();
Object obj = constructor.newInstance(); //创建对应的对象
Field namefield = c.getDeclaredField("name");
Field agefield = c.getDeclaredField("age");
Field addressfield = c.getDeclaredField("address");
//IllegalAccessException
namefield.setAccessible(true);
namefield.set(obj,"唐姐");
agefield.set(obj,18);
addressfield.set(obj,"成都");
System.out.println(obj);
}
反射构造器: 通过Class对象,获取成员方法
封装了两个方法: 增删改 excuteUpdate, 查询 excuteQuery
public class JdbcUntils {
static {
//1.注册驱动 告诉jdbc我们使用哪一个数据库厂商的驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
//写一个jdbc 工具类
Connection connection = null;
PreparedStatement st = null;
ResultSet rs = null;
//分析:
// 当前方法写完了,发现sql 语句是不是直接传过来,可以如果 有参数,直接进行sql拼接会产生sql注入风险
// 所以,就考虑使用参数化
// 将数据,传过来,传来后,使用PreparedStatement添加参数
public int excuteUpdate(String sql,Object... params) {
int result = -1;
try {
//2. 创建连接
connection = getConnection();
//3. 创建statement对象
//这个里包括了? ,所以需要参数指定
st = connection.prepareStatement(sql);
//要把参数加到PreparedStatement对象中
//pst.setString(1,username);
// pst.setString(2,pwd);
for (int i = 0; i < params.length; i++) {
st.setObject((i + 1), params[i]);
}
result = st.executeUpdate();
//4. release 释放资源
release();
}catch (SQLException e){
e.printStackTrace();
result = -1;
}finally {
return result;
}
}
public <T> List<T> excuteQuery(String sql,Class<T> c,Object... prarms){
//创建一个集合,存放所有的结果对象
List<T> list = new ArrayList<>();
try {
connection = getConnection();
// 3. 获得执行对象 Statement
// 查询 里的sql语句也可以能有参数
st = connection.prepareStatement(sql);
//添加参数
for (int i = 0; i < prarms.length; i++) {
st.setObject((i + 1), prarms[i]);
}
// 4. 执行sql并获得结果集(ResultSet)
rs = st.executeQuery();
// 4.1 得到结果集的元数据
ResultSetMetaData ms = rs.getMetaData();
//列的数量
int colCount = ms.getColumnCount();
// 处理结果
while (rs.next()){
// int i = 1;
//添加一个 T类的实例
T t = c.getDeclaredConstructor().newInstance();
// 1. 得到结果集,列名数组
//2. 循环列名
// 循环体里,根据列名,去找对象的对应的字段 ,然后在进行赋值
for(int i=1;i<=colCount;i++){
Object value = rs.getObject(i);
if(value!=null){
//将这对应的值,放到对象对应的字段中
String colName = ms.getColumnName(i);
//通过反射,设置字段的值
//要求结果的列名,与实体对象的属性(字段名)相同
Field field = c.getDeclaredField(colName);
// 跳出java的语法检查
field.setAccessible(true);
// 给字段设置对应的值
field.set(t,value);
}
}
list.add(t);
//给实例对象的每个属性,赋值
// 方法一: 获取所有字段进行赋值,这个要求字段与数据的列的顺序要求一致
// Field[] Fields = c.getDeclaredFields(); //获取所有的字段
// for(Field field : Fields){
// //缺点: 实体的字段 与数据查询 出来的字段要一一对应
// //思路: 如果不对应怎么?
// // 最好,可以得到结果集的列名,根据列名,给对应的字段赋值
//
// // 这里值 ,应该从结果集
// field.set(t,rs.getObject(i++));
// }
//T对象加入到list中
}
// 5. 处理结果集
release();
}catch (Exception e){
e.printStackTrace();
list = null;
} finally {
return list;
}
}
//创建连接
public Connection getConnection() throws SQLException {
//2.通过驱动管理器获取一个链接
Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/j352", "root", "");
return connection;
}
//释放资源
public void release(){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs =null; //让jvm来回收它
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st =null; //让jvm来回收它
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
connection =null; //让jvm来回收它
}
}
}
1)创建软件应用程序是为了满足不断变化和发展的需求。一个成功的应用程序还应该提供一种简单的方法来扩展它以满足不断变化的期望。如果在设计和开发软件时应用一组面向对象的设计原则和模式,则可以避免或解决这些常见问题。
2)面向对象的设计原则也被称为 SOLID 。在设计和开发软件时可以应用这些原则,以便创建易于维护和开发的程序。
SOLID 原则包括,单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。
专业的人做专业的事。每个类只负责做一件事
单一职责原则,其它就是“高内聚”的体现。
每个类只负责做一件事, 对外只提供一个功能,而引起类的变化 的原因应该只有一个。
核心思想:一个对象 对扩展开放,对修改关闭
对类的改动是通过增加代码进行,而不是修改代码
如何实现?这就需要借助于抽象 和多态。即把可能变化 的内容抽象 出来.
因为抽象的部分是相对稳定
子类可以扩展父类的功能,但不能改变父类原有的功能
子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
别人不需要的东西不要强加给人家。
接口隔离原则是为了约束接口,降低类对接口的依赖。
接口隔离原则是为了约束接口,降低类对接口的依赖。
接口隔离原则的优点:
1)灵活性,可维护性增强
2)高内聚,低耦合
3)减少代码冗余(减少了方法的实现)
4)体现对象层次
依赖于抽象,而不是依赖于具体,依赖注入模式
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个原则,它是SOLID原则中的一部分。依赖倒置原则的核心思想是:
高层模块不应该依赖于低层模块,二者都应该依赖于抽象; 抽象不应该依赖于具体细节,而具体细节应该依赖于抽象。
(依赖于抽象)
简而言之,依赖倒置原则要求我们通过抽象来解耦高层模块和低层模块之间的依赖关系,从而使系统更加灵活、可扩展和易于维护。
核心思路: 要依赖于抽象 ,不要依赖于具体
为了实现这一原则 ,在编程时针对抽象类或者接口编程,而不是具体要求实现
设计模式是一套反复被使用,经过验证的,代码的总结 ;
设计模式不是具体的方法,而一种思想.
学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使程序实现复用
创建型模式 对象的创建
结构型模式 对象的组成(结构)
行为型模式 对象的行为
创建型模式: 简单工厂模式, 工厂模式,单例模式等。。。
**结构型模式:**外观模式,适配器模式,装饰模式 。。。
**行为型模式:**模板方法模式,观察者模式,状态模式。。。
静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
优点:客户端不需要负责创建对象,明确了各位类的职责
缺点:静态工厂负责创建对象,如果有新的对象增加,或者创建方式不同,需要不断的修改工厂类,不利于后期维护
public class AnimalFoctory {
private AnimalFoctory(){
}
public static Animal createAnimal(String type){
if("dog".equals(type)){
return new Dog();
}else if("cat".equals(type)){
return new Cat();
}else{
return null;
}
}
public static Dog createDog(){
return new Dog();
}
public static Cat createCat(){
return new Cat();
}
}
工厂模式: 抽象工厂类负责定义创建对象的接口,具体对象的创建由继承抽象工厂的具体类实现
优点:客户端不需要负责创建对象,明确了各位类的职责 ; 如果 新的对象增加,不修改已有的代码,增加了维护性和扩展性
缺点:需要额外编写代码,增加了工作量
单例模式: 确保类在内存中只有一个对象,此实例必须自动创建,并且对外提供
优点:
缺点:
(饿汉式): 类加载的就创建对象
public class Student {
// 为了让静态方法可以访问此对象,所以把它变成静态的
// 为了让外界不能直接访问,加private
private static Student s = new Student();
private Student(){
}
public static Student getStudent(){
return s;
}
}
(懒汉式):用的时候,才去创建
public class Student2 {
private static Student2 s = null;
private Student2(){
}
public static Student2 getStudent(){
if(s==null){
s = new Student2();
}
return s;
}
}
面试题:单例模式的思想是什么?请写一代码体现。
? 开发:饿汉式(是不会出现问题的)
? 面试:懒汉式(可能会出现问题的)
? A: 懒加载(延迟加载)
? B: 线程安全问题
public class Student2 {
private static Student2 s = null;
private Student2(){
}
public static synchronized Student2 getStudent(){
if(s==null){
s = new Student2();
}
return s;
}
}
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
}
需求: 计算出一段代码的运行时间
优点: 满足用户需求,灵活多变
缺点:如果算法需要改变,需要修改抽象 类
优点:
可以提供比继承更灵活的扩展对象的功能
缺点:可以任意组合
已经学过的装饰:
Scanner scanner = new Scanner(System.in);
枚举是指变量的一一列出来,只能在指范围内取值;
如: 一周只有7天,一年12个月