技术面试八股文-Java - Python - 前端 - 算法分方向_第1页
技术面试八股文-Java - Python - 前端 - 算法分方向_第2页
技术面试八股文-Java - Python - 前端 - 算法分方向_第3页
技术面试八股文-Java - Python - 前端 - 算法分方向_第4页
技术面试八股文-Java - Python - 前端 - 算法分方向_第5页
已阅读5页,还剩83页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

技术面试八股文整理

Java·Python·前端·算法分方向

系统整理高频考点与深度解析

10大章节·200+面试题·300+代码示例

技术面试备考系列

目录

第一章技术面试备战策略与复习方法

第二章Java基础与集合框架

第三章Java并发与JVM

第四章Python基础与进阶

第五章Python异步、并发与性能优化

第六章前端JavaScript核心

第七章前端框架、浏览器与工程化

第八章数据结构与算法基础

第九章算法进阶与高频题型

第十章系统设计、行为面试与常见问题

技术面试八股文整理·Java/Python/前端/算法分方向

第一章技术面试备战策略与复习方法

1.1八股文的本质

所谓"八股文",是技术面试中被反复考察的基础知识。它们之所以被称为"八股",并非因为毫无价值,而是因

为它们构成了技术能力的地基。面试官通过这些问题,判断候选人是否具备扎实的基础、清晰的逻辑和良好的表

达。理解八股文的本质,比死记硬背答案更重要。

八股文考察三个层面的能力。知识储备:你是否知道这些基础知识。理解深度:你是背诵答案还是真正理解

原理。表达能力:能否把复杂问题讲清楚。真正优秀的候选人,不仅知道答案,还能讲清楚为什么、优缺点、适用

场景和实际案例。

1.2备战策略

阶段时长建议主要任务产出物

基础梳理2-4周系统学习核心技术栈知识笔记

专项突破2-3周针对薄弱环节加强训练错题本

项目复盘1-2周整理项目经验和技术亮点项目介绍稿

模拟面试1周与朋友互问或使用模拟平台面试反馈

持续刷题全程保持算法手感刷题记录

1.3面试回答的框架

#技术问题的回答框架

##基本框架:是什么+为什么+怎么做+优缺点

1.是什么:给出定义

2.为什么:说明背景和设计目标

3.怎么做:讲解原理和实现

4.优缺点:分析利弊

5.应用场景:说明适用场景

##示例:回答"什么是HashMap"

###是什么

HashMap是基于哈希表实现的Map接口,用于存储键值对。

###为什么

相比数组和链表,HashMap提供了接近O(1)的查找性能。

###怎么做

-底层数据结构:数组+链表+红黑树(JDK8+)

-哈希冲突:链地址法

-扩容机制:负载因子0.75,2倍扩容

-树化条件:链表长度≥8且数组长度≥64

###优缺点

优点:查找快、灵活

缺点:非线程安全、可能退化为O(n)

###应用场景

-需要快速查找

-不需要排序

-单线程场景

##高级技巧:主动补充

-与Hashtable、ConcurrentHashMap的对比

-JDK8相比JDK7的优化

-实际项目中的应用案例

-源码级别的实现细节

##回答要点

1.先说结论,再展开细节

2.主动提及关键概念(如树化、扩容)

3.展示源码理解(不要只会用)

4.关联实际经验(最好有案例)

5.留出互动空间("这块还需要我展开吗?")

1.4常见问题分类

类别考察重点应对策略

基础知识语言特性、API、原理系统学习,理解原理

项目经验实际开发、问题解决STAR法则,准备案例

算法题编程能力、思维逻辑刷题,总结模式

系统设计架构思维、技术深度分步推进,先框架后细节

行为问题团队协作、职业规划真诚表达,准备故事

反问环节主动性、关注点准备高质量问题

面试准备的核心原则:第一,理解优于背诵,背诵只能应付简单问题,理解才能应对追问;第二,关联实际

经验,把知识点和项目结合起来讲更有说服力;第三,主动展示深度,回答时主动提及相关联的知识点;第

四,保持诚实,不会的问题承认并尝试分析,比胡编乱造好得多。

第二章Java基础与集合框架

2.1Java语言特性

