Introduction de base à Java





Que devez-vous savoir sur Java ? Cet article vous donnera une compréhension détaillée, bienvenue à collectionner !

Les bases de Java

▐Trois caractéristiques majeures de l'orientation objet  


Caractéristiques : encapsulation, héritage, polymorphisme ;
Encapsulation : abstrait des éléments dans un objet, privatise les propriétés de l'objet et fournit des méthodes accessibles au monde extérieur ;
Héritage : les sous-classes étendent de nouveaux champs de données ou fonctions et réutilisent les attributs et fonctions de la classe parent, avec un héritage unique et des implémentations multiples ;
Polymorphisme : par héritage (plusieurs sous-classes réécrivant la même méthode), ou via des interfaces (implémentation d'interfaces et substitution d'interfaces) ;

  • La différence entre Java et C++

Différences : C++ prend en charge l'héritage multiple et a le concept de pointeurs, et les programmeurs gèrent eux-mêmes la mémoire ; Java a un héritage unique et peut utiliser des interfaces pour implémenter l'héritage multiple, et Java ne fournit pas de pointeurs pour accéder directement à la mémoire du programme. a Le mécanisme de gestion automatique de la mémoire JVM n'exige pas que les programmeurs libèrent manuellement la mémoire inutilisée.

  • Principes de mise en œuvre polymorphes


L'implémentation sous-jacente du polymorphisme est la liaison dynamique, qui associe les appels de méthode aux implémentations de méthode au moment de l'exécution.

Liaison statique et liaison dynamique :

L'un est déterminé au moment de la compilation, appelé répartition statique, comme la surcharge de méthode ;

L'un d'entre eux est déterminé au moment de l'exécution, appelé répartition dynamique, tel que le remplacement de méthode (réécriture) et l'implémentation de l'interface.

implémentation polymorphe

Le cadre de pile de l'appel de méthode en cours (table de variables locales, pile d'opérations, connexion dynamique, adresse de retour) sera stocké dans la pile de la machine virtuelle. Le processus d'implémentation du polymorphisme est le processus de répartition dynamique des appels de méthode.Si la sous-classe remplace la méthode de la classe parent, alors dans l'appel polymorphe, le processus de liaison dynamique déterminera d'abord que le type réel est une sous-classe, afin de recherchez d'abord la méthode de sous-classe. Ce processus est l’essence même de la couverture des méthodes.


  • mots-clés statiques et finaux


