当前位置: 首页 > news >正文

Java面向对象思想以及原理以及内存图解

文章目录

  • 什么是面向对象
    • 面向对象和面向过程区别
    • 创建一个对象用什么运算符?
    • 面向对象实现伪代码
    • 面向对象三大特征
    • 类和对象的关系。
  • 基础案例
    • 代码实现
    • 实例化
    • 创建car对象时car引用的内存图
    • 对象调用方法过程
  • 成员变量和局部变量
    • 作用范围
    • 在内存中的位置
  • 关于对象的引用关系
    • 简介
    • 相关代码
    • 内存图解
    • 对象相等和引用相等的区别
    • 类的构造方法的作用是什么
    • 构造方法的特点
    • 深拷贝和浅拷贝区别
      • 浅拷贝
      • 深拷贝
  • 匿名对象
    • 实例代码
    • 匿名对象与实例对象的区别
      • 实例代码
      • 图解匿名与非匿名内存运行
    • 使用场景
  • 封装
    • 什么是封装
    • 什么时private修饰
    • 代码示例
  • 构造函数
    • 什么是构造函数
    • 构造函数的小细节
    • 构造代码块
      • 构造代码块示例以及与构造方法的区别
  • this关键字
    • 什么是this关键字
    • this的应用
      • 解决构造函数初始化的问题
      • 用于构造函数之间进行互相调用
  • static关键字
    • 什么是static关键字
    • static特点
    • 实例变量和类变量的区别
    • 静态使用注意事项:
    • 静态有利有弊
      • 利处
      • 弊处
    • 错误代码示范
    • 图解对象如何调用static变量
  • main函数
    • 主函数
    • 主函数的定义格式以及关键字含义
    • 主函数是固定格式的
    • 如何使用args
  • 静态代码块
    • 格式
  • 设计优化
    • 单例模式
      • 简介
      • 饿汉式
      • 懒汉式(线程不安全)
      • 懒汉式(线程安全)
      • 内部类模式
      • 双重锁校验(线程安全)
      • 枚举单例模式(线程安全)
  • 相关面试题
  • 参考文献

什么是面向对象

面向对象和面向过程区别

面向过程:面向过程是将解决问题的思路转为一个个方法。
面向对象:面向对象则是编写一个对象,将这些思路封装成一个个对象方法,后续调用这个对象解决问题,相对面向过程而言,这种思路更符合人的思维并且更易扩展、复用、维护。

面向对象和面向过程性能差距:人们常常认为面向过程性能高于面向对象,因为创建的对象开销远远大于面向过程,实际上Java面向对象性能差的原因并不是这个,真正的原因是Java为半编译语言,运行并不是直接拿着二进制机械码执行,而是需要结果字节码转换这一步。
而且面向过程的性能并不一定比面向过程快,面向过程也需要计算偏移量以及某些脚本语言的性能也一定比Java好。

创建一个对象用什么运算符?

用new运算符,创建的对象的实例会在堆内存中开辟一个空间。而引用则在栈内存中,指向对象实例。

面向对象实现伪代码

以人开门为例,人需要开门,所以我们需要创建一个门对象,描述门的特征,这个门可以开或者关。所以门的伪代码如下:

门{开(){操作门轴}
}

上文说到了,面向对象的特点就是符合人的思维,而人开门这个功能,我们就可以创建一个人的对象,编写一个开门的动作,把门打开。通过这种对象调用对象的方式完成了功能。后续我们需要狗开门,猫开门也只是编写一个方法调用门对象的开的动作。

{开门(门对象){.()}}

面向对象三大特征

  1. 封装
  2. 继承
  3. 多态

类和对象的关系。

以生活事务为例,现实生活中的对象:张三 李四。他们都有姓名、性别、学习Java的能力。

所以我们要想通过面向对象思想实现抽象出对象,就得提取共性,编写一个类有姓名、性别、学习Java的能力。

public class Student {private String name;private int sex;public void studyJava(){System.out.println(this.name+"学习java");}
}

描述时,这些对象的共性有:姓名,年龄,性别,学习java功能。再将这些分析映射到java中,就是以class定义的类进行展开。