问题一:Java为什么是跨平台的?

Java跨平台的核心机制是"一次编写,到处运行"。Java源代码编译成字节码(.class文件),字节码运行在

JVM上。不同操作系统有各自的JVM实现,字节码在JVM上解释执行或JIT编译为机器码。因此,只要目标平台

有对应的JVM,同一份字节码就能运行。

#编译和执行过程

Java源代码(.java)

↓javac编译

字节码(.class)

↓类加载器加载

JVM

↓解释执行+JIT编译

操作系统机器码

#JVM的好处

1.平台无关:字节码不依赖具体硬件

2.安全:JVM提供沙箱机制

3.优化:JIT可以动态优化热点代码

#对比C++

C++源码→编译→特定平台的机器码(不可跨平台)

问题二:==和equals的区别?

对比项==equals

类型运算符方法

比较内容基本类型:值;引用类型:地址默认是地址,可重写为内容

可重写不可可以

NPE风险无有(调用方为null)

推荐用法基本类型、枚举、单例对象内容比较

//经典陷阱

Strings1="hello";

Strings2="hello";

Strings3=newString("hello");

System.out.println(s1==s2);//true(常量池)

System.out.println(s1==s3);//false(堆对象)

System.out.println(s1.equals(s3));//true(内容相同)

//Integer的缓存机制

Integera=127;

Integerb=127;

System.out.println(a==b);//true(-128~127缓存)

Integerc=128;

Integerd=128;

System.out.println(c==d);//false(超出缓存范围)

System.out.println(c.equals(d));//true

//重写equals必须重写hashCode

publicclassUser{

privateLongid;

privateStringname;

@Override

publicbooleanequals(Objecto){

if(this==o)returntrue;

if(o==null||getClass()!=o.getClass())returnfalse;

Useruser=(User)o;

returnObjects.equals(id,user.id)

&&Objects.equals(name,);

}

@Override

publicinthashCode(){

returnObjects.hash(id,name);

}

}

问题三:String、StringBuilder、StringBuffer

对比项StringStringBuilderStringBuffer

可变性不可变可变可变

线程安全安全(不可变)不安全安全(synchronized)

性能拼接慢快较慢

适用场景少量拼接、常量单线程拼接多线程拼接

//String不可变

Strings="hello";

s=s+"world";

//实际创建了新对象"helloworld"

//原对象"hello"未变

//StringBuilder高效拼接

StringBuildersb=newStringBuilder();

for(inti=0;i<10000;i++){

sb.append(i);

}

Stringresult=sb.toString();

//编译器优化:常量拼接

Strings="a"+"b"+"c";//编译期优化为"abc"

//变量拼接使用StringBuilder

Strings=a+b+c;//编译为newStringBuilder().append(a)...

2.2集合框架

问题四:ArrayList和LinkedList的区别

对比项ArrayListLinkedList

底层结构动态数组双向链表

随机访问O(1)O(n)

插入/删除(头尾)O(n)/均摊O(1)O(1)

插入/删除(中间)O(n)O(n)(需要先查找)

内存占用紧凑,有预留空间每个节点有前后指针,开销大

迭代性能快(连续内存)慢(指针跳转)

适用场景随机访问多头尾插入删除多

//ArrayList扩容机制

//初始容量10(懒加载)

//扩容:新容量=旧容量+(旧容量>>1)即1.5倍

privatevoidgrow(intminCapacity){

intoldCapacity=elementData.length;

intnewCapacity=oldCapacity+(oldCapacity>>1);

if(newCapacity-minCapacity<0)

newCapacity=minCapacity;

elementData=Arrays.copyOf(elementData,newCapacity);

}

//LinkedList节点

privatestaticclassNode{

Eitem;

Nodenext;

Nodeprev;

}

//性能测试建议

//大部分场景用ArrayList

//只在明确需要大量头尾插入删除时用LinkedList

//实践中LinkedList的优势场景很少

问题五:HashMap底层原理

HashMap是Java面试的必考题。JDK8之后,HashMap的底层数据结构是"数组+链表+红黑树",通过链地址

法解决哈希冲突。理解其内部机制,需要掌握哈希计算、冲突处理、扩容机制和树化条件。

