九、java 继承

文章目录

  • java 继承
    • 3.1 根父类Object
    • 3.2 方法重写
    • 3.3 继承案例:图形类继承体系
    • 3.4 继承的细节
      • 3.4.1 构造方法
      • 3.4.2 重名与静态绑定
      • 3.4.3 重载和重写
      • 3.4.4 父子类型转换
      • 3.4.5 继承访问权限protected
      • 3.4.6 可见性重写
      • 3.4.7 防止继承final
    • 3.5 继承是把双刃剑
      • 3.5.1 继承破坏封装性
      • 3.5.2 继承没有反映is-a关系

java 继承

本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记

我们知道,java中有类和对象的概念,当我们研究类与类之间的关系时,会发现类与类之间有一种继承的关系。比如动物类Animal和狗类DogAnimal是父类,Dog是子类。父类也叫基类,子类也叫派生类。父类、子类是相对的,一个类B可能是类A的子类,但又是类C的父类。之所以叫继承,是因为子类继承了父类的属性和行为,父类有的属性和行为会继承给子类。但子类也可以增加子类特有的属性和行为,某些父类有的行为,子类的实现方式可能与父类也不完全一样。使用继承一方面可以复用代码,公共的属性和行为可以放到父类中,而子类只需要关注子类特有的就可以了;另一方面,不同子类的对象可以更为方便地被统一处理。

3.1 根父类Object

Java中,即使没有声明父类,也有一个隐含的父类,这个父类叫ObjectObject没有定义属性,但定义了一些方法,如下图所示。

上图中定义了一些方法,Object中的方法我们先学习toString()方法,toString()方法的目的是返回一个对象的文本描述,这个方法可以直接被所有类使用,toString代码如下:

   public String toString() {return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());}