statique : les propriétés et les méthodes peuvent être modifiées
attributs modifiés statiques :
Les attributs de niveau classe, partagés par tous les objets, sont chargés au fur et à mesure du chargement de la classe (chargés une seule fois), avant que l'objet ne soit créé, il peut être appelé directement en utilisant le nom de la classe.
méthode de modification statique :
Chargé au fur et à mesure du chargement de la classe ; il peut être appelé directement en utilisant le nom de la classe ; dans les méthodes statiques, seuls les membres statiques peuvent être appelés, et cela ne peut pas être utilisé ;
final : le mot-clé est principalement utilisé à trois endroits : variables, méthodes et classes.
variable finale modifiée :
S'il s'agit d'une variable d'un type de données de base, sa valeur ne peut pas être modifiée une fois initialisée ;
S'il s'agit d'une variable de type référence, elle ne peut pas pointer vers un autre objet après son initialisation.
méthode de modification finale :
Verrouillez la méthode pour empêcher toute classe héritante de modifier sa signification (remplacement) ; toutes les méthodes privées de la classe sont implicitement désignées comme finales.
classe finale modifiée :
Lorsque final modifie une classe, cela indique que cette classe ne peut pas être héritée. Toutes les méthodes membres d’une classe finale sont implicitement désignées comme méthodes finales.
Une classe ne peut pas être héritée. En plus du mot-clé final, le constructeur peut également être privatisé. (la classe interne n'est pas valide)

  • Classes abstraites et interfaces

Classe abstraite : une classe qui contient des méthodes abstraites, c'est-à-dire une classe modifiée avec des classes abstraites ne peut être héritée, donc la modification finale ne peut pas être utilisée et les classes abstraites ne peuvent pas être instanciées ;
Interface : Une interface est un type abstrait et une collection de méthodes abstraites. L'interface prend en charge l'héritage multiple. Les méthodes définies dans l'interface sont par défaut des méthodes abstraites modifiées par le résumé public ;
Même point :
  1. Ni les classes abstraites ni les interfaces ne peuvent être instanciées ;
  2. Les classes abstraites et les interfaces peuvent définir des méthodes abstraites, et les sous-classes/classes d'implémentation doivent remplacer ces méthodes abstraites ;
différence:
  1. Les classes abstraites ont des constructeurs, les interfaces n'ont pas de constructeurs ;
  2. Les classes abstraites peuvent contenir des méthodes ordinaires et les interfaces ne peuvent modifier les méthodes abstraites qu'avec un résumé public (disponible après Java 8) ;
  3. Les classes abstraites ne peuvent être héritées qu'une seule fois, les interfaces peuvent être héritées plusieurs fois ;
  4. Les classes abstraites peuvent définir différents types de variables membres, et les interfaces ne peuvent être que des constantes statiques modifiées par public static final ;
Scénarios d'utilisation des classes abstraites :
Non seulement vous souhaitez contraindre les sous-classes à avoir des comportements communs (sans vous soucier de la manière de les implémenter), mais vous souhaitez également disposer de méthodes par défaut et de variables d'instance ;
Scénarios d'application d'interface :
Contraindre plusieurs classes d'implémentation à avoir un comportement uniforme, mais peu importe la façon dont chaque classe d'implémentation est implémentée, il peut n'y avoir aucune connexion entre les différentes fonctions de la classe d'implémentation ;
  • Génériques et effacement générique

Référence : https://blog.csdn.net/baoyinwang/article/details/107341997
Génériques :
L’essence des génériques réside dans les types paramétrés. Ce type de paramètre peut être utilisé dans la création de classes, d'interfaces et de méthodes, appelées respectivement classes génériques, interfaces génériques et méthodes génériques.
Effacement générique :
Les génériques de Java sont des pseudo-génériques. Lors de l'utilisation de génériques, des paramètres de type sont ajoutés, qui seront supprimés lorsque le compilateur compile le bytecode généré. Ce processus est appelé effacement de type.
Les types tels que List deviendront List après la compilation. Tout ce que la JVM voit, c'est la liste, et les informations de type attachées par les génériques sont invisibles pour la JVM.
D'autres types d'éléments peuvent être ajoutés par réflexion.

  • Principes de réflexion et scénarios d’utilisation

Réflexion Java :
Cela signifie qu'à l'état d'exécution, pour n'importe quelle classe, vous pouvez connaître toutes les propriétés et méthodes de cette classe et vous pouvez appeler n'importe laquelle de ses méthodes ;
Principe de réflexion :
Reflection obtient d'abord le bytecode de la classe de réflexion en Java, puis mappe les méthodes, variables, constructeurs, etc. dans le bytecode aux classes Method, Filed, Constructor et autres correspondantes.
Comment obtenir une instance de Class :
  
  
  
  
  
1.类名.class(就是一份字节码)2.Class.forName(String className);根据一个类的全限定名来构建Class对象3.每一个对象多有getClass()方法:obj.getClass();返回对象的真实类型
scènes à utiliser :
  1. Développer des cadres communs - L'utilisation la plus importante de la réflexion est de développer divers cadres communs. De nombreux frameworks (tels que Spring) sont configurés (comme la configuration de JavaBeans, de filtres, etc. via des fichiers XML. Afin d'assurer la polyvalence du framework, il est nécessaire de charger dynamiquement différents objets ou classes et d'appeler différentes méthodes en fonction). le fichier de configuration pendant l'exécution.

  2. Proxy dynamique - En programmation d'aspect (AOP), il est nécessaire d'intercepter des méthodes spécifiques. Habituellement, la méthode du proxy dynamique est choisie. À l’heure actuelle, la technologie de réflexion est nécessaire pour y parvenir.

    JDK : le proxy dynamique par défaut de Spring doit implémenter l'interface ;

    CGLIB : sérialise les flux d'octets via le framework asm, configurable, performances médiocres ;
  3. Annotation personnalisée - l'annotation elle-même sert uniquement de marque. Elle doit utiliser le mécanisme de réflexion pour appeler l'interpréteur d'annotation en fonction de la marque d'annotation et exécuter le comportement.


  • Système d'exceptions Java

Throwable est la super classe pour toutes les erreurs ou exceptions du langage Java. Le niveau suivant est divisé en erreur et exception.

Erreur:

Fait référence aux erreurs internes et aux erreurs d'épuisement des ressources du système d'exécution Java. L'application ne lancera pas d'objets de cette classe. Si une telle erreur se produit, en plus d'informer l'utilisateur, le reste consiste à essayer de terminer le programme en toute sécurité.

Exception:RuntimeException、CheckedException;

Les erreurs de programmation peuvent être divisées en trois catégories : les erreurs de syntaxe, les erreurs logiques et les erreurs opérationnelles.

Les erreurs de syntaxe (également appelées erreurs de compilation) sont des erreurs qui se produisent pendant le processus de compilation et qui sont vérifiées par le compilateur pour détecter les erreurs de syntaxe.

Une erreur logique signifie que le résultat de l'exécution du programme n'est pas conforme aux attentes. La cause de l'erreur peut être localisée et découverte grâce au débogage.

Les erreurs d'exécution sont des erreurs qui entraînent la fin anormale du programme, et les erreurs d'exécution doivent être traitées via la gestion des exceptions.

RuntimeException : exception d'exécution, le programme doit essayer d'éviter de telles exceptions d'un point de vue logique.

Tels que NullPointerException, ClassCastException ;

CheckedException : exception vérifiée, le programme utilise trycatch pour intercepter et traiter ;

Par IOException、SQLException、NotFoundException;


  数据结构



  • ArrayList和LinkedList


ArrayList:

底层基于数组实现,支持对元素进行快速随机访问,适合随机查找和遍历,不适合插入和删除。(提一句实际上)

默认初始大小为10,当数组容量不够时,会触发扩容机制(扩大到当前的1.5倍),需要将原来数组的数据复制到新的数组中;当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。

LinkedList:

底层基于双向链表实现,适合数据的动态插入和删除;
内部提供了List接口中没有定义的方法,用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。(比如jdk官方推荐使用基于linkedList的Deque进行堆栈操作)

ArrayList与LinkedList区别:

都是线程不安全的,ArrayList适用于查找的场景,LinkedList适用于增加、删除多的场景。

实现线程安全:

可以使用原生的Vector,或者是Collections.synchronizedList(List list)函数返回一个线程安全的ArrayList集合。

建议使用concurrent并发包下的CopyOnWriteArrayList的。

Vector:底层通过synchronize修饰保证线程安全,效率较差。

CopyOnWriteArrayList:写时加锁,使用了一种叫写时复制的方法;读操作是可以不用加锁的。


  • List遍历快速和安全失败


普通for循环遍历List删除指定元素

  
  
  
  
  
for(int i=0; i < list.size(); i++){   if(list.get(i) == 5)        list.remove(i);}


 迭代遍历,用list.remove(i)方法删除元素

  
  
  
  
  
Iterator<Integer> it = list.iterator();while(it.hasNext()){    Integer value = it.next();    if(value == 5){        list.remove(value);    }}


foreach遍历List删除元素

  
  
  
  
  
for(Integer i:list){    if(i==3) list.remove(i);}


fail—fast:快速失败

当异常产生时,直接抛出异常,程序终止。

fail-fast主要是体现在当我们在遍历集合元素的时候,经常会使用迭代器,但在迭代器遍历元素的过程中,如果集合的结构(modCount)被改变的话,就会抛出异常ConcurrentModificationException,防止继续遍历。这就是所谓的快速失败机制。

fail—safe:安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。


  • 详细介绍HashMap


角度:数据结构+扩容情况+put查找的详细过程+哈希函数+容量为什么始终都是2^N,JDK1.7与1.8的区别。

参考:https://www.jianshu.com/p/9fe4cb316c05

数据结构:

HashMap在底层数据结构上采用了数组+链表+红黑树,通过散列映射来存储键值对数据。

扩容情况:

默认的负载因子是0.75,如果数组中已经存储的元素个数大于数组长度的75%,将会引发扩容操作。

【1】创建一个长度为原来数组长度两倍的新数组

【2】1.7采用Entry的重新hash运算,1.8采用高于运算。

put操作步骤: 


1、判断数组是否为空,为空进行初始化;
2、不为空,则计算 key的hash值,通过(n - 1) & hash计算应当存放在数组中的下标 index;
3、查看table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;
4、存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据;
5、若不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;
6、若不是红黑树,创建普通Node加入链表中;判断链表长度是否大于8,大于则将链表转换为红黑树;
7、插入完成之后判断当前节点数是否大于阈值,若大于,则扩容为原数组的二倍;
哈希函数:
通过hash函数(优质因子31循环累加)先拿到key的hashcode,是一个32位的值,然后让hashcode的高16位和低16位进行异或操作。该函数也称为扰动函数,做到尽可能降低hash碰撞,通过尾插法进行插入。
容量为什么始终都是2^N:
先做对数组的⻓度取模运算,得到的余数才能⽤来要存放的位置也就是对应的数组下标。这个数组下标的计算⽅法是“ (n - 1) & hash ”。(n代表数组⻓度)。方便数组的扩容和增删改时的取模。
JDK1.7与1.8的区别:
JDK1.7 HashMap:
底层是 数组和链表 结合在⼀起使⽤也就是链表散列。如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。扩容翻转时顺序不一致使用头插法会产生死循环,导致cpu100%
JDK1.8 HashMap:
底层数据结构上采用了数组+链表+红黑树;当链表⻓度⼤于阈值(默认为 8-泊松分布),数组的⻓度大于 64时,链表将转化为红⿊树,以减少搜索时间。(解决了tomcat臭名昭著的url参数dos攻击问题)

  • ConcurrentHashMap 

可以通过ConcurrentHashMapHashtable来实现线程安全;Hashtable 是原始API类,通过synchronize同步修饰,效率低下;ConcurrentHashMap通过分段锁实现,效率较比Hashtable要好。
ConcurrentHashMap的底层实现:
JDK1.7的ConcurrentHashMap底层采⽤ 分段的数组+链表 实现;采用 分段锁(Sagment) 对整个桶数组进⾏了分割分段(Segment默认16个),每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率。

JDK1.8的 ConcurrentHashMap采⽤的数据结构跟HashMap1.8的结构⼀样,数组+链表/红⿊树;摒弃了Segment的概念,⽽是直接⽤ Node 数组+链表+红⿊树的数据结构来实现,通过并发控制synchronizedCAS来操作保证线程的安全。

  • 序列化和反序列化


序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象。对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输。反序列化就是根据这些保存的信息重建对象的过程。

序列化:将java对象转化为字节序列的过程。
反序列化:将字节序列转化为java对象的过程。 
优点:
  1. 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)Redis的RDB
  2. 利用序列化实现远程通信,即在网络上传送对象的字节序列。Google的protoBuf。