//HashMap核心结构

publicclassHashMap{

//哈希桶数组

transientNode[]table;

//链表节点

staticclassNodeimplementsMap.Entry{

finalinthash;

finalKkey;

Vvalue;

Nodenext;

}

//红黑树节点

staticfinalclassTreeNodeextendsLinkedHashMap.Entry{

TreeNodeparent;

TreeNodeleft;

TreeNoderight;

TreeNodeprev;

booleanred;

}

//关键参数

staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;//16

staticfinalintMAXIMUM_CAPACITY=1<<30;

staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;

staticfinalintTREEIFY_THRESHOLD=8;//树化阈值

staticfinalintUNTREEIFY_THRESHOLD=6;//去树化阈值

staticfinalintMIN_TREEIFY_CAPACITY=64;//最小树化容量

}

//哈希计算

staticfinalinthash(Objectkey){

inth;

//高位与低位异或,让高位参与运算,减少冲突

return(key==null)?0:(h=key.hashCode())^(h>>>16);

}

//索引计算

//index=(n-1)&hash//n是数组长度

//使用位运算替代取模,前提是n是2的幂

//put方法流程

publicVput(Kkey,Vvalue){

returnputVal(hash(key),key,value,false,true);

}

finalVputVal(inthash,Kkey,Vvalue,

booleanonlyIfAbsent,booleanevict){

Node[]tab;

Nodep;

intn,i;

//1.数组为空,初始化

if((tab=table)==null||(n=tab.length)==0)

n=(tab=resize()).length;

//2.计算索引,如果位置为空,直接插入

if((p=tab[i=(n-1)&hash])==null)

tab[i]=newNode(hash,key,value,null);

else{

//3.位置不为空,处理冲突

Nodee;

Kk;

//3.1首节点key相同

if(p.hash==hash&&

((k=p.key)==key||(key!=null&&key.equals(k))))

e=p;

//3.2首节点是红黑树节点

elseif(pinstanceofTreeNode)

e=((TreeNode)p).putTreeVal(this,tab,hash,key,value);

//3.3首节点是链表节点,遍历链表

else{

for(intbinCount=0;;++binCount){

if((e=p.next)==null){

p.next=newNode(hash,key,value,null);

//链表长度达到8,尝试树化

if(binCount>=TREEIFY_THRESHOLD-1)

treeifyBin(tab,hash);

break;

}

if(e.hash==hash&&

((k=e.key)==key||

(key!=null&&key.equals(k))))

break;

p=e;

}

}

//4.找到相同key,更新value

if(e!=null){

VoldValue=e.value;

if(!onlyIfAbsent||oldValue==null)

e.value=value;

afterNodeAccess(e);

returnoldValue;

}

}

//5.新增节点,判断是否需要扩容

++modCount;

if(++size>threshold)

resize();

afterNodeInsertion(evict);

returnnull;

}

//扩容机制

finalNode[]resize(){

Node[]oldTab=table;

intoldCap=(oldTab==null)?0:oldTab.length;

intoldThr=threshold;

intnewCap,newThr=0;

if(oldCap>0){

//已达最大容量,不再扩容

if(oldCap>=MAXIMUM_CAPACITY){

threshold=Integer.MAX_VALUE;

returnoldTab;

}

//扩容为原来的2倍

elseif((newCap=oldCap<<1)<MAXIMUM_CAPACITY&&

oldCap>=DEFAULT_INITIAL_CAPACITY)

newThr=oldThr<<1;

}

//...处理其他情况

//重新分配元素

Node[]newTab=(Node[])newNode[newCap];

table=newTab;

if(oldTab!=null){

for(intj=0;j<oldCap;++j){

Nodee;

if((e=oldTab[j])!=null){

oldTab[j]=null;

//单个节点,直接重新计算索引

if(e.next==null)

newTab[e.hash&(newCap-1)]=e;

//红黑树,拆分

elseif(einstanceofTreeNode)

((TreeNode)e).split(this,newTab,j,oldCap);

//链表,拆分为低位和高位两条

else{

NodeloHead=null,loTail=null;

NodehiHead=null,hiTail=null;

Nodenext;

do{

next=e.next;

//利用扩容后多出的1位判断高低位

if((e.hash&oldCap)==0){

if(loTail==null)

loHead=e;

else

loTail.next=e;

loTail=e;

}else{

if(hiTail==null)

hiHead=e;

else

hiTail.next=e;

hiTail=e;

}

}while((e=next)!=null);

if(loTail!=null){

loTail.next=null;

newTab[j]=loHead;

}

if(hiTail!=null){

hiTail.next=null;

newTab[j+oldCap]=hiHead;

}

}

}

}

}

returnnewTab;

}