现在有一个Point point对象,可以这样使用toString`方法:

System.out.println(point.toString());

输出类似这样:com.ieening.Point@2e5c649

结合toString和输出结果,我们知道,@之前是类名,之后是该对象哈希值的十六进制表示。为什么这么设计呢?写类名是可以理解的,表示对象的类型,而写哈希值则是不得已的,因为Object类并不知道具体对象的属性,不知道怎么用文本描述,但又需要区分不同对象,只能是写一个哈希值。

但子类是可以知道自己的属性和值的,显然ObjecttoString不适合子类,为解决该问题,java允许子类重写父类继承的方法,以适合自己的实际和需求。所谓重写,就是定义和父类一样的方法,并重新实现。

3.2 方法重写

设计一个Point类,并重写toString方法。

class Point {private int x;private int y;Point() {this(0, 0);}Point(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public double distance() {return Math.sqrt(x * x + y * y);}@Overridepublic String toString() {return "Point [x=" + x + ", y=" + y + "]";}}

toString方法前面有一个@Override,这表示toString这个方法是重写的父类的方法,重写后的方法返回Pointxy坐标的值。重写后,将调用子类的实现。比如,如下代码的输出就变成了:Point [x=3, y=4]

  public static void main(String[] args) {Point point = new Point(3, 4);System.out.println(point);}

3.3 继承案例:图形类继承体系

接下来,我们以一些图形处理中的例子来进一步解释。先来看一些图形的例子,如下图所示:

在这里插入图片描述

这都是一些基本的图形,图形有直线、正方形、三角形、圆形等,图形有不同的颜色。接下来,我们定义以下类来说明关于继承的一些概念和使用:

  1. 父类Shape,表示图形。
  2. Circle,表示圆。
  3. Line,表示直线。
  4. ArrowLine,表示带箭头的直线。
  5. 图形管理者类ShapeManager,它负责管理画板上的所有图形对象并负责绘制。

1、Shape

package com.ieening.learninheritshape;public class Shape {private static final String DEFAULT_COLOR = "black";private String color;public Shape() {this(DEFAULT_COLOR);}public Shape(String color) {this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public void draw() {System.out.println("draw shape");}
}

上面代码非常简单,实例变量color表示颜色,draw方法表示绘制,可以看到并没有实现实际的绘制代码,主要是演示继承关系。

2、圆

圆(Circle)继承自Shape,但包括了额外的属性:中心点和半径,以及额外的方法area,用于计算面积,另外,重写了draw方法,代码如下所示。

package com.ieening.learninheritshape;public class Circle extends Shape { // 注释1// 中心点private Point center;// 半径private double r;public Circle(Point center, double r) {this.center = center;this.r = r;}@Overridepublic void draw() {System.out.println("draw circle at " + center.toString() + " with r " + r+ ", using color : " + getColor()); // 注释2、注释3}public double area() {return Math.PI * r * r;}@Overridepublic String toString() {return "Circle [center=" + center + ", r=" + r + "]";}public static void main(String[] args) {Point center = new Point(3, 4);// 创建圆,赋值给circleCircle circle = new Circle(center, 2);// 调用draw方法,会执行Circle的draw方法circle.draw();// 输出圆面积System.out.println(circle.area());}
}

程序输出:

12.566370614359172
draw circle at Point [x=3, y=4] with r 2.0, using color : black // 注释
  1. 注释1:Java使用extends关键字表示继承关系,一个类最多只能有一个父类;
  2. 注释2:子类不能直接访问父类的私有属性和方法。比如,在Circle中,不能直接访问Shape的私有实例变量color,而是调用共有方法getColor
  3. 注释3:除了私有的外,子类继承了父类的其他属性和方法。比如,在Circledraw方法中,可以直接调用getColor方法;

这里比较奇怪的是,color是什么时候赋值的?在new的过程中,父类的构造方法也会执行,且会优先于子类执行。在这个例子中,父类Shape的默认构造方法会在子类Circle的构造方法之前执行。

3、直线

线(Line)继承自Shape,但有两个点,以及一个获取长度的方法,并重写了draw方法,代码如下所示。

package com.ieening.learninheritshape;public class Line extends Shape {private Point start;public Point getStart() {return start;}public void setStart(Point start) {this.start = start;}private Point end;public Point getEnd() {return end;}public void setEnd(Point end) {this.end = end;}public Line(Point start, Point end, String color) {super(color);this.start = start;this.end = end;}public double length() {return start.distance(end);}@Overridepublic void draw() {System.out.println("draw line from " + start.toString() + " to " + end.toString() + ", using color " + super.getColor());}public static void main(String[] args) {Line line = new Line(new Point(0, 0), new Point(1, 1), "red");System.out.println(line.length());line.draw();}
}

运行结果:

1.0
draw line from Point [x=0, y=0] to Point [x=1, y=1], using color red

这里我们要说明的是super这个关键字,super用于指代父类,可用于调用父类构造方法,访问父类方法和变量。

  1. Line构造方法中,super(color)表示调用父类的带color参数的构造方法。调用父类构造方法时,super必须放在第一行。
  2. draw方法中,super.getColor()表示调用父类的getColor方法,当然不写super.也是可以的,因为这个方法子类没有同名的,没有歧义,当有歧义的时候,通过super.可以明确表示调用父类的方法。
  3. super同样可以引用父类非私有的变量。可以看出,super的使用与this有点像,但superthis是不同的,this引用一个对象,是实实在在存在的,可以作为函数参数,可以作为返回值,但super只是一个关键字,不能作为参数和返回值,它只是用于告诉编译器访问父类的相关变量和方法。

4、带箭头直线

带箭头直线(ArrowLine)继承自Line,但多了两个属性,分别表示两端是否有箭头,也重写了draw方法,代码如下所示:

package com.ieening.learninheritshape;public class ArrowLine extends Line {private boolean startArrow;public boolean isStartArrow() {return startArrow;}public void setStartArrow(boolean startArrow) {this.startArrow = startArrow;}private boolean endArrow;public boolean isEndArrow() {return endArrow;}public void setEndArrow(boolean endArrow) {this.endArrow = endArrow;}public ArrowLine(Point start, Point end, String color, boolean startArrow, boolean endArrow) {super(start, end, color);this.startArrow = startArrow;this.endArrow = endArrow;}@Overridepublic void draw() {if (startArrow) {System.out.print("draw start arrow, ");}if (endArrow) {System.out.print("draw end arrow");}super.draw();}public static void main(String[] args) {ArrowLine arrowLine = new ArrowLine(new Point(0, 0), new Point(2, 2), "blue", true, true);arrowLine.draw();}
}

运行结果:

draw start arrow, draw end arrowdraw line from Point [x=0, y=0] to Point [x=2, y=2], using color blue

ArrowLine继承自Line,而Line继承自Shape, ArrowLine的对象也有Shape的属性和方法。注意draw方法的最后一行,super.draw()表示调用父类的draw()方法,这时候不带super.是不行的,因为当前的方法也叫draw()

5、图形管理器

使用继承的一个好处是可以统一处理不同子类型的对象。比如,我们来看一个图形管理者类,它负责管理画板上的所有图形对象并负责绘制,在绘制代码中,只需要将每个对象当作Shape并调用draw方法就可以了,系统会自动执行子类的draw方法。代码如下所示。

package com.ieening.learninheritshape;public class ShapeManager {private static final int MAX_NUM = 100;private Shape[] shapes = new Shape[MAX_NUM];private int shapeNum = 0;public void addShape(Shape shape) {if (shapeNum < MAX_NUM) {shapes[shapeNum++] = shape;}}public void draw() {for (int i = 0; i < shapeNum; i++) {shapes[i].draw();}}public static void main(String[] args) {ShapeManager shapeManager = new ShapeManager();shapeManager.addShape(new Circle(new Point(4, 4), 3));shapeManager.addShape(new Line(new Point(2, 3), new Point(3, 4), "green"));shapeManager.addShape(new ArrowLine(new Point(1, 2), new Point(5, 5), "black", false, true));shapeManager.draw();}
}

运行结果如下:

draw circle at Point [x=4, y=4] with r 3.0, using color : black
draw line from Point [x=2, y=3] to Point [x=3, y=4], using color green
draw end arrowdraw line from Point [x=1, y=2] to Point [x=5, y=5], using color black

ShapeManager使用一个数组保存所有的shape,在draw方法中调用每个shapedraw方法。ShapeManager并不知道每个shape具体的类型,也不关心,但可以调用到子类的draw方法。如上面代码main方法中,使用ShapeManager的一个例子,新建了三个shape,分别是一个圆、直线和带箭头的线,然后加到了shapeManager中,然后调用managerdraw方法。需要说明的是,在addShape方法中,参数Shape shape,声明的类型是Shape,而实际的类型则分别是CircleLineArrowLine。子类对象赋值给父类引用变量,这叫**向上转型**,转型就是转换类型,向上转型就是转换为父类类型。

变量shape可以引用任何Shape子类类型的对象,这叫多态,即一种类型的变量,可引用多种实际类型对象。这样,对于变量shape,它就有两个类型:类型Shape,我们称之为shape的静态类型;类型Circle/Line/ArrowLine,我们称之为shape的动态类型。在ShapeManagerdraw方法中,shapes[i].draw()调用的是其对应动态类型的draw方法,这称之为方法的动态绑定。

为什么要有多态和动态绑定呢?创建对象的代码(ShapeManager以外的代码)和操作对象的代码(ShapeManager本身的代码),经常不在一起,操作对象的代码往往只需要知道对象是某种父类型,也往往只需要知道它是某种父类型就可以了。可以说,多态和动态绑定是计算机程序的一种重要思维方式,使得操作对象的程序不需要关注对象的实际类型,从而可以统一处理不同对象,但又能实现每个对象的特有行为。

3.4 继承的细节

3.4.1 构造方法

我们已经知道,子类可以通过super调用父类的构造方法,如果子类没有通过super调用,则会自动调动父类的默认构造方法,那如果父类没有默认构造方法呢?如下所示:

package com.ieening;public class Base {private String member;public Base(String member) {this.member = member;}
}

这个类只有一个带参数的构造方法,没有默认构造方法。这个时候,它的任何子类都必须在构造方法中通过super调用Base的带参数构造方法,如下所示,否则,Java会提示编译错误:Implicit super constructor Base() is undefined for default constructor. Must define an explicit constructorJava(134217868)

另外需要注意的是,如果在父类构造方法中调用了可被重写的方法,则可能会出现意想不到的结果。我们来看个例子,下面是基类和子类代码:

package com.ieening;public class Base {private String member;public Base(String member) {this.member = member;}public Base() {test();}public void test() {}
}/*** Child*/
class Child extends Base {private int a = 123;@Overridepublic void test() {System.out.println(a);}public static void main(String[] args) {Child child = new Child();child.test();}
}

运行结果:

0
123

构造方法调用了test方法。子类Child`有一个实例变量`a`,初始赋值为`123`,重写了`test()方法,输出a的值。输出结果中第一次输出为0,第二次输出为123。第一行为什么是0呢?第一次输出是在new过程中输出的,在new过程中,首先是初始化父类,父类构造方法调用test()方法,test()方法被子类重写了,就会调用子类的test()方法,子类方法访问子类实例变量a,而这个时候子类的实例变量的赋值语句和构造方法还没有执行,所以输出的是其默认值0

像这样,在父类构造方法中调用可被子类重写的方法,是一种很不好的实践,容易引起混淆,应该只调用private的方法。

3.4.2 重名与静态绑定

子类可以重写父类非private的方法,当调用的时候,会动态绑定,执行子类的方法。那实例变量、静态方法和静态变量呢?它们可以重名吗?如果重名,访问的是哪一个呢?重名是可以的,重名后实际上有两个变量或方法。private变量和方法只能在类内访问,访问的也永远是当前类的,即:在子类中访问的是子类的;在父类中访问的是父类的,它们只是碰巧名字一样而已,没有任何关系。public变量和方法,则要看如何访问它。在类内,访问的是当前类的,但子类可以通过super.明确指定访问父类的。在类外,则要看访问变量的静态类型:静态类型是父类,则访问父类的变量和方法;静态类型是子类,则访问的是子类的变量和方法。

public class Base {public static String s = "static_base";public String m = "base";public static void staticTest(){System.out.println("base static: "+s);}
}

base类中定义了一个public静态变量s,一个public实例变量m,一个静态方法staticTest。子类Child中定义了和父类重名的变量和方法。对于一个子类对象,它就有了两份变量和方法,在子类内部访问的时候,访问的是子类的,或者说,子类变量和方法隐藏了父类对应的变量和方法。

public class Child extends Base {public static String s = "child_base";public String m = "child";public static void staticTest(){System.out.println("child static: "+s);}
}

观察下面外部访问代码,创建了一个子类对象,然后将对象分别赋值给了子类引用变量c和父类引用变量b,然后通过bc分别引用变量和方法。程序输出为:

public static void main(String[] args) {Child c = new Child();Base b = c;System.out.println(b.s);System.out.println(b.m);b.staticTest();System.out.println(c.s);System.out.println(c.m);c.staticTest();
}

运行结果如下:

static_base
base
base static: static_base
child_base
child
child static: child_base

当通过b(静态类型Base)访问时,访问的是Base的变量和方法,当通过c(静态类型Child)访问时,访问的是Child的变量和方法,这称之为静态绑定,即访问绑定到变量的静态类型。静态绑定在程序编译阶段即可决定,而动态绑定则要等到程序运行时。实例变量、静态变量、静态方法、private方法,都是静态绑定的。

3.4.3 重载和重写

重载是指方法名称相同但参数签名不同(参数个数、类型或顺序不同),重写是指子类重写与父类相同参数签名的方法。对一个函数调用而言,可能有多个匹配的方法,有时候选择哪一个并不是那么明显。总体的规律就是:

  1. 当有多个重名函数的时候,在决定要调用哪个函数的过程中,首先是按照参数类型进行匹配的,换句话说,寻找在所有重载版本中最匹配的;
  2. 当没有最匹配时,看变量的动态类型,进行动态绑定;

来看个例子,这是基类代码:

public class Base {public int sum(int a, int b){System.out.println("base_int_int");return a+b;}
}

代码中定义了方法sum,下面是子类代码:

public class Child extends Base {public long sum(long a, long b){System.out.println("child_long_long");return a+b;}
}

以下是调用的代码:

public static void main(String[] args){Child c = new Child();int a = 2;int b = 3;c.sum(a, b);
}

ChildBase都定义了sum方法,这里调用的是哪个sum方法呢?子类的sum方法参数类型虽然不完全匹配但是是兼容的,父类的sum方法参数类型是完全匹配的。程序输出为:

0
base_int_int

父类类型完全匹配的方法被调用了。如果父类代码改成下面这样呢?

public class Base {public long sum(int a, long b){System.out.println("base_int_long");return a+b;}
}

父类方法类型也不完全匹配了。程序输出为:

0
base_int_long

调用的还是父类的方法。父类和子类的两个方法的类型都不完全匹配,为什么调用父类的呢?因为父类的更匹配一些。现在修改一下子类代码,更改为:

public class Child extends Base {public long sum(int a, long b){System.out.println("child_int_long");return a+b;}
}

程序输出变为了:

0
child_int_long

终于调用了子类的方法。

3.4.4 父子类型转换

之前我们说过,子类型的对象可以赋值给父类型的引用变量,这叫向上转型,向上转型不会出现问题,那父类型的变量可以赋值给子类型的变量吗?或者说可以向下转型吗?语法上可以进行强制类型转换,但不一定能转换成功。我们以前面的例子来看:

Base b = new Child();
Child c = (Child)b;

Child c = (Child) b就是将变量b的类型强制转换为Child并赋值为c,这是没有问题的,因为b的动态类型就是Child,但下面的代码是不行的:

Base b = new Base();
Child c = (Child) b;

语法上Java不会报错,但运行时会抛出错误,错误为类型转换异常。一个父类的变量能不能转换为一个子类的变量,取决于这个父类变量的动态类型(即引用的对象类型)是不是这个子类或这个子类的子类。给定一个父类的变量能不能知道它到底是不是某个子类的对象,从而安全地进行类型转换呢?答案是可以,通过instanceof关键字,看下面代码:

public boolean canCast(Base b){return b instanceof Child;
}

这个函数返回Base类型变量是否可以转换为Child类型,instanceof前面是变量,后面是类,返回值是boolean值,表示变量引用的对象是不是该类或其子类的对象。

3.4.5 继承访问权限protected

变量和函数有public/private修饰符,public表示外部可以访问,private表示只能内部使用,还有一种可见性介于中间的修饰符protected,表示虽然不能被外部任意访问,但可被子类访问。另外,protected还表示可被同一个包中的其他类访问,不管其他类是不是该类的子类。我们来看个例子,这是基类代码:

public class Base {protected int currentStep;protected void step1(){}protected void step2(){}public void action(){this.currentStep = 1;step1();this.currentStep = 2;step2();}
}

action表示对外提供的行为,内部有两个步骤step1()step2(),使用currentStep变量表示当前进行到了哪个步骤,step1()step2()currentStepprotected的,子类一般不重写action,而只重写step1step2,同时,子类可以直接访问currentStep查看进行到了哪一步。子类的代码是:

public class Child extends Base {protected void step1(){System.out.println("child step " + this.currentStep);}protected void step2(){System.out.println("child step " + this.currentStep);}
}

使用Child的代码是:

public static void main(String[] args){Child c = new Child();c.action();
}

输出为:

child step 1
child step 2

基类定义了表示对外行为的方法action,并定义了可以被子类重写的两个步骤step1()step2(),以及被子类查看的变量currentStep,子类通过重写protected方法step1()step2()来修改对外的行为。

这种思路和设计是一种设计模式,称之为模板方法。action方法就是一个模板方法,它定义了实现的模板,而具体实现则由子类提供。模板方法在很多框架中有广泛的应用,这是使用protected的一种常见场景。

3.4.6 可见性重写

重写方法时,一般并不会修改方法的可见性。但我们还是要说明一点,重写时,子类方法不能降低父类方法的可见性。不能降低是指,父类如果是public,则子类也必须是public,父类如果是protected,子类可以是protected,也可以是public,即子类可以升级父类方法的可见性但不能降低。

为什么要这样规定呢?继承反映的是is-a的关系,即子类对象也属于父类,子类必须支持父类所有对外的行为,将可见性降低就会减少子类对外的行为,从而破坏is-a的关系,但子类可以增加父类的行为,所以提升可见性是没有问题的。

3.4.7 防止继承final

有的时候我们不希望父类方法被子类重写,有的时候甚至不希望类被继承,可以通过final关键字实现。final关键字可以修饰变量,而这是final的另一种用法。一个Java类,默认情况下都是可以被继承的,但加了final关键字之后就不能被继承了,如下所示:

public final class Base {//主体代码
}

一个非final的类,其中的public/protected实例方法默认情况下都是可以被重写的,但加了final关键字后就不能被重写了,如下所示:

public class Base {public final void test(){System.out.println("不能被重写");}
}

3.5 继承是把双刃剑

一方面继承是非常强大的;另一方面继承的破坏力也是很强的。继承为什么会有破坏力呢?主要是因为继承可能破坏封装,而封装可以说是程序设计的第一原则;另外,继承可能没有反映出is-a关系。

3.5.1 继承破坏封装性

什么是封装呢?封装就是隐藏实现细节,提供简化接口。使用者只需要关注怎么用,而不需要关注内部是怎么实现的。实现细节可以随时修改,而不影响使用者。函数是封装,类也是封装。通过封装,才能在更高的层次上考虑和解决问题。可以说,封装是程序设计的第一原则,没有封装,代码之间会到处存在着实现细节的依赖,则构建和维护复杂的程序是难以想象的。继承可能破坏封装是因为子类和父类之间可能存在着实现细节的依赖。子类在继承父类的时候,往往不得不关注父类的实现细节,而父类在修改其内部实现的时候,如果不考虑子类,也往往会影响到子类。子类和父类之间是细节依赖,子类扩展父类,仅仅知道父类能做什么是不够的,还需要知道父类是怎么做的,而父类的实现细节也不能随意修改,否则可能影响子类。更具体地说,子类需要知道父类的可重写方法之间的依赖关系。

3.5.2 继承没有反映is-a关系

继承关系是设计用来反映is-a关系的,子类是父类的一种,子类对象也属于父类,父类的属性和行为也适用于子类。就像橙子是水果一样,水果有的属性和行为,橙子也必然都有。但现实中,设计完全符合is-a关系的继承关系是困难的。比如,绝大部分鸟都会飞,可能就想给鸟类增加一个方法fly()表示飞,但有一些鸟就不会飞,比如企鹅。在is-a关系中,重写方法时,子类不应该改变父类预期的行为,但是这是没有办法约束的。还是以鸟为例,你可能给父类增加了fly()方法,对企鹅,你可能想,企鹅不会飞,但可以走和游泳,就在企鹅的fly()方法中,实现了有关走或游泳的逻辑。继承是应该被当作is-a关系使用的,但是,Java并没有办法约束,父类有的属性和行为,子类并不一定都适用,子类还可以重写方法,实现与父类预期完全不一样的行为。但对于通过父类引用操作子类对象的程序而言,它是把对象当作父类对象来看待的,期望对象符合父类中声明的属性和行为。如果不符合,结果是什么呢?混乱。


  1. 马俊昌.Java编程的逻辑[M].北京:机械工业出版社,2018. ↩︎

  2. 尚硅谷教育.剑指Java:核心原理与应用实践[M].北京:电子工业出版社,2023. ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/681728.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫-华为云空间备忘录导出到docx-selenium控制浏览器行为-python数据处理

背景适用情况介绍 老的荣耀手机属于华为云系统&#xff0c;家里人换了新荣耀手机属于荣耀云系统无法通过云空间将备忘录转移到新手机&#xff0c;不想让他们一个一个搞&#xff0c;于是整了一晚上想办法爬取下来。从网页抓取下来&#xff0c;然后存到docx文档中&#xff08;包…

福布斯2023年推荐:十佳项目管理软件榜单揭晓

项目管理软件可以轻松规划项目、分配任务并保持团队井井有条&#xff0c;以便满足截止日期和目标。然而当今市场上有如此多的项目管理系统&#xff0c;选择适合您需求的正确选项可能很困难。为了提供帮助&#xff0c;福布斯小型企业顾问团队分析了数十家领先的提供商&#xff0…

.NET高级面试指南专题七【SocketWebSocket】

Socket&#xff08;套接字&#xff09;是一种在计算机网络中实现通信的一种机制&#xff0c;它提供了一种标准的接口&#xff0c;使不同计算机上的程序能够通过网络进行数据交换。Socket允许在网络中的不同设备之间建立连接&#xff0c;进行双向的数据传输。 Socket通常用于实现…

Ubuntu如何使用宝塔面板部署开源论坛HadSky并结合内网穿透远程访问?

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

php数组与字符串函数

php数组与字符串函数 1. php数组2. 字符串函数 1. php数组 在php中&#xff0c;有三种类型的数组&#xff1a; 数值数组 - 带有数字ID键的数组关联数组 - 带有指定的键的数组&#xff0c;每个键关联一个值多维数组 - 包含一个或多个数组的数组 2. 字符串函数 在PHP中&#xf…

耳机壳UV树脂制作私模定制耳塞适合什么样的人使用呢?

耳机壳UV树脂制作私模定制耳塞适合以下人群使用&#xff1a; 对音质要求高的人&#xff1a;私模定制耳塞能够完美契合用户的耳朵形状&#xff0c;减少漏音和外部噪音的干扰&#xff0c;提供更好的音质体验。需要长时间佩戴耳机的人&#xff1a;私模定制耳塞能够提高佩戴舒适度…

<网络安全>《30 常用安全标准》

《常用安全标准》 1 个人信息安全 o《信息安全技术 个人信息安全规范》&#xff08;GB/T35273-2017) o《信息安全技术 个人信息去标识化指南》&#xff08;GB/T37964-2019) 2 工业控制安全 o《信息安全技术 工业控制系统安全检查指南》&#xff08;GB/T 37980-2019) o《信息…

Git基础命令,分支,标签的使用【快速入门Git】

Git基础命令&#xff0c;分支&#xff0c;标签的使用【快速入门Git】 Git基础常用命令Git工作流程工作区&#xff0c;暂存区和版本库文件状态获取Git仓库 git init | git clone查看文件状态 git status暂存已修改的文件 git add 查看已暂存和未暂存的修改 git diff提交文件更改…

【JAVA】类加载机制

目录 一、快速梳理JAVA类加载机制 1、JDK8的类加载体系 2、沙箱保护机制 3、Linking链接过程 二、一个用类加载机制加薪的故事 三、通过类加载器引入外部Jar包 四、自定义类加载器实现Class代码混淆 五、自定义类加载器实现热加载 六、打破双亲委派&#xff0c;实现同类…

VBA技术资料MF118:在多个工作表中插入页眉和页脚

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

多模态论文串讲·下【论文精读·49】最近使用 transformer encoder 和 decoder 的一些方法

大家好&#xff0c;我们今天就接着上次多模态串讲&#xff0c;来说一说最近使用 transformer encoder 和 decoder 的一些方法。 1 BLIP&#xff1a;Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 我们要过的第一篇论文…

计算机网络——11EMail

EMail 电子邮件&#xff08;EMail&#xff09; 3个主要组成部分 用户代理邮件服务器简单邮件传输协议&#xff1a;SMTP 用户代理 又名“邮件阅读器”撰写、编辑和阅读邮件输入和输出邮件保存在服务器上 邮件服务器 邮箱中管理和维护发送给用户的邮件输出报文队列保持待发…

LeetCode、435. 无重叠区间【中等,贪心 区间问题】

文章目录 前言LeetCode、435. 无重叠区间【中等&#xff0c;贪心 区间问题】题目链接及分类思路贪心、区间问题 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技…

08:K8S资源对象管理|服务与负载均衡|Ingress

K8S资源对象管理&#xff5c;服务与负载均衡&#xff5c;Ingress DaemonSet控制器污点策略容忍容忍污点 其他资源对象Job资源对象 有限生命周期CronJob资源对象 集群服务服务自动发现headless服务 实现服务定位与查找 服务类型 Ingress插件 发布服务的方式 DaemonSet控制器 Da…

Elasticsearch:适用于 iOS 和 Android 本机应用程序的 Elastic APM

作者&#xff1a;来自 Elastic Akhilesh Pokhariyal, Cesar Munoz, Bryce Buchanan 适用于本机应用程序的 Elastic APM 提供传出 HTTP 请求和视图加载的自动检测&#xff0c;捕获自定义事件、错误和崩溃&#xff0c;并包括用于数据分析和故障排除目的的预构建仪表板。 适用于 …

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示 1 纹理 规则和不规则的 2 纹理的用处 从纹理中恢复形状 3 分割与合成 4 分析纹理进行分类 通过识别纹理分析物理性质 如何区分纹理 5 寻找有效的纹理分类方法 发现模式、描述区域内模式 A对应图2 B对应图…

Java 基于微信小程序的电子商城购物系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

EL表达式和JSTL标签

1.1. EL表达式概述 EL&#xff08;Expression Language&#xff09;是一门表达式语言&#xff0c;它对应<%…%>。我们知道在JSP中&#xff0c;表达式会被输出&#xff0c;所以EL表达式也会被输出。 EL表达式的格式&#xff1a;${…}&#xff0c;例如&#xff1a;${12}…

【深度学习】S2 数学基础 P1 线性代数(上)

目录 基本数学对象标量与变量向量矩阵张量降维求和非降维求和累计求和 点积与向量积点积矩阵-向量积矩阵-矩阵乘法 深度学习的三大数学基础 —— 线性代数、微积分、概率论&#xff1b; 自本篇博文以下几遍博文&#xff0c;将对这三大数学基础进行重点提炼。 本节博文将介绍线…

mysql Day05

sql性能分析 sql执行频率 show global status like Com_______ 慢查询日志 执行时间超过10秒的sql语句 profile详情 show profiles帮助我们了解时间都耗费到哪里了 #查看每一条sql的耗时情况 show profiles#查看指定query_id的sql语句各个阶段的耗时情况 show profile fo…