面向对象:以对象为基础,以事件或消息来驱动对象执行处理的程序设计技术,把构成问题事务分解成各个对象,将程序代码按一个个对象进行组织和编写,描叙某个事物在整个解决问题的步骤中的行为。
面向过程:分析出解决问题所需要的步骤,用函数把这些步骤一步步实现,使用的时候依次调用。
面向对象的特征:封装(对象的属性-变量、行为-方法封装在一个类中)、抽象(事物的相似共性之处归为一类)、继承(子类自动共享父类数据和方法)、多态(多态的实现——父类声明指向子类引用)
举例:五子棋——面向过程:开始游戏、黑子先走、绘制画面、判断输赢、轮到白子、绘制画面、判断输赢、返回步骤、输出最后结果,将上面步骤分别用函数实现;面向对象——黑白双方,这两方的行为是一样的;棋盘系统,负责绘制画面;规则系统,负责判定诸如犯规、输赢等。
Int——>String: s=i+” ”; Integer.toString(i); s=String.valueOf(i);
String ——> Int: i=Integer.parseInt(s); i=Integer.valueOf(s).intValue();
String——>char: String.charAt(index)(返回值为char,可以得到String中某一指定位置的char);
String.toCharArray()(返回值为char[],可以得到将包含整个String的char数组,key;
Char——>String: char[ ] arr; String s = String.valueOf(arr);
String s = Arrays.toString(arr);
Java将可抛出(Throwable)的结构分为三种类型:被检查的异常(Checked Exception,编译时被检查),运行时异常(RuntimeException)和错误(Error)。
Java异常机制用到的几个关键字:try(检测/监听,能抛出异常的代码放在try语句块内,发生异常,被抛出)、catch(用来捕获异常)、finally(该语句块总是被执行)、throw(用来抛出异常,用在方法内,只能抛出一个异常对象名)、throws(编译时异常地抛出必须对其处理,用在方法声明后跟(多个)异常类名)
Java线程的实现:继承Thread类和实现Runnable接口 ,使用new Thread()/new SubThread()直接调用run方法,使用new Thread(Runnable)调用runnable的run方法;应用程序可以使用Executor框架来创建线程池;还有一种方式是实现Callable接口;
同步的实现: Synchronized(保证在块开始时都同步主内存的值到工作内存,而块结束时将变量同步回主内存),wait(线程类(Thread)的方法,使一个线程处于等待状态,并释放所持有对象的lock(释放对象锁),必须在Synchronized内部调用),notify(唤醒一个处于等待状态的线程,由JVM确定唤醒哪个线程),sleep(wait是Object类的方法,不释放对象锁,让一个正在运行的线程处于睡眠状态,是一个方法,暂停线程、但监控状态仍然保持,结束后会自动恢复,必须捕获异常 ),start(启动一个线程,使线程就绪状态)
Stop():不安全;suspend():容易发生死锁;
volatile — 保证修饰后在对变量读写前都会与主内存更新。
List:有序,存取顺序一致,有索引,可以重复;
1)Arraylist:底层数组,查询快,增删慢,不是同步保护的,线程不安全,效率高;(大小可变)
2)Vector:底层数组,查询快,增删慢,线程安全,效率低;(自动增长)
3)Linklist:底层链表,查询慢,增删快,线程不安全,效率高;
Set:无序,存取顺序不一致,无索引,不能重复存储;
1)Hashset:哈希算法,非线程安全;
哈希函数分布均匀、初识容量、填装因子(=表中记录数/哈希表的长度)
如何保证元素的唯一性:先调用对象的hashcode()方法得到一个哈希值,在集合中查找是否有哈希值相同的对象,没有就直接存入集合,如果有就和哈希值相同的对象逐一进行equals()比较,结果为false存入,true则不存;
Object类的equal和hashCode方法重写,为什么?
2)Treeset:二叉树算法;
Map:双列,键唯一;每个键最多映射一个值;针对键有效,跟值无关
1)HashMap:底层为Hash算法,非synchronization(同步),线程不安全,效率高;可存储null键值;
2)HashTable:底层为Hash算法,synchronization,线程安全,效率低;
注:add()方法如果是list集合一直返回true,如果是set集合重复存储就会返回false;Removeall()删除的是交集;containsall()取交集;
相关问题:集合类以及集合框架?列举java的集合以及集合之间的继承关系?并发集合了解哪些?容器类介绍以及之间的区别(Java容器主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections))
ArrayList和LinkedList的区别以及应用场景?
List,Set,Map的区别、实现及存储方式?
(Concurrent)HashMap、TreeMap的实现原理、数据结构、源码理解?
ArrayMap、HashMap、HashTable的对比?
HashSet与HashMap怎么判断集合元素重复?
集合Set实现Hash怎么防止碰撞?
对集合进行遍历,每个集合内部存储结构不同,向上抽取接口,在每个类内部,定义自己的迭代方法;作用相当于一个位置指针一样,在获得一个集合对象的迭代器时,该指针指向第一个元素之前;循环访问集合每个元素,顺序访问;
socket即为套接字,在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一的标识网络通讯中的一个进程,“IP地址+TCP或UDP端口号”就为socket。
原理:通信两端都有socket,数据在两个Socket间通过IO传输;
Java网络支持:InetAddress:用于标识网络上的硬件资源,主要是IP地址;
URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据;
Sockets:使用TCP协议实现的网络通信Socket相关的类;
Datagram:使用UDP协议,将数据保存在用户数据报中,通过网络进行通信;
Tcp编程:tcp协议、实现通信的类(客户端Socket、服务器端ServerSocket)
Socket通信的步骤:
① 创建ServerSocket和Socket;
② 打开连接到Socket的输入/输出流
③ 按照协议对Socket进行读/写操作
④ 关闭输入输出流、关闭Socket
服务端:
① 创建ServerSocket对象,绑定监听接口;;
② 通过accept()方法监听客户端请求;
③ 通过输入流读取客户端发送的请求信息;
④ 通过输出流向客户端发送响应信息,关闭输入输出流;
客户端:
① 创建Socket对象,指明需要连接的服务器的地址和端口号;
②通过输出流向服务端发送请求信息;
③通过输入流读取服务端发送响应信息,关闭输入输出流(socket);
④
多线程之间的通信:
思考:为什么不建议客户端进行bind()?
答:当客户端没有自己进行bind时,系统随机分配给客户端一个端口号,并且在分配的时候,操作系统会做到不与现有的端口号发生冲突。但如果自己进行bind,客户端程序就很容易出现问题,假设在一个PC机上开启多个客户端进程,如果是用户自己绑定了端口号,必然会造成端口冲突,影响通信。
Udp编程:
普通字符集、标准字符集、自定义字符集、量词({m}{n,m}{m,}?:0-1次,+:至少一次;*:0次或者多次;贪婪模式、非贪婪模式次数后加?号)
字符边界:零宽匹配——^:不同于括号外,表示字符串开始的地方;$:表示字符串结束的地方;\b:单词边界,不全是\w
选择符和分组: |:或,分支组; ():捕获组;\n:反向引用;(?:exp):不保存捕获内容;
预搜索:零宽断言,对位置的匹配;(?=exp)自身位置后面能匹配表达式;(? <=exp)自身位置前面能匹配表达式;(?!exp)后面不能;(? <!exp)前面不能;
案例:电话号码匹配——固化/手机(0\d{2,3}-\d{7,9})|(1[35789]\d{9})
邮箱——[\w\-]+@[0-9a-zA-Z]+(\.[a-zA-Z]{2,3}){1,2}
网络爬虫
乱码处理
Java中正则表达式的使用:pattern p=pattern.compile(”正则表达式”);
Matcher m=p.matcher(“matcher对象”);
Boolean yn=m.matches/find/group();
获取、分组、替换、分割
区域(堆内存和栈内存):寄存器(反映虚拟机当前状态)、栈(存放基本数据类型和对象的引用,存放局部变量,生命周期跟随线程,线程/方法结束,内存释放)、堆(存放用new产生的数据,由java虚拟机自动垃圾收集器管理)、静态域(存放用static定义的静态成员)、常量池(存放常量)、方法区
堆和栈的区别:
堆的物理地址分配对对象是不连续的,因此性能慢些;栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的,所以性能快;
堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定;栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的;
堆存放的是对象的实例和数组(数据的存储);栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。
堆对于整个应用程序都是共享、可见的;栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同;
堆内存分为三部分:
1)PermanentSpace永久存储区
永久存储区是一个常驻内存区域,用于存放jdk自身所携带的ClassInterface的元数据。也就是说它存储的是运行环境必需的类信息,被装载到此区域的数据不会被垃圾收集器回收,关闭java虚拟机才会释放此区域占的内存。
2)YoungGeneration Space新生区
新生区是类的诞生、成长、消亡的区域,新生区又分两部分:伊甸区(Edenspace)和幸存区(Survivorspace),所有的类都是在伊甸区被new出来的。幸存区有两个:0区(survivor0 space)和1区(survivor1space)。当伊甸区的空间用完时,程序再创建对象,虚拟机将对伊甸区进行垃圾回收,将伊甸区中的不再被引用的对象进行销毁,然后将伊甸区中的剩余对象移动到幸存0区,如果幸存0区也满了,将对该区进行垃圾回收,然后移动到1区,如果1区也满了,就会移动到老年区。
3)Tenuregeneration space老年区
老年区保存从新生区帅选出来的java对象。
Java内存溢出: 程序在申请内存时,没有足够的内存空间供其使用,出现内存溢出;内存溢出的原因:内存中加载的数据量过于庞大,如一次从数据库取出过多数据;集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;代码中存在死循环或循环产生过多重复的对象实体;使用的第三方软件中的BUG;启动参数内存值设定的过小
内存溢出的解决方案:修改JVM启动参数,直接增加内存(-Xms,-Xmx参数);检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误;对代码进行走查和分析,找出可能发生内存溢出的位置。
Java内存泄漏:指一个不再被程序使用的对象或变量一直被占据在内存中;长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。具体主要有如下几大类:
静态集合类引起内存泄露—— HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着;
当集合里面的对象属性被修改后,再调用remove()方法时不起作用;
监听器 ——在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会;
各种连接 ——比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
单例模式——如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式)。
String:不可变的字符序列,作为参数传递,和基本数据类型一样,不改变其值;
StringBuffer:可变的字符序列,jdk1.0版本,安全,效率低,改变其值;
StringBuilder:jdk1.5版本,线程不安全,效率高;
相互转换:String ——>StringBuffer构造方法,append()方法;
StringBuffer ——>String构造方法,toString()方法,substring();
字符串中的所有字符都是Unicode字符;
思考:String为什么要设计成不可变的?
Java虚拟机:是一个可以执行Java字节码的虚拟机进程,Java源文件被编译成能被Java虚拟机执行的字节码文;
体系结构:类装载子系统(类加载器):装载具有适合名称的类或接口,字节代码解释器(安全管理器、垃圾收集器、线程管理、图形);执行引擎(执行包含在已装载的类或接口中的指令,把字节码导入虚拟机)、内存管理、错误/异常管理、线程接口、对本地方法的支持;
JVM运行时的数据结构:包含方法区、Java堆、Java栈、本地方法栈、指令计数器及其他隐含寄存器;
JAVA编译和执行过程:Java源码编译机制、类加载机制、类执行机制
JVM内存管理和垃圾回收机制GC:常见的垃圾回收算法——标记-清除算法、复制算法(Java堆中新生代的垃圾回收算法)、标记-压缩算法(或称为标记-整理算法,Java堆中老年代的垃圾回收算法)、分代收集算法;
哪些情况下的对象会被垃圾回收机制处理掉?
==:两个简单变量的值是否相等或两个变量的引用地址是否相等;
Equals:用于判断引用变量引用地址指向的存储内容是否相等(覆盖);
Hashcode:hashCode()方法返回的就是一个数值,从方法的名称上就可以看出,其目的是生成一个hash码,在集合中用到,将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
构造方法:方法名与类名相同;无返回值;一个类可以有多个构造方法;子类不可以继承父类的构造方法,只可以调用父类的构造方法;一个类想要被继承必须提供无参构造器。
接口:不能被实例化;只能包含常量声明,没有具体的实现方法,抽象方法的集合;一个类可以实现多个接口;一个接口可继承一个或者多个父接口;接口的实现implements;一个类实现接口,除非抽象类,否则接口中的方法必须一一实现;
重载:方法名相同,参数个数或参数类型、顺序不同;重写(覆盖):方法名、参数相同,私有方法不能被重写;
抽象类:用abstract关键字来修饰,不能用new运算符实例化为一个对象,非必须有抽象方法,但有抽象方法的类一定是抽象类;抽象方法不能用Private修饰符作为访问范围限制;抽象类需要被继承;抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、native访问修饰符修饰;
Static:static代码块类初始化;static成员变量(共享数据)表名该属性属于类所有,直接通过类名来访问;static方法,类方法,通过类名.方法名()调用;static方法不能被覆盖,private的方法也不可以被覆盖;
多态:把用超类或接口声明的变量去引用不同子类或实现类的现象,在不同的时刻一个具有某种类型的变量可以引用不同类型的对象,并向该变量发出的消息取决于该变量此时引用对象的实际类型;(实现多态的机制)
Final类:继承体系的终止;不可再被作为超类的类(即不能被继承)或方法不可被子类重写(不能被子类覆盖);
This:访问当前实例的成员变量和方法;super:调用父类的成员变量和构造方法;
内部类:成员内部类、静态内部类、局部内部类和匿名内部类
Static为什么不能修饰abstract:抽象类是不能实例化的,即不能被分配内存;而static修饰的方法在类实例化之前就已经别分配了内存,这样一来矛盾就出现了:抽象类不能被分配内存,而static方法必须被分配内存。所以抽象类中不能有静态的抽象方法;定义抽象方法的目的是重写此方法,但如果定义成静态方法就不能被重写。
泛型:提高安全性(将运行期的错误转换到编译期)、省去强转的麻烦;<>存放引用数据类型;前后必须一致;
=:相当于将一个数组变量的引用传递给另一个数组,如果一个数组发生改变,那么引用同一数组的变量也要发生改变;
for :循环逐一复制:效率差;
System.arraycopy:效率最高;
Array.copyOf:
clone方法:得到数组的值,而不是引用,不能复制指定元素,灵活性差一点
第一种方式:对象实现了序列化接口Serializable,这个使用的比较多,对于序列化接口Serializable接口是一个空的接口,它的主要作用就是标识这个对象时可序列化的,jre对象在传输对象的时候会进行相关的封装。
第二种方式:实现接口Externalizable;
注:如何将一个Java对象序列化到文件里?
(1)常见编码方式?
ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16
(2)静态代理和动态代理的区别,什么场景使用?
(3)谈对解析与分派的认识?
(4)对Java反射、注解、依赖注入的理解?
(5)泛型原理,并举例?