为什么容量必须是2的幂?因为索引计算用(n-1)&hash,只有n是2的幂时,n-1的二进制才是全1,

位与操作才能均匀散列。如果不是2的幂,某些位永远为0,会导致大量冲突。

为什么负载因子是0.75?这是空间和时间的权衡。负载因子太小,数组利用率低,浪费空间;太大,冲突率

高,查询变慢。0.75是经过测试的经验值,在大部分场景下表现最好。

为什么树化阈值是8?根据泊松分布,在负载因子0.75时,链表长度达到8的概率约为千万分之六。因此正

常情况下链表不会达到8,一旦达到说明哈希冲突严重,可能是哈希函数被攻击或质量差,此时树化能保证O(logn)

的性能。

问题六:ConcurrentHashMap原理

//JDK7:分段锁(Segment)

//将数据分成多个Segment,每个Segment独立加锁

//并发度=Segment数量(默认16)

//JDK8:CAS+synchronized+红黑树

//1.数组元素用volatile修饰

//2.空槽位用CAS插入

//3.非空槽位用synchronized锁住首节点

//4.冲突链过长时树化

publicclassConcurrentHashMap{

//put方法(简化)

finalVputVal(Kkey,Vvalue,booleanonlyIfAbsent){

if(key==null||value==null)thrownewNullPointerException();

inthash=spread(key.hashCode());

intbinCount=0;

for(Node[]tab=table;;){

Nodef;

intn,i,fh;

//1.数组为空,初始化

if(tab==null||(n=tab.length)==0)

tab=initTable();

//2.槽位为空,CAS插入

elseif((f=tabAt(tab,i=(n-1)&hash))==null){

if(casTabAt(tab,i,null,

newNode(hash,key,value,null)))

break;

}

//3.正在扩容,帮助迁移

elseif((fh=f.hash)==MOVED)

tab=helpTransfer(tab,f);

//4.槽位非空,synchronized锁住首节点

else{

VoldVal=null;

synchronized(f){

if(tabAt(tab,i)==f){

if(fh>=0){

//链表遍历

binCount=1;

for(Nodee=f;;++binCount){

Kek;

if(e.hash==hash&&

((ek=e.key)==key||

(ek!=null&&key.equals(ek)))){

oldVal=e.val;

if(!onlyIfAbsent)

e.val=value;

break;

}

Nodepred=e;

if((e=e.next)==null){

pred.next=newNode(hash,key,

value,null);

break;

}

}

}elseif(finstanceofTreeBin){

//红黑树插入

//...

}

}

}

if(binCount!=0){

if(binCount>=TREEIFY_THRESHOLD)

treeifyBin(tab,i);

if(oldVal!=null)

returnoldVal;

break;

}

}

}

addCount(1L,binCount);

returnnull;

}

}

//关键设计

//1.通过CAS保证无锁并发

//2.通过synchronized保证冲突时的线程安全

//3.通过多线程协助扩容提高并发能力

//4.size()是近似值,不保证精确

//与Hashtable对比

//Hashtable:全表锁,性能差

//ConcurrentHashMap:细粒度锁,高并发

2.3面向对象

问题七:重载和重写的区别

对比项重载Overload重写Override

发生范围同一个类子类与父类

方法签名方法名相同,参数列表不同方法名、参数列表都相同

返回类型可不同相同或协变(子类型)

访问修饰符可不同不能更严格

绑定时机编译期(静态)运行期(动态)

//重载

publicclassCalculator{

publicintadd(inta,intb){returna+b;}

publicdoubleadd(doublea,doubleb){returna+b;}

publicintadd(inta,intb,intc){returna+b+c;}

}