反序列化失败的场景:
序列化ID:serialVersionUID不一致的时候,导致反序列化失败。

  • String


String使用数组存储内容,数组使用final修饰,因此String定义的字符串的值也是不可变的。

StringBuffer对方法加了同步锁,线程安全,效率略低于StringBuilder。


  设计模式与原则


  • 单例模式


某个类只能生成一个实例,该实例全局访问,例如Spring容器里一级缓存里的单例池。
优点:
唯一访问:如生成唯一序列化的场景、或者spring默认的bean类型。
提高性能:频繁实例化创建销毁或者耗时耗资源的场景,如连接池、线程池。
缺点:
不适合有状态且需变更的;
实现方式:
饿汉式:线程安全速度快;
懒汉式:双重检测锁,第一次减少锁的开销、第二次防止重复、volatile防止重排序导致实例化未完成;
静态内部类:线程安全利用率高;
枚举:effictiveJAVA推荐,反射也无法破坏;

  • 工厂模式

定义一个用于创建产品的接口,由子类决定生产何种产品。
优点:解耦:提供参数即可获取产品,通过配置文件可以不修改代码增加具体产品。
缺点:每增加一个产品就得新增一个产品类。

  • 抽象工厂模式


提供一个接口,用于创建相关或者依赖对象的家族,并由此进行约束。