public static void main(String[] args) {Student zhangsan=new Student();zhangsan.setName("张三");zhangsan.studyJava();Student lisi=new Student();lisi.setName("李四");lisi.studyJava();//        输出结果
//        张三学习java
//                李四学习java}

而具体对象就是对应java在堆内存中用new建立实体。

基础案例

需求:描述汽车(颜色,轮胎数)。描述事物其实就是在描述事物的属性和行为。

属性对应在类中即变量行为对应的类中的函数(方法)

代码实现

public class Car {//描述颜色String color = "红色";//描述轮胎数int num = 4;//运行行为。public void run() {System.out.println("颜色:"+color + " 轮胎数:" + num);}
}

实例化

public class Main {public static void main(String[] args) {Car car = new Car();car.run();}
}

创建car对象时car引用的内存图

在这里插入图片描述

对象调用方法过程

首先我们看一段代码,这是一个人类的class类代码

public class Person {private String name;private int age;private static String country = "cn";public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public static void showCountry() {System.out.println("showCountry " + country);}public void speak() {System.out.println(this.getName() + " speak");}
}

假如我们在main中编写这样一段代码,请问在内存中他是如何工作的呢?

 public static void main(String[] args) {Person p = new Person("张三", 18);p.setName("李四");}

我们先从类类加载时开始分析,由于static关键字修改的变量或者方法会随着jvm加载类时一起创建,所以countryshowCountry()在方法区是这样的。

在这里插入图片描述

然后main方法开始执行对应代码,首先main方法入栈,初始化一个p引用

在这里插入图片描述

堆区开辟一个空间,创建一个person实例,p引用指向这个内存空间

在这里插入图片描述

调用setName,setName入栈,完成name值修改之后销毁

在这里插入图片描述

成员变量和局部变量

作用范围

成员变量作用于整个类中。
局部变量变量作用于函数中,或者语句中。

在内存中的位置

成员变量:在堆内存中,因为对象的存在,才在内存中存在。
局部变量:存在栈内存中。

关于对象的引用关系

简介

对象引用用于指向0个或者多个对象实例,对象实例可以被多个对象引用指向。

相关代码

假如我们使用上文car类执行以下代码,那么在内存中会如何执行呢?

car c=new car();
c.num=5;
car c1=c;
c.run();

内存图解

  1. 首先堆区开辟一个空间创建car对象,初始化值
  2. 修改num为5
  3. c1引用指向c,如下图所示
    在这里插入图片描述

对象相等和引用相等的区别

  1. 对象相等:两个对象内存中的存放的内容都相等
  2. 引用相等:两个引用指向的内存地址相等。

类的构造方法的作用是什么

完成对象初始化,首先在堆区创建对象实例。

构造方法的特点

  1. 与类名相同
  2. 无返回值
  3. 生成对象时自动执行
  4. 不可重写可以重载

深拷贝和浅拷贝区别

浅拷贝

对象进行拷贝时,如果内部有引用类型,克隆对象仅仅是复制被克隆内部对象的引用地址

为了介绍浅拷贝我们贴出这样一段代码,可以看到一个学生类有id和name,以及一个Vector的引用对象

public class Student implements Cloneable {private String id;private String name;private Vector<String> vector;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Vector<String> getVector() {return vector;}public void setVector(Vector<String> vector) {this.vector = vector;}public Student() {try {System.out.println("创建对象需要三秒......");Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}public Student newInstance() {try {return (Student) this.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}}

然后我们使用下面这段代码进行测试,可以看到输出结果为true,说明student2的vector是student1的。如下图所示,克隆对象的内部引用对象和student1是通用的

@Testpublic void cloneTest() throws CloneNotSupportedException {long start,end;start=System.currentTimeMillis();Student student=new Student();end=System.currentTimeMillis();System.out.println("学生1创建时间长 "+(end-start));student.setId("1");student.setName("小明");Vector<String> v = new Vector<>();v.add("000000");v.add("000001");student.setVector(v);start=System.currentTimeMillis();Student student2= student.newInstance();end=System.currentTimeMillis();System.out.println("学生2创建时间长 "+(end-start));for (String s : student2.getVector()) {System.out.println(s);}
//        false则说明深拷贝成功System.out.println(student.getVector()==student2.getVector());}

在这里插入图片描述

深拷贝

了解了浅拷贝之后,我们就可以解释深拷贝了,克隆对象的内部引用对象都是全新复制出来的一份

基于上文student代码我们对此进行改造,重写以下clone方法

@Overrideprotected Object clone() throws CloneNotSupportedException {Student clone = new Student();clone.setId(this.getId());clone.setName(this.getName());//避免clone导致浅拷贝问题Vector<String> srcVector = this.getVector();Vector<String> dstVector = new Vector<>();for (String v : srcVector) {dstVector.add(v);}clone.setVector(dstVector);return clone;}

在这里插入图片描述

匿名对象

实例代码

如下所示,在堆区创建一个对象实例,用后即被销毁。为了介绍匿名对象,我们首先需要编写一个汽车类

public class Car {//描述颜色private String color = "红色";//描述轮胎数private int num = 4;public String getColor() {return color;}public void setColor(String color) {this.color = color;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}//运行行为。public void run() {this.setNum(++num);System.out.println("颜色:" + color + " 轮胎数:" + num);}
}

然后我们使用测试单元进行测试


@Testpublic void anonymously(){new Car().run();}

上述代码的工作过程如下所示,可以看到完成方法调用之后

在这里插入图片描述

匿名对象与实例对象的区别

实例代码

可以看到我们现实创建一个非匿名的汽车类和匿名汽车类,并作为show方法的参数传入


public static void main(String[] args) {Car car = new Car();show(car);show(new Car());/*** 输出结果* 颜色:black 轮胎数:4* 颜色:black 轮胎数:4*/}public static void show(Car c) {c.setNum(3);c.setColor("black");c.run();}

图解匿名与非匿名内存运行

非匿名对象内存运行过程图解

在这里插入图片描述

匿名对象完成方法调用后即被销毁

在这里插入图片描述

使用场景

  1. 当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。如果对一个对象进行多个成员调用,必须给这个对象起个名字。

  2. 可以将匿名对象作为实际参数进行传递。

封装

什么是封装

以生活为例子,某公司老板招开发人员,招得开发人员后,开发人员工作过程不用看到,老板只关注开发结果,而老板只看到开发结果这一现象即封装。

什么时private修饰

private :私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。私有只在本类中有效。

代码示例

如下所示,setAge就是对age赋值的封装,隐藏对年龄操作的细节,用户只需通过这个方法完成自己需要的赋值动作即可

public class Person {private int age;public void setAge(int a) {if (a > 0 && a < 130) {age = a;speak();} elseSystem.out.println("feifa age");}public int getAge() {return age;}private void speak() {System.out.println("age=" + age);}
}

构造函数

什么是构造函数

  1. 对象一建立就会调用与之对应的构造函数。
  2. 构造函数的作用:可以用于给对象进行初始化。

构造函数的小细节

  1. 类默认有构造函数,显示创建后默认构造类就消失。
  2. 默认构造函数权限和类权限修饰符一致,例如类权限为public,则构造方法默认也为public,除非显示修改权限。

构造代码块

构造代码块示例以及与构造方法的区别

构造代码块。

作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
和构造函数的区别:
1. 构造代码块是给所有对象进行统一初始化,在jvm完成类加载时就会运行方法,也就是说调用静态方法的情况下,构造代码块也会被执行
2. 而构造函数是给对应的对象初始化。

构造代码块会随着对象实例的创建和运行。

public class Person {private String name;private int age;{System.out.println("person 类的构造代码块执行了");run();}public void run(){System.out.println("person run");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

如下便是main函数的运行结果

public static void main(String[] args) {Person p=new Person();/*** person 类的构造代码块执行了* person run*/}

this关键字

什么是this关键字

代表它所在函数所属对象的引用。简单来说,调用对象方法的对象就是this关键字多代表的对象。

this的应用

解决构造函数初始化的问题

如下代码,假如所有成员变量不加this,编译器则不会找成员变量name,导致赋值过程毫无意义。

在这里插入图片描述
输出结果

在这里插入图片描述

对此我们就可以使用this关键字即可解决问题

public class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

用于构造函数之间进行互相调用

注意:this语句只能定义在构造函数的第一行。

public Person(String name, int age) {this(name);this.age = age;}public Person(String name) {this.name = name;}

static关键字

什么是static关键字

用法:是一个修饰符,用于修饰成员(成员变量,成员函数).
当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。类名.静态成员。

static特点

  1. 随着类的加载而加载。随着jvm完成类加载该变量或者方法就会被加载到方法区。
  2. 静态会随着类的消失而消失。说明它的生命周期最长。
  3. 优先于对象存在,即静态变量先存在,对象后存在。
  4. 被所有对象所共享,所以有时候我们需要考虑线程安全问题。
  5. 可以直接被类名所调用。

实例变量和类变量的区别

  1. 实例变量随着对象的创建而存放在堆内存上,而类变量即静态变量随着类加载而存放在方法区上。
  2. 实例变量随着对象实例消亡而消亡,而类变量随着类的消亡和消亡。

静态使用注意事项:

  1. 静态方法只能访问静态变量,实例对象则静态非静态都可以访问
  2. 静态方法不可使用this和super关键字,因为this和super都是对象实例的关键字,this关键字是指向对象实例,static关键字在类加载时候就能被指向,故不可使用这两个关键字。

静态有利有弊

利处

随着类加载而创建,每个对象公用一份,无需每个实例到堆区创建一份。

弊处

  1. 生命周期长
  2. 使用不当可能造成线程安全问题。
  3. 访问有局限性,静态方法只能访问静态相关变量或者方法。

错误代码示范

在这里插入图片描述

图解对象如何调用static变量

我们首先编写这样一段代码

public class Person {private String name;private int age;public static int staticVar=4;}

然后我们的main方法进行这样的调用,在jvm内存是如何执行的呢?

 public static void main(String[] args) {Person person=new Person();person.staticVar=5;}
  1. main方法入栈
  2. 堆区创建person对象实例,p指向实例
  3. p实例通过堆区对象操作方法的静态变量,修改值为5

在这里插入图片描述

main函数

主函数

主函数是一个特殊的函数,程序执行的入口,可以被jvm执行。

主函数的定义格式以及关键字含义

public:代表着该函数访问权限是最大的。
static:代表主函数随着类的加载就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。

主函数是固定格式的

只有符合上述的固定格式,jvm才能识别。

jvm在调用主函数时,传入的是new String[0];即可长度为0的String

如何使用args

class MainDemo 
{public static void main(String[] args)//new String[]{String[] arr = {"hah","hhe","heihei","xixi","hiahia"};MainTest.main(arr);}
}class MainTest
{public static void main(String[] args){for(int x=0; x<args.length; x++)System.out.println(args[x]);}
}

静态代码块

格式

static
{静态代码块中的执行语句。
}

我们在person类中编写一个静态代码块,然后调用其他静态方法,可以发现静态代码块会随着类的加载而完成执行,并且只执行一次

public class Person {static {System.out.println("静态代码块,随着方法执行而执行.....");}public static void func(){System.out.println("静态方法执行了");}}

main方法调用示例

 public static void main(String[] args) {Person.func();Person.func();/*** 输出结果* 静态代码块,随着方法执行而执行.....* 静态方法执行了* 静态方法执行了*/}

设计优化

单例模式

简介

对于重量级对象的创建可能会导致以下问题:

  1. 创建对象开销大
  2. GC压力大,可能导致系统卡顿

饿汉式

代码如下所示,可以看到对象随着类的加载就会立刻完成创建,这就导致假如我们使用这个类的某些不需要单例的方法也会完成对象的创建。
例如我们就像调用以下Singleton 的sayHello这个静态方法就会导致单例实例被创建,所以如果非必要我们不建议采用这种非延迟加载的单例模式

public class Singleton {private Singleton() {System.out.println("创建单例");}public static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}public static void sayHello(){System.out.println("hello");}}

测试代码,可以看到调用静态方法单例就被被创建了

public static void main(String[] args) {Singleton.sayHello();/*** 输出:* 创建单例* hello*/}

原理也很简单,静态变量和方法都在方法区,随着类被加载这些变量或者方法都会被加载。

在这里插入图片描述

懒汉式(线程不安全)

懒汉式即实现延迟加载的有效手段,代码如下所示

/*** 延迟加载的单例类 避免jvm加载时创建对象*/
public class LazySingleton {private LazySingleton() {System.out.println("懒加载单例类");}private static LazySingleton instance = null;public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}public static void sayHello(){System.out.println("hello");}}

调用示例如下所示,可以看到静态方法调用后并没有创建实例,只有调用获取对象时才会得到对象实例

public static void main(String[] args) {LazySingleton.sayHello();LazySingleton.getInstance();/*** 输出结果* hello* 懒加载单例类*/}

懒汉式的工作原理如下图所示,可以看到只有调用getInstance后,才会在堆内存中开辟一块内存空间创建对象

在这里插入图片描述

实际上,当前的懒汉式存在线程安全问题,如上内存图解所示,可能会有两个线程走到==null的判断中进而出现创建多个单例对象的情况。我们使用JUC的倒计时门闩调用获取单例的情况,可以看到对象被创建了多次。

 /*** 不加synchronized的懒加载 加上则没有下面这样输出结果*/@Testpublic void threadTest0(){ExecutorService threadPool = Executors.newFixedThreadPool(1000);CountDownLatch countDownLatch=new CountDownLatch(1);for (int i = 0; i < 10000; i++) {threadPool.submit(()->{System.out.println(LazySingleton.getInstance());try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}});}countDownLatch.countDown();/*** 懒加载单例类* 懒加载单例类* 懒加载单例类* 懒加载单例类* com.optimize.design.LazySingleton@12423874* com.optimize.design.LazySingleton@350c55ec* com.optimize.design.LazySingleton@350c55ec* com.optimize.design.LazySingleton@350c55ec* 懒加载单例类* com.optimize.design.LazySingleton@350c55ec* com.optimize.design.LazySingleton@5897fc07* 懒加载单例类* com.optimize.design.LazySingleton@5897fc07* 懒加载单例类* com.optimize.design.LazySingleton@39d8305* com.optimize.design.LazySingleton@1a0eae7f* com.optimize.design.LazySingleton@1a0eae7f* 懒加载单例类* com.optimize.design.LazySingleton@1a0eae7f* 懒加载单例类*/}

懒汉式(线程安全)

要想实现线程安全,我们只需要通过下面这种方式上锁即可保线程安全,但是缺点也很明显,在高并发情况下,获取对象的实践会随着增加

 /*** 增加 synchronized确保线程安全* @return*/public synchronized static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}

测试用例如下,可以看到饿汉式和线程安全懒汉式时间的差距

@Testpublic void test(){long start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {Singleton.getInstance();}long end=System.currentTimeMillis();System.out.println(end-start);start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {LazySingleton.getInstance();}end=System.currentTimeMillis();System.out.println(end-start);/*** 输出结果** 创建单例* 3* 懒加载单例类* 20*/}

内部类模式

上文提到的懒汉式的性能问题,所以我们可以使用内部类模式解决该问题,代码如下所示,可以看到我们在单例类的内部增加一个静态内部类,该类被加载时静态内部类并不会被加载,只有调用getInstance才会创建单例对象,并且该对象的创建是随着类的加载就完成创建,故这是一种线程友好的单例模式

/*** 线程安全的单例 但还是会被反射攻破*/
public class StaticSingleton {private StaticSingleton() {System.out.println("静态内部类延迟加载");}private static class SingletonHolder {private static StaticSingleton instance = new StaticSingleton();}public static StaticSingleton getInstance(){return SingletonHolder.instance;}public static void sayHello(){System.out.println("hello");}}

测试代码和输出结果

public static void main(String[] args) {StaticSingleton.sayHello();StaticSingleton.getInstance();/*** 输出结果* * hello* 静态内部类延迟加载*/}

内部类单例模式工作过程

在这里插入图片描述

实际上这种模式也有缺点,就是会被发射攻破,后续我们会介绍对应的解决方案

双重锁校验(线程安全)

双重锁校验的单例模式如下所示,可以看到双重锁校验的编码方式和简单,第一次判断避免没必要的执行,第二次判断避免第一次判定为空走到创建对象代码块的线程,从而避免线程安全问题

public class DoubleCheckLockSingleton {private static DoubleCheckLockSingleton instance = null;private DoubleCheckLockSingleton() {System.out.println("双重锁单例对象被创建");}public static DoubleCheckLockSingleton getInstance() {if (instance != null) {return instance;}synchronized (DoubleCheckLockSingleton.class) {//这一重校验是为了避免上面判空后进入休眠走到这个代码块的线程if (null == instance) {instance = new DoubleCheckLockSingleton();return instance;}}return instance;}}

性能上我们可以看到双重锁校验的性能要好于静态内部类的方式

 @Testpublic void test(){long start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {Singleton.getInstance();}long end=System.currentTimeMillis();System.out.println(end-start);start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {LazySingleton.getInstance();}end=System.currentTimeMillis();System.out.println(end-start);start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {StaticSingleton.getInstance();}end=System.currentTimeMillis();System.out.println(end-start);start=System.currentTimeMillis();for (int i = 0; i < 100_0000; i++) {DoubleCheckLockSingleton.getInstance();}end=System.currentTimeMillis();System.out.println(end-start);/*** 创建单例* 6* 懒加载单例类* 20* 静态内部类延迟加载* 4* 双重锁单例对象被创建* 3*/}

枚举单例模式(线程安全)

/*** 使用枚举保证类单例*/
public enum Elvis {INSTANCE;private String name="elvis";public String getName() {return name;}public static Elvis getInstance(){return INSTANCE;}public void leaveTheBuilding() {System.out.println("Whoa baby, I'm outta here!");}}

可以看到这种方式不会被反射攻破

public static void main(String[] args) throws IllegalAccessException, InstantiationException {Class<Elvis> elvisClass = Elvis.class;Elvis elvis1 = elvisClass.newInstance();System.out.println(elvis1.getName()); }

输出结果

在这里插入图片描述

相关面试题

下面这段代码。new StaticCode(4)的输出结果?

public class StaticCode {int num = 9;StaticCode() {System.out.println("b");}static {System.out.println("a");}{System.out.println("c" + this.num);}StaticCode(int x) {System.out.println("d");}public static void show() {System.out.println("show run");}
}

答案:a c9 d

创建类,加载顺序为:

  1. 加载静态代码块
  2. 加载构造代码块
  3. 加载构造方法

参考文献

Java基础常见面试题总结(中)

Effective Java中文版(第3版)

Java系统性能优化实战

相关文章:

Java面向对象思想以及原理以及内存图解

文章目录 什么是面向对象面向对象和面向过程区别创建一个对象用什么运算符?面向对象实现伪代码面向对象三大特征类和对象的关系。 基础案例代码实现实例化创建car对象时car引用的内存图对象调用方法过程 成员变量和局部变量作用范围在内存中的位置 关于对象的引用关系简介相关…...

Gitbook----基于 Windows 10 系统本地安装配置 Gitbook 编写属于自己的电子书

查看原文 文章目录 一、安装 Nodejs二、安装 Gitbook三、gitbook 的使用方法四、设计电子书的目录结构五、设置 gitbook 常用配置 一、安装 Nodejs 若要在 Windows 10 系统即本地使用 Gitbook&#xff0c;需要安装 gitlab-cli 工具&#xff0c;而 gitbook-cli 工具是基于 Node…...

springMVC-Restful风格

基本介绍 REST&#xff1a;即Representational State Transfer。&#xff08;资源&#xff09;表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便&#xff0c;所以正得到越来越多网站的采用. 1.HTTP协议里面&#xff0c;四个表示操…...

【OS】操作系统总复习笔记

操作系统总复习 文章目录 操作系统总复习一、考试题型1. 论述分析题2. 计算题3. 应用题 二、操作系统引论&#xff08;第1章&#xff09;2.1 操作系统的发展过程2.2 操作系统定义2.3 操作系统的基本特性2.3.1 并发2.3.2 共享2.3.3 虚拟2.3.4 异步 2.4 OS的功能2.5 OS结构2.5 习…...

powerbuilder游标的使⽤

在某些PowerBuilder应⽤程序的开发中,您可能根本⽤不到游标这样⼀个对象。因为在其它⼯具开发中很多需⽤游标实现的⼯作,在PowerBuilder中却已有DataWin-dow来代劳了。事实上,DataWindow不仅可以替代游标进⾏从后台数据库查询多条记录的复杂操作,⽽且还远不⽌这些。但是同DataW…...

docker创建镜像 Dockerfile

目录 docker的创建镜像的方式 dockerfile形成&#xff08;原理&#xff09; docker的核心作用 docker的文件结构 dockerfile的语法 CMD和ENTRPOINT的区别 创建dockerfile镜像 区别 RUN命令的优化 如何把run命令写在一块 copy和ADD区别 区别 centos7 构建Apache的d…...

C++共享和保护——(2)生存期

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 生命如同寓言&#xff0c;其价值不在于…...

你好,C++(3)2.1 一个C++程序的自白

第2部分 与C第一次亲密接触 在浏览了C“三分天下”的世界版图之后&#xff0c;便对C有了基本的了解&#xff0c;算是一只脚跨入了C世界的大门。那么&#xff0c;怎样将我们的另外一只脚也跨入C世界的大门呢&#xff1f;是该即刻开始编写C程序&#xff1f;还是…… 正在我们犹…...

【INTEL(ALTERA)】Agilex7 FPGA Development Kit DK-DEV-AGI027R1BES编程/烧录/烧写/下载步骤

DK-DEV-AGI027R1BES 的编程步骤&#xff1a; 将外部 USB Blaster II 连接到 J10- 外部 JTAG 接头。将交换机 SW5.3 设置为 ON&#xff08;首次&#xff09;。打开 英特尔 Quartus Prime Pro Edition 软件编程工具。单击 硬件设置 &#xff0c;然后选择 USB Blaster II。将硬件…...

大文件分块上传的代码,C++转delphi,由delphi实现。

在 Delphi 中&#xff0c;我们通常使用 IdHTTP 或 TNetHTTPClient 等组件来处理 HTTP 请求 原文章链接&#xff1a; 掌握分片上传&#xff1a;优化大文件传输的关键策略 【C】【WinHttp】【curl】-CSDN博客 改造思路&#xff1a; 文件分块处理&#xff1a;使用 TFileStream 来…...

MongoDB表的主键可以重复?!MongoDB的坑

MongoDB表的主键可以重复&#xff1f;&#xff01; 眼见为实&#xff1f; 碰到一个奇怪的现象&#xff0c; MongoDB的一个表居然有两个一样的_id值&#xff01; 再次提交时&#xff0c;是会报主键冲突的。那上图&#xff0c;为什么会有两个一样的_id呢&#xff1f; 将它们的…...

C++初阶-list类的模拟实现

list类的模拟实现 一、基本框架1.1 节点类1.2 迭代器类1.3 list类 二、构造函数和析构函数2.1 构造函数2.2 析构函数 三、operator的重载和拷贝构造3.1 operator的重载3.2 拷贝构造 四、迭代器的实现4.1 迭代器类中的各种操作4.1 list类中的迭代器 五、list的增容和删除5.1 尾插…...

RecyclerView中的设计模式解读

一.观察者模式&#xff1a;&#xff08;待完善&#xff0c;这个写的不咋地&#xff0c;没理解透彻&#xff09; 1.观察者模式的概念&#xff1a; &#xff08;1&#xff09;消息传递方向&#xff1a;被观察者->观察者 &#xff08;2&#xff09;代码实现&#xff1a; 首…...

ACwing算法备战蓝桥杯——Day30——树状数组

定义&#xff1a; 树状数组是一种数据结构&#xff0c;能将对一个区间内数据进行修改和求前缀和的这两种操作的最坏时间复杂度降低到O(logn); 实现所需变量 变量名变量数据类型作用数组a[]int存储一段区间数组tr[]int表示树状数组 主要操作 函数名函数参数组要作用lowbit()int…...

elementui + vue2实现表格行的上下移动

场景&#xff1a; 如上&#xff0c;要实现表格行的上下移动 实现&#xff1a; <el-dialogappend-to-bodytitle"条件编辑":visible.sync"dialogVisible"width"60%"><el-table :data"data1" border style"width: 100%&q…...

2、快速搞定Kafka术语

快速搞定Kafka术语 Kafka 服务端3层消息架构 Kafka 客户端Broker 如何持久化数据小结 Kafka 服务端 3层消息架构 第 1 层是主题层&#xff0c;每个主题可以配置 M 个分区&#xff0c;而每个分区又可以配置 N 个副本。第 2 层是分区层&#xff0c;每个分区的 N 个副本中只能有…...

CSS新手入门笔记整理:CSS3选择器

属性选择器 属性选择器&#xff0c;指的是通过“元素的属性”来选择元素的一种方式。 语法 元素[attr^"xxx"]{} 元素[attr$"xxx"]{} 元素[attr*"xxx"]{} 选择器 说明 E[attr^"xxx"] 选择元素E&#xff0c;其中E元素的attr属性是…...

D34|不同路径

62.不同路径 初始思路&#xff1a; 1&#xff09;确定dp数组以及下标的含义&#xff1a; dp[i][i]存放到第i1行和第i1列的方法数 2&#xff09;确定递推公式&#xff1a; dp[i][i] dp[i -1][i] dp[i][i-1] 3&#xff09;dp数组如何初始化 第0行是1&#xff1b; 第0列是1&a…...

【运维】Kafka高可用: KRaft(不依赖zookeeper)集群搭建

文章目录 一. kafka kraft 集群介绍1. KRaft架构2. Controller 服务器3. Process Roles4. Quorum Voters5. kraft的工作原理 ing 二. 集群安装1. 安装1.1. 配置1.2. 格式化 2. 启动测试2.1. 启功节点服务2.2. 测试 本文主要介绍了 kafka raft集群架构&#xff1a; 与旧架构的不…...

Python 自动化之批量处理文件(一)

批量新建目录、文档Pro版本 文章目录 批量新建目录、文档Pro版本前言一、做成什么样子二、基本思路1.引入库2.基本架构 三、用户输入模块四、数据处理模块1.excel表格数据获取2.批量数据的生成 总结 前言 我来写一个不一样的批量新建吧。在工作中&#xff0c;有些同学应该会遇…...

力扣72. 编辑距离

动态规划 思路&#xff1a; 假设 dp[i][j] 是 word1 前 i 个字母到 word2 前 j 个字母的编辑距离&#xff1b;那么状态 dp[i][j] 状态的上一个状态有&#xff1a; dp[i - 1][j]&#xff0c;word1 前 i - 1 个字母到 word2 前 j 个字母的编辑距离&#xff0c;此状态再插入一个字…...

Unity中 URP Shader 的纹理与采样器的分离定义

文章目录 前言一、URP Shader 纹理采样的实现1、在属性面板定义一个2D变量用于接收纹理2、申明纹理3、申明采样器4、进行纹理采样 二、申明纹理 和 申明采样器内部干了什么1、申明纹理2、申明采样器 三、采样器设置采样器的传入格式1、纹理设置中&#xff0c;可以看见我们的采样…...

Electron学习第一天 ,启动项目

之前在安装官网的步骤操作&#xff0c;结果报错&#xff0c;找了好多办法&#xff0c;最后这种办法成功启动项目&#xff0c;并且没有报错&#xff0c;特此记录 特别提醒&#xff0c;最好安装淘宝镜像&#xff0c;npm 太慢&#xff0c;会导致报错问题&#xff0c;解决起来个人觉…...

WebService技术--随笔1

1.WebService 发展史 创建阶段&#xff08;1990 年代末至 2000 年代初&#xff09;&#xff1a;在这个阶段&#xff0c;XML-RPC 和 SOAP 协议被引入&#xff0c;为跨平台和跨语言的应用程序集成提供了基础。XML-RPC 提供了一种基于 XML 的远程过程调用机制&#xff0c;而 SOAP…...

如何使用Docker将.Net6项目部署到Linux服务器(一)

目录 一 配置服务器环境 1.1 配置yum 1.1.1 更新yum包 1.1.2 yum命令 1.2 配置docker …...

第4章-第3节-Java中跟数组相关的几个算法以及综合应用

在写这篇博文之前&#xff0c;先大概说明一下&#xff0c;就是很常见的数组算法如求最大值、一维数组的遍历等&#xff0c;这里就不去专门说明了&#xff0c;只说一些有代表性的&#xff0c;然后就是冒泡排序算法很容易查阅到&#xff0c;这里也不专门说明了&#xff0c;只说明…...

AlexNet(pytorch)

AlexNet是2012年ISLVRC 2012&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;竞赛的冠军网络&#xff0c;分类准确率由传统的 70%提升到 80% 该网络的亮点在于&#xff1a; &#xff08;1&#xff09;首次利用 GPU 进行网络加速训练。 &#xff…...

【单调栈 】LeetCode321:拼接最大数

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的知识点 单调栈 题目 给定长度分别为 m 和 n 的两个数组&#xff0c;其元素由 0-9 构成&#xff0c;表示两个自然数各位上的数字。现在从这两个数组中选出 k (k < m n) 个数字…...

TikTok与虚拟现实的完美交融:全新娱乐时代的开启

TikTok&#xff0c;这个风靡全球的短视频平台&#xff0c;与虚拟现实&#xff08;VR&#xff09;技术的深度结合&#xff0c;为用户呈现了一场全新的娱乐盛宴。虚拟现实技术为TikTok带来了更丰富、更沉浸的用户体验&#xff0c;标志着全新娱乐时代的开启。本文将深入探讨TikTok…...

PXI/PCIe/VPX机箱 ARM|x86 + FPGA测试测量板卡解决方案

PXI便携式测控系统是一种基于PXI总线的便携式测试测控系统&#xff0c;它填补了现有台式及机架式仪器在外场测控和便携测控应用上的空白&#xff0c;在军工国防、航空航天、兵器电子、船舶舰载等各个领域的外场测控场合和科学试验研究场合都有广泛的应用。由于PXI便携式测控系统…...

基于html的网站设计/北京十大教育培训机构排名

Jenkins 动态 Slave 背景 Jenkins日常工作中在代码编译、打包、部署等一系列操作&#xff0c;都是以 Job 的形式进行&#xff0c;而 Job 的承载是 Jenkins 所在的主机&#xff0c;当面临 Jenkins 扩展、迁移等操作时&#xff0c;需要安装大量的依赖软件来支持 Job的运行。所以本…...

品牌seo公司/seo关键词优化公司哪家好

No valid host was found. There are not enough hosts available ascii codec cant decode byte 0xe6 in position 0: ordinal not in range(128)...

哪个网站做logo设计师/推广软文平台

来看一下题目&#xff0c;给定一个整数数组&#xff0c;另外给定一个数&#xff0c;如果在数组中两个数相加等于这个数&#xff0c;获得这两个数的索引&#xff0c;两个数不能重复&#xff0c;而且假设每个数最多只有一组数存在于数组中。 Example:Given nums [2, 7, 11, 15],…...

免费动态网站成品作业/惊艳的网站设计

隐写篇0x01. 通过进制转换隐藏信息0x02. 在图片中隐藏压缩包(图种)加密篇0x03. 伪加密0x04. 爆破/字典/掩码攻击0x05. 明文攻击0x06. CRC32碰撞格式篇0x07. 修改格式0x01. 通过进制转换隐藏信息这种方法比较简单&#xff0c;直接拿一道题讲解(题目来自ISCC 2017 Basic-04)。题目…...

网站 只做程序/百度推荐现在为什么不能用了

使用Python可以很容易地创建一个AI。首先&#xff0c;您需要了解Python的基本编程概念&#xff0c;这可以通过编写简单的程序&#xff0c;如计算器&#xff0c;来实现。其次&#xff0c;您需要学习如何使用Python的机器学习库&#xff0c;例如scikit-learn和TensorFlow&#xf…...

h5网站制作/百度手机助手应用商店下载

在项目文件夹下使用npm包管理工具安装base64&#xff0c;此处要做兼容所以未使用‘js-base64’&#xff0c;参考base-64和js-base64 npm install base-64在页面的 ‘script’ 标签中引入 import Base64 from base-64;在逻辑代码中使用 var code 123456; var en Base64.enc…...