//重写

publicclassAnimal{

publicvoidspeak(){

System.out.println("...");

}

}

publicclassDogextendsAnimal{

@Override

publicvoidspeak(){

System.out.println("Woof");

}

}

第三章Java并发与JVM

3.1Java并发

问题八:volatile关键字的作用

volatile保证两个特性:可见性和有序性,但不保证原子性。可见性指一个线程修改volatile变量后,其他线程

能立即看到;有序性指禁止指令重排。

//可见性问题

publicclassVisibilityDemo{

privatestaticbooleanflag=false;//非volatile

publicstaticvoidmain(String[]args){

newThread(()->{

while(!flag){

//由于可见性问题,可能永远循环

}

System.out.println("Flagistrue");

}).start();

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){}

flag=true;//主线程修改变量

}

}

//使用volatile修复

privatestaticvolatilebooleanflag=false;

//volatile的原理

//1.写操作:在写后插入StoreBarrier,将缓存刷回主存

//2.读操作:在读前插入LoadBarrier,从主存读取最新值

//3.禁止重排:通过内存屏障实现

//volatile不保证原子性

publicclassAtomicityDemo{

privatestaticvolatileintcount=0;

publicstaticvoidincrement(){

count++;//非原子操作:读取、加1、写回

}

}

//保证原子性:使用AtomicInteger

privatestaticAtomicIntegercount=newAtomicInteger(0);

publicstaticvoidincrement(){

count.incrementAndGet();//CAS保证原子性

}

问题九:synchronized的原理

//synchronized三种用法

//1.修饰实例方法:锁住this

publicsynchronizedvoidmethod(){}

//2.修饰静态方法:锁住Class对象

publicstaticsynchronizedvoidmethod(){}

//3.修饰代码块:锁住指定对象

publicvoidmethod(){

synchronized(obj){}

}

//底层原理:monitorenter+monitorenter指令

//每个对象都有Monitor(监视器锁)

//锁升级过程

//无锁→偏向锁→轻量级锁→重量级锁

//偏向锁:只有一个线程访问,记录线程ID,无竞争

//轻量级锁:多个线程交替访问,使用CAS自旋

//重量级锁:竞争激烈,进入操作系统等待队列

//锁升级不可逆,只能升级不能降级

//synchronized与ReentrantLock对比

//synchronized:

//-JVM内置,自动释放锁

//-非公平锁

//-不支持超时、中断、多个条件

//

//ReentrantLock:

//-JDK实现,需要手动unlock

//-支持公平/非公平锁

//-支持超时、中断、多条件

//-提供tryLock等更灵活的方法

问题十:线程池的核心参数

//ThreadPoolExecutor七个参数

publicThreadPoolExecutor(

intcorePoolSize,//核心线程数

intmaximumPoolSize,//最大线程数

longkeepAliveTime,//空闲线程存活时间

TimeUnitunit,//时间单位

BlockingQueueworkQueue,//工作队列

ThreadFactorythreadFactory,//线程工厂

RejectedExecutionHandlerhandler//拒绝策略

){}

//工作流程

//1.线程数<corePoolSize:创建新线程执行

//2.线程数≥corePoolSize:加入队列

//3.队列满&线程数<maxPoolSize:创建新线程

//4.队列满&线程数=maxPoolSize:执行拒绝策略

//四种拒绝策略

//1.AbortPolicy:抛异常(默认)

//2.CallerRunsPolicy:调用者线程执行

//3.DiscardPolicy:静默丢弃

//4.DiscardOldestPolicy:丢弃最老的任务

//推荐创建方式

ThreadPoolExecutorexecutor=newThreadPoolExecutor(

4,//核心4

16,//最大16

60L,TimeUnit.SECONDS,//空闲60秒

newArrayBlockingQueue<>(200),//有界队列

newThreadFactoryBuilder()

.setNameFormat("biz-pool-%d")

.build(),

newThreadPoolExecutor.CallerRunsPolicy()

);

//为什么不推荐Executors的工厂方法?

//1.newFixedThreadPool:无界队列,可能OOM

//2.newCachedThreadPool:最大线程数是Integer.MAX_VALUE,可能OOM