优点:可以在类的内部对产品族进行约束。
缺点:假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。


   
   
   
   
   
面试题

  构造方法


构造方法可以被重载,只有当类中没有显性声明任何构造方法时,才会有默认构造方法。

构造方法没有返回值,构造方法的作用是创建新对象。


  初始化块


静态初始化块的优先级最高,会最先执行,在非静态初始化块之前执行。

静态初始化块会在类第一次被加载时最先执行,因此在main方法之前。


  This


关键字this代表当前对象的引用。当前对象指的是调用类中的属性或方法的对象。

关键字this不可以在静态方法中使用。静态方法不依赖于类的具体对象的引用。


  重写和重载的区别


重载指在同一个类中定义多个方法,这些方法名称相同,签名不同。

重写指在子类中的方法的名称和签名都和父类相同,使用override注解。


  Object类方法


toString默认是个指针,一般需要重写;

equals比较对象是否相同,默认和==功能一致;

hashCode散列码,equals则hashCode相同,所以重写equals必须重写hashCode;

finalize用于垃圾回收之前做的遗嘱,默认空,子类需重写;

clone深拷贝,类需实现cloneable的接口;

getClass反射获取对象元数据,包括类名、方法;

notify、wait用于线程通知和唤醒;


▐  基本数据类型和包装类



¤  拓展阅读  ¤

3DXR技术 |  终端技术 |  音视频技术

服务端技术 | 技术质量 | 数据算法


本文分享自微信公众号 - 大淘宝技术(AlibabaMTT)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

90后程序员开发视频搬运软件、不到一年获利超 700 万,结局很刑! 高中生自创开源编程语言作为成人礼——网友锐评:依托答辩 RustDesk 由于诈骗猖獗,暂停国内服务 淘宝 (taobao.com) 重启网页版优化工作 Java 17 是最常用的 Java LTS 版本 Windows 10 市场份额达 70%,Windows 11 持续下滑 开源日报 | 谷歌扶持鸿蒙上位;开源Rabbit R1;Docker加持的安卓手机;微软的焦虑和野心;海尔电器把开放平台关了 Apple 发布 M4 芯片 谷歌删除 Android 通用内核 (ACK) 对 RISC-V 架构的支持 云风从阿里离职,未来计划制作 Windows 平台的独立游戏
{{o.name}}
{{m.name}}

Je suppose que tu aimes

Origine my.oschina.net/u/4662964/blog/11104131
conseillé
Classement