java基础理论知识点

本文详细介绍了Java编程中的核心概念,包括OOP的五个原则(封装、继承、多态、抽象、单一职责原则、开闭原则、里氏代换原则、单一职责原则、接口隔离原则、依赖倒转原则)、抽象类与接口的区别、序列化、单例模式、匿名内部类、字符串操作、hashCode()和equals()的使用、内存管理和内存泄漏、Java中的Arrays与ArrayList的区别、泛型的使用以及设计模式等。文章旨在深入理解Java编程基础和最佳实践。
摘要由CSDN通过智能技术生成

Java 核心

解释一下 OOP 的概念

面向对象编程是使用类,对象,继承性,多态性,封装性和抽象的一种程序设计方法。。

抽象

在面向对象的概念中,所有对象都是由类来描述,但是反过来,并不是所有类都是用来描述对象的。如果一个类中没有包含足够信息来描绘一个具体的对象,这样的类就是抽象类。

继承

继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。 有些编程语言支持多重继承,即一个子类别可以同时有多个父类别,比如C++编程语言;而在有些编程语言中,一个子类别只能继承自一个父类别,比如Java编程语言,这时可以利用接口来实现与多重继承相似的效果。 现今面向对象程式设计技巧中,继承并非以继承类别的“行为”为主,而是继承类别的“型态”,使得元件的型态一致。另外在设计模式中提到一个守则,“多用合成,少用继承”,此守则也是用来处理继承无法在执行期动态扩充行为的遗憾。

封装

从字面上理解就是包装的意思,是指利用抽象数据类型,将数据和关于数据的操作封装起来,使其成为一个不可分割的独立实体。数据将会被保护在抽象数据类型的内部,仅能够通过暴露在表面的操作(public方法,比如setter和getter)来与这个对象进行交流和交互。用户不知道对象的内部细节,但是通过该对象提供的接口来访问对象。其好处是:减少耦合,方便地在未来修改调整自己,更加有把握地(精确地)控制成员,隐藏信息,实现细节。### 三级目录

多态

使用相同的消息,使得类作出不同的反应(继承为我们使用多态打下了基础)。Java实现多态有三个必要条件:继承、重写、向上转型。

面向对象的三个基本元素和五个原则

三个基本元素:

封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。

继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

多态: 多态性是指允许不同类的对象对同一消息作出 响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议

五个基本原则:

单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。

开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。

Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。

依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。

接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口

抽象类和接口的区别?Link

抽象类是一个可同时包含具体方法和抽象方法(方法未被实现)的类。抽象方法必须被该抽象类的子类实现。抽象类是可以继承的。

接口像是描述类的一张蓝图或者说是类的一种契约,它包含了许多空方法,这代表着它的所有的子类都应该拥有共同点。它的子类应该提供这些空方法的具体实现。一 个类需要用 implements 来实现接口,接口可以用 extends 来继承其他接口。

在设计层级理解他们的不同:

抽象的层次不同:抽象类对类的整体(包括属性,行为)都可以进行抽象,接口对类的局部进行抽象,具体来说接口仅仅是对类的行为进行抽象。
跨域不同:抽象类是 从各种子类中提取相似的部分,然后泛化成抽象类,子类可以继承这样的抽象类。 实现接口是 不存在is-a的关系的类们,你不可以称同样可以飞行的飞机和鸟为同一个抽象类,但是他们可以有同样的接口fly-able。抽象类的父类和派生类在概念上一致,接口的原生类和派生类在仅仅在局部行为上一致。
设计层次不同:抽象类是从一堆在底层的子类们来进行抽象提取,从下往上,从而产生抽象类;接口是在直接定义的高度来声明的,然后从这个高度上往下实现此接口。抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。

序列化是什么?如何实现它?

序列化是一种将对象转换为字节流的过程,目的是为了将该对象存储到内存中,等后面再次构建该对象时可以获取到该对象先前的状态及数据信息。Java中,有两种方式可以实现序列化,既可以实现Serializable接口,也可以实现Parcelable接口。然而,在Android中,我们不应该使用Serializable接口。因为Serializable接口使用了反射机制,这个过程相对缓慢,而且往往会产生出很多临时对象,这样可能会触发垃圾回收器频繁地进行垃圾回收。相比而言,Parcelable接口比Serializable接口效率更高,性能方面要高出10x多倍。

什么是单例?

单例模式指的是一个类只能被初始化一次,即只有一个实例。单例模式限定一个类只能拥有一个实例。这在系统中只需要一个实例来和其他模块协调工作时是很实用的。单例普遍使用在只需要一个或是限制一定数量实例的系统中。(wikipedia)