//3.newSingleThreadExecutor:无界队列,可能OOM

//4.newScheduledThreadPool:最大线程数是Integer.MAX_VALUE

//线程池大小估算

//CPU密集型:CPU核数+1

//IO密集型:CPU核数*2或CPU核数/(1-阻塞系数)

//经验公式:核心数=CPU核数*目标CPU利用率

//*(1+等待时间/计算时间)

3.2JVM内存模型

问题十一:JVM内存结构

#JVM内存区域

┌─────────────────────────────────────────────┐

│线程共享区域│

├────────────────────┬────────────────────────┤

│堆Heap│方法区MethodArea│

││(元空间Metaspace)│

│-新生代││

│-Eden│-类元信息│

│-Survivor0│-运行时常量池│

│-Survivor1│-静态变量│

│-老年代││

└────────────────────┴────────────────────────┘

┌─────────────────────────────────────────────┐

│线程私有区域│

├──────────────┬──────────────┬───────────────┤

│程序计数器│虚拟机栈│本地方法栈│

│PCRegister│VMStack│NativeStack│

││││

│-当前指令│-栈帧│-native方法│

│地址│-局部变量表│的栈帧│

││-操作数栈││

││-动态链接││

││-返回地址││

└──────────────┴──────────────┴───────────────┘

#堆的分代模型

#新生代(YoungGeneration)

#-Eden:对象出生地

#-Survivor0/Survivor1:存活对象暂存区

#-比例:Eden:S0:S1=8:1:1

#

#老年代(OldGeneration)

#-长期存活的对象

#-大对象直接进入

#对象分配过程

#1.新对象优先分配到Eden

#2.Eden满触发MinorGC

#3.存活对象移到Survivor,年龄+1

#4.年龄达到15(默认),晋升老年代

#5.老年代满触发FullGC

#常用JVM参数

-Xms2g#初始堆大小

-Xmx4g#最大堆大小

-Xmn1g#新生代大小

-XX:MetaspaceSize=256m#元空间初始大小

-XX:MaxMetaspaceSize=512m#元空间最大

-XX:SurvivorRatio=8#Eden:Survivor=8:1

-XX:MaxTenuringThreshold=15#晋升年龄

-XX:+UseG1GC#使用G1垃圾收集器

问题十二:垃圾回收算法

算法原理优缺点适用区域

标记-清除标记存活对象,清除未标记简单;产生碎片老年代

标记-复制存活对象复制到新空间无碎片;浪费空间新生代

标记-整理标记存活,整理到一端无碎片;移动成本高老年代

分代收集不同代使用不同算法综合优势整体

#常见垃圾收集器

##新生代收集器

#Serial:单线程,STW

#ParNew:多线程版Serial

#ParallelScavenge:注重吞吐量

##老年代收集器

#SerialOld:单线程

#ParallelOld:多线程

#CMS:并发标记清除,注重停顿时间

##整堆收集器

#G1:Region分块,可预测停顿

#ZGC:超低停顿(<10ms)

#Shenandoah:低停顿

#G1垃圾收集器

#特点:

#1.将堆分为多个Region(默认2048个)

#2.每个Region可以是Eden/Survivor/Old/Humongous

#3.维护优先队列,优先回收收益大的Region

#4.可设置目标停顿时间

#参数:

#-XX:+UseG1GC

#-XX:MaxGCPauseMillis=200

#-XX:G1HeapRegionSize=16m

#CMS垃圾收集器

#流程:

#1.初始标记(STW):标记GCRoots直接关联对象

#2.并发标记:与用户线程并发

#3.重新标记(STW):修正并发标记期间变化

#4.并发清除:与用户线程并发

#缺点:

#1.产生碎片

#2.并发失败(ConcurrentModeFailure)

#参数:

#-XX:+UseConcMarkSweepGC

问题十三:类加载机制

#类加载过程

##五个阶段

1.加载:读取.class文件,生成Class对象

2.验证:文件格式、元数据、字节码、符号引用验证

3.准备:为静态变量分配内存,设置默认值

4.解析:将符号引用替换为直接引用

5.初始化:执行静态代码块和静态变量赋值

##双亲委派模型

BootstrapClassLoader(启动类加载器)

