JAVA的三大特性
首先简单介绍一下Java语言吧,现在比较常见的编程语言一般分为面向过程语言和面向对象语言,面向过程语言有:C语言等等,面向对象语言有:Java,C++,PHP等等,他们到底有什么不同呢,一个简单的例子来解释。
- 把大象放冰箱需要几步?
面向过程语言 面向对象语言1.需要先把冰箱门打开2.需要把大象放进去3.把冰箱门关上然后面向对象则只需要一步操作只去控制冰箱,让冰箱学会自动开门,关门的行为,打开门,让大象进去,最后自动关门
你是否已经心里对这些语言有了一个简单的认知,这也正是Java的好处。Java具有简单性、面向对象、分布式、健壮性、安全性、平立与可移植性、多线程、动态性等特点。
JAVA语言中有三大特性,分别是封装,继承,多态。
- 封装
封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。封装其实可以认为就是一道保护屏障,保证该类的代码和数据不能被外部代码随机访问。
优点 1.良好的封装可以减少耦合 2.类内部的数据可以自由修改 3.隐藏信息,实现细节
- 继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承的特性 1.子类拥有父类非 private 的属性、方法。 2.子类可以有自己的属性,可以进行扩展 3.Java只能单继承,多继承的话只能是A继承B,B继承C依次类推 4.继承会增加类之间的耦合性 (缺点:代码独立性差)
- 多态
多态是同一个行为具有多个不同表现形式或形态的能力。
就是通过不同的内容去选择打印不同的东西。
优点 1.降低耦合度2.灵活可扩充3. 接口性,简化性
多态存在的必要三个条件
- 继承
- 重写
- 父类引用子类对象
那些有用但不为大家所熟知的 Java 特性
本文最初发表于 Piotr Mińkowski 的个人站点 Medium 网站,经作者授权,由 InfoQ 中文站翻译分享。
在本文中,你将会了解到一些有用的 Java 特性,这些特性可能你之前没有听说过。这是我最近在阅读关于 Java 的文章时,才发现和整理的私人特性清单。我不会把重点放到语言方面,而是会放到 API 方面。
你喜欢 Java,想了解它最新的特性吗?如果是的话,你可以阅读我关于 Java 8 之后新特性的文章。接下来,在本文中你将会了解到八个不为大家熟知但是非常有用的特性。那我们开始吧!
我们都知道,在 Java 中有类型众多的集合。那么你听说过 DelayQueue 吗?它是一个特殊类型的 Java 集合,允许我们根据元素的延迟时间对其进行排序。坦白来讲,这是一个非常有意思的类。尽管 DelayQueue 类是 Java 集合的成员之一,但是它位于 java.util.concurrent 包中。它实现了 BlockingQueue 接口。只有当元素的时间过期时,才能从队列中取出。
要使用这个集合,首先,我们的类需要实现 Delayed 接口的 getDelay 方法。当然,它不一定必须是类,也可以是 Java Record。
复制代码
假设我们想要把元素延迟 10 秒钟,那么我们只需要在 DelayedEvent 类上将时间设置成当前时间加上 10 秒钟即可。
复制代码
对于上面的代码,我们能够看到什么输出呢?如下所示。
好吧,我承认这个 Java 特性对于你们中的大多数人来讲并没有太大的用处,但是,我对这个特性情有独钟……Java 8 对时间处理 API 做了很多的改进。从这个版本的 Java 开始,在大多数情况下,我们都不需要任何额外的库来处理时间了,比如 Joda Time。你可能想象不到,从 Java 16 开始,我们甚至可以使用标准的格式化器来表达一天中的时段,也就是“in the morning”或者“in the afternoon”。这是一个新的格式模式,叫做 B。
复制代码
如下是我运行的结果。当然,你的结果可能会因时间不同而有所差异。
好,稍等……现在,你可能会问这个格式为什么叫做 B。事实上,对于这种类型的格式来讲,它不是最直观的名字。但也许下面的表格能够解决我们所有的疑惑。它是 DateTimeFormatter 能够处理的模式字符和符号的片段。我猜想,B 是第一个空闲出来的字母。当然,我可能是错的。
我认为,Java Concurrent 是最有趣的 Java 包之一。同时,它也是一个不太为开发者所熟知的包,当开发人员主要使用 web 框架的时候更是如此。我们有多少人曾经在 Java 中使用过锁呢?锁是一种比 synchronized 块更灵活的线程同步机制。从 Java 8 开始,我们可以使用一种叫做 StampedLock 的新锁。StampedLock 是 ReadWriteLock 的一个替代方案。它允许对读操作进行乐观的锁定。而且,它的性能比 ReentrantReadWriteLock 更好。
假设我们有两个线程。第一个线程更新一个余额,而第二个线程则读取余额的当前值。为了更新余额,我们当然需要先读取其当前值。在这里,我们需要某种同步机制,假设第一个线程在同一时间内多次运行。第二个线程阐述了如何使用乐观锁来进行读取操作。
复制代码
现在,我们同时运行这两个线程 50 次。它的结果应该是符合预期的,最终的余额是 60000。
复制代码
在 Java Concurrent 包中,有意思的并不仅仅有锁,另外一个很有意思的东西是并发累加器(concurrent accumulator)。我们也有并发的加法器(concurrent adder),但它们的功能非常类似。LongAccumulator(我们也有 DoubleAccumulator)会使用一个提供给它的函数更新一个值。在很多场景下,它能让我们实现无锁的算法。当多个线程更新一个共同的值的时候,它通常会比 AtomicLong 更合适。
我们看一下它是如何运行的。要创建它,我们需要在构造函数中设置两个参数。第一个参数是一个用于计算累加结果的函数。通常情况下,我们会使用 sum 方法。第二个参数表示累积器的初始值。
现在,让我们创建一个初始值为 10000 的 LongAccumulator,然后从多个线程调用 accumulate() 方法。最后的结果是什么呢?如果你回想一下的话,我们做的事情和上一节完全一样,但这一次没有任何锁。
复制代码
关于这个特性并没有什么大的故事。有时我们需要在十六进制的字符串、字节或字符之间进行转换。从 Java 17 开始,我们可以使用 HexFormat 类实现这一点。只要创建一个 HexFormat 的实例,然后就可以将输入的 byte 数组等格式化为十六进制字符串。你还可以将输入的十六进制字符串解析为字节数组,如下所示。
复制代码
假设我们想在排序的数组中插入一个新的元素。如果数组中已经包含该元素的话,Arrays.binarySearch() 会返回该搜索键的索引,否则,它返回一个插入点,我们可以用它来计算新键的索引:-(insertion point)-1。此外,在 Java 中,binarySearch 方法是在一个有序数组中查找元素的最简单和最有效的方法。
让我们考虑下面的例子。我们有一个输入的数组,其中有四个元素,按升序排列。我们想在这个数组中插入数字 3,下面的代码展示了如何计算插入点的索引。
复制代码
如果我们需要对 bit 数组进行一些操作该怎么办呢?你是不是会使用 boolean[] 来实现呢?其实,有一种更有效、更节省内存的方法来实现。这就是 BitSet 类。BitSet 类允许我们存储和操作 bit 的数组。与 boolean[] 相比,它消耗的内存要少 8 倍。我们可以对数组进行逻辑操作,例如:and、or、xor。
比方说,有两个 bit 的数组, 我们想对它们执行 xor 操作。为了做到这一点,我们需要创建两个 BitSet 的实例,并在实例中插入样例元素,如下所示。最后,对其中一个 BitSet 实例调用 xor 方法,并将第二个 BitSet 实例作为参数。
复制代码
如下是运行上述代码的结果:
最后,我们介绍本文最后一个有趣的 Java 特性。和其他一些样例一样,它也是 Java Concurrent 包的元素,被称为 Phaser。它与更知名的 CountDownLatch 相当相似。然而,它提供了一些额外的功能。它允许我们设置在继续执行之前需要等待的线程的动态数量。在 Phaser 中,已定义数量的线程需要在进入下一步执行之前在屏障上等待。得益于此,我们可以协调多个阶段的执行。
在下面的例子中,我们设置了一个具有 50 个线程的屏障,在进入下一个执行阶段之前,需要到达该屏障。然后,我们创建一个线程,在 Phaser 实例上调用 arriveAndAwaitAdvance() 方法。它会一直阻塞线程,直到所有的 50 个线程都到达屏障。然后,它进入 phase-1,同样会再次调用 arriveAndAwaitAdvance() 方法。
复制代码
如下是执行上述代码的结果:
Java-面向对象三大基本特性,五大基本原则
前面文章我们分享过 ,深刻理解面向对象思想在于深刻理解面向对象三大基本特性和五大基本原则,而且透切理解面向对象三大基本特性是理解面向对象五大基本原则的基础。
面向对象三大特征
1.封装(Encapsulation)
封装就是把客观世界中有共同之处的事物抽象为类,并且该类可以把数据(成员变量)和方法只提供给可信任的类或者对象操作,对其他对象进行信息隐藏不可见。简而言之,一个类就是封装了数据和方法的逻辑实体,并且在对象内部某些数据和方法是私有的,外界无法访问的,防止外界意外改变或错误使用对象私有部分,达到对数据和方法不同级别保护的目的。
- public class Person {
- private String name;
- private int age;
- public void say() {
- System.out.print(\”person say.\”);
- }
- }
2.继承(Inheritance)
如果想复用已有的类的所有代码怎么办?答案是继承,继承是可以使用现有类的所有功能,并且无需重新编写原来类的前提下对功能进行扩展的方法。继承创建的新类叫做 “ 子类 ”或 “ 派生类 ”,被继承的类叫做 “基类”、“父类”、“超类”。继承有二类:实现继承与接口继承,实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。
- public Student extends Person {
- public void say() {
- System.out.print(\”student say.\”);
- }
- public void study() {
- System.out.print(\”I like study.\”);
- }
- }
代码复用
通过 “继承” 和 “组合(Composition)” 可以用来实现代码复用,上面展示了继承,组合:
- public School {
- private Student stu;
- public School(Student stu) {
- this.stu = stu;
- }
- public void selfintroduction() {
- stu.say();
- stu.study();
- }
- }
这里选择 “继承” 或 “组合” 的原则:父类和子类的关系是 “ is a ” 选择继承,而父类和子类的关系是 “ has a ” 选择组合。
3.多态(Polymorphism)
多态指一个类实例的相同方法在不同情形有不同表现形式,多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。最常见的多态就是将子类传入父类参数中,运行时调用父类方法时通过传入的子类决定具体的内部结构或行为。
- public SelfIntroduction {
- public void self(Person person) {
- person.say();
- }
- }
上面这个 selfIntroduction(Personperson) 方法如果传递的是 Person 对象,输出 Person 对象的自我介绍,如果传递的是 Student 对象(Student 是 Person 子类),输出 Student 对象的自我介绍。
面向对象五大原则
1.单一职责原则(Single-Resposibility Principle)
一个类的功能要单一,不能包罗万象,如果要变动某个功能,只需要修改一个类或者很少量的类。比如不能把所有的职业(Student等)的行为都写入一个类中,那样会十分臃肿。
2.开放封闭原则(Open-Closed principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的,什么意思呢?如果不必改动类的源代码,就能扩充它的行为,那么这个软件实体设计就是满足开放封闭原则的。如果说我们预测到某种变化,或者某种变化发生了,我们应当创建抽象类来隔离以后发生的同类变化。还有就是暴露出去的方法(public 修饰)是可以让使用者重写扩展的,而隐藏私有(private 修饰)的是可以自己修改的,而且也不会影响既有用户的现在使用。
3.里氏替换原则(Liskov-Substituion Principle)
父类应尽可能使用接口或者抽象类来实现,子类通过实现了父类接口,能够替父类的使用地方。贯彻 GOF (Gang of Four、四人组,《设计模式》一书由四人所著)倡导的面向接口编程。这样做的好处就是,在根据新要求扩展父类接口的新子类的时候而不影响当前客户端的使用。
4.依赖倒置原则(Dependecy-Inversion Principle)
传统的结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,称为高层依赖低层。依赖倒置原则就是要逆转这种依赖关系,让高层模块不要依赖低层模块,所以称之为依赖倒置原则。
假设 B 是较 A 低的模块,但 B 需要使用到 A 的功能,这个时候,B 不应当直接使用 A 中的具体类: 而应当由 B 定义一抽象接口,并由 A 来实现这个抽象接口,B 只使用这个抽象接口,这样就达到了依赖倒置的目的,B 也解除了对A的依赖,反过来是 A 依赖于 B 定义的抽象接口。
5.接口隔离原则 (Interface-Segregation Principle)
使用接口的地方,记住使用多个专门、专一的接口比使用单个接口包括一切要好的多,接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺就是你的大量精力和时间去维护一个冗杂的接口。
参考:
https://blog.csdn.net/jiyiqinlovexx/article/details/46593053
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。