什么是匿名内部类?

普通的类可以自然地实例化他自己,相反地,内部类是这样的类: 一定要绑定上一个外部类才能进行实例化的类。

内部类提供了一种模拟车和车轮的机制,车是外部类,车轮是内部类

而匿名内部类也就是没有名字的内部类,因为没有名字,所以只能使用一次。

使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

对字符串进行 == 和 equals() 操作时有什么区别?

== 比较两个字符串的地址,初学者很经常拿来比较其内容,将会导致出现不等的情况。 equals()是String这个类重写的一个方法,平常的类的equals()也仅仅是比较两个变量的地址,而String类的equals()重写后,将依次比较其串中的字符。

hashCode() 和 equals() 何时使用?

一般是在想要人性化地(而不是计算机式地,比较地址那样)比较两个对象的时候,我们需要使用这两个方法,或者说我们要重写这两个方法,而且有如下的原则:

hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”

Java 中的 final, finally 和 finalize?

final: 修饰变量,方法,类; 修饰变量时表明这对象的值不可变,你不能为这个变量赋一个新的值,或者这样说:对基本类型而言,你不能改变其数值,对于引用,你不能将其指向一个新的引用(而引用自身是可以改变的)。 修饰方法时表明我们希望把这个方法锁定,以防止任何继承类修改它的含义,这样会确保在继承中,我们的final方法的行为不会改变,并且不会被覆盖。使用final方法的另一个考虑是效率问题:在Java早期的时候,遇到final方法,编译器会将此方法调用转为内嵌调用,如此一来以减小方法调用产生的开销。 修饰类的时候表明你不打算继承该类,而且也不允许别人这样做。
finally:是异常处理中进行收场处理的代码块,比如关闭一个数据库连接,清理一些资源占用的问题。不管有没有异常被捕获,finally子句中的代码都会被执行。
finalize:finalize出现的原因在于: 我们一定需要进行清理动作。Java没有用于释放对象的,如同C++里的delete调用,的方法,而是使用垃圾回收器(GC)帮助我们释放空间。当垃圾回收器准备释放对象占用的存储空间的时候,将首先调用其finalize()方法。

什么是Java中的内存泄露

导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
这里引用一个常看到的例子,在下面的代码中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。
Vector v = new Vector(10);
for (int i = 1; i < 100; i++)
…{
 Object o = new Object();
 v.add(o);
 o = null;
}//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。
实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因。 再引用另一个例子来说明Java的内存泄漏。假设有一个日志类Logger,其提供一个静态的log(String msg),任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中。
Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)的时候,都首先将message的值写入temp中(以当前线程+当前时间为键),在退出之前再从temp中将以当前线程和当前时间为键的条目删除。注意,这里当前时间是不断变化的,所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目。这样,任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用,而无法得到回收,这种对象保持就是我们所说的Java内存泄漏。总的来说,内存管理中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。

Java是如何管理内存

为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。
Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。
在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。虽然,我们有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者可能使用不同的算法管理GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。

java中Arrays和ArrayList的区别

总结一下:就是

Arrays:一个包含许多和操纵数组有关方法的类,比如排序和查找,它继承自Object类。
ArraysList:是一个实现了List接口的类,它可以实现数组的大小可变,方便地增加和删除元素


java.util.Arrays类
能方便地操作数组,它提供的所有方法都是静态的。具有以下功能:
² 给数组赋值:通过fill方法。
² 对数组排序:通过sort方法,按升序。
² 比较数组:通过equals方法比较数组中元素值是否相等。
² 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。

import java.util.Arrays;
public class TestArrays {
public static void output(int[] array) {
if (array!=null) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
}
System.out.println();
}
public static void main(String[] args) {
int[] array = new int[5];
//填充数组
Arrays.fill(array, 5);
System.out.println(“填充数组:Arrays.fill(array, 5):”);
TestArrays.output(array);

//将数组的第2和第3个元素赋值为8
Arrays.fill(array, 2, 4, 8);
System.out.println(“将数组的第2和第3个元素赋值为8:Arrays.fill(array, 2, 4, 8):”);
TestArrays.output(array);

int[] array1 = {7,8,3,2,12,6,3,5,4};
//对数组的第2个到第6个进行排序进行排序
Arrays.sort(array1,2,7);
System.out.println(“对数组的第2个到第6个元素进行排序进行排序:Arrays.sort(array,2,7):”);
TestArrays.output(array1);

//对整个数组进行排序
Arrays.sort(array1);
System.out.println(“对整个数组进行排序:Arrays.sort(array1):”

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值