ExtensionClassLoader(扩展类加载器)

ApplicationClassLoader(应用类加载器)

CustomClassLoader(自定义类加载器)

#工作流程

1.收到类加载请求

2.委派给父加载器

3.父加载器继续向上委派

4.顶层加载器无法加载时,向下尝试

5.直到某个加载器能加载为止

#为什么需要双亲委派?

1.安全性:防止核心类被篡改

2.唯一性:同一个类只被加载一次

3.避免重复加载

#打破双亲委派

1.SPI机制(如JDBC):使用线程上下文类加载器

2.OSGi模块化:网状委派

3.Tomcat:Web应用隔离

#自定义类加载器示例

publicclassMyClassLoaderextendsClassLoader{

privateStringclassPath;

publicMyClassLoader(StringclassPath){

this.classPath=classPath;

}

@Override

protectedClassfindClass(Stringname)

throwsClassNotFoundException{

try{

byte[]data=loadClassData(name);

returndefineClass(name,data,0,data.length);

}catch(IOExceptione){

thrownewClassNotFoundException(name,e);

}

}

privatebyte[]loadClassData(Stringname)throwsIOException{

StringfileName=classPath+"/"

+name.replace('.','/')+".class";

try(InputStreamis=newFileInputStream(fileName);

ByteArrayOutputStreambaos=newByteArrayOutputStream()){

byte[]buffer=newbyte[1024];

intlen;

while((len=is.read(buffer))!=-1){

baos.write(buffer,0,len);

}

returnbaos.toByteArray();

}

}

}

第四章Python基础与进阶

4.1Python语言特性

问题一:Python的可变类型与不可变类型

类型可变性示例

int、float、complex不可变a=1

str不可变s="hello"

tuple不可变t=(1,2,3)

frozenset不可变fs=frozenset([1,2])

list可变lst=[1,2,3]

dict可变d={"a":1}

set可变s={1,2,3}

#不可变类型的特性

a=1

b=a

a=2

print(b)#1(b指向原对象)

#可变类型的陷阱

lst1=[1,2,3]

lst2=lst1

lst1.append(4)

print(lst2)#[1,2,3,4](共享引用)

#正确复制

lst2=lst1.copy()#浅拷贝

lst2=lst1[:]#浅拷贝

importcopy

lst2=copy.copy(lst1)#浅拷贝

lst2=copy.deepcopy(lst1)#深拷贝

#默认参数的陷阱

defadd_item(item,items=[]):#危险!

items.append(item)

returnitems

print(add_item(1))#[1]

print(add_item(2))#[1,2],而不是[2]

print(add_item(3))#[1,2,3]

#正确写法

defadd_item(item,items=None):

ifitemsisNone:

items=[]

items.append(item)

returnitems

#函数参数传递

defmodify_list(lst):

lst.append(4)#修改原列表

defreassign_list(lst):

lst=[1,2,3]#不会影响原列表(创建新对象)

a=[1,2,3]

modify_list(a)

print(a)#[1,2,3,4]

b=[1,2,3]

reassign_list(b)

print(b)#[1,2,3](未变)

问题二:Python的is和==的区别

#==比较值

#is比较身份(内存地址)

a=[1,2,3]

b=[1,2,3]

c=a

print(a==b)#True(值相同)

print(aisb)#False(不同对象)

print(aisc)#True(同一对象)

#小整数缓存

x=256

y=256

print(xisy)#True(-5~256被缓存)

x=257

y=257

print(xisy)#False(可能,取决于实现)

#字符串驻留

s1="hello"

s2="hello"

print(s1iss2)#True(字符串驻留)

s3="helloworld"

s4="helloworld"

print(s3iss4)#True

s5="".join(["hello","","world"])

print(s5iss4)#False(动态创建)

#推荐

#使用==比较值

#只在以下场景使用is:

#-判断None:xisNone

#-判断True/False:xisTrue

#-判断同一对象

问题三:Python的GIL

GIL(GlobalInterpreterLock,全局解释器锁)是CPython的一个机制,它保证同一时刻只有一个线程执行

Python字节码。这意味着Python的多线程无法实现真正的并行计算,只能并发。

#GIL的影响

import

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论