一、java语法基础
1、final
关于final的问题我曾经被问到两次,所以首先说一下这个东西。
应用于变量,表示该变量一旦被赋值则不可以改变,但是有一个特殊的:StringBuffer,看下面一个例子
final StringBuffer sb=new StringBuffer("123");
这样写 sb=new StringBuffer("");会报错,但是sb.append("111");不会报错。
final是指引用变量不能变,但变量的内容是不可以改变的。StringBuffer 只是有这样一个方法可以改变它的内容而已。
应用于方法,表示该方法不可以被重写覆盖。
应用与类,表示该类不可以被继承。
2、关于private,public,protected,friendly
可以用一个表格说明他们的权限和访问范围,当修饰元素没有修饰符时默认是friendly
- 表示不支持 √ 表示支持
修饰符 | 当前类 | 同一包 | 子孙类 | 其他包 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | - |
friendly | √ | √ | - | - |
private | √ | - | - | - |
3、基本数据类型
byte、int、short、double、float、long、double、boolean
级别从低到高为:byte,char,short(这三个平级)-->int-->float-->long-->double
由低级到高级系统会自动转换,由高级到低级需要强制转换。
4、运算符
a++ 和++a区别
两者都表示a+1但是前者表示先进行运算,然后给a+1,后者表示先a+1,再参加运算,例如:a=2;b=a++;可以理解为b=a,a=a+1;b=++a;可以理解为a=a+1,b=a;同样a--和--a类似。
& 和 &&(| 和||)区别
首先两者都表示与运算符,两边条件均为true时结果才为true,其他情况均为false;
&&有短路功能,例如a&&b,当a为false时,就不会执行b;但是a&b不管a为true或者false,b都会执行。
& 还可以当作位运算符,两边表达式不是boolean类型时,表示按位与操作。
有一个关于位运算的题:请编程实现计算1234变成二进制以后包含多少1。
一般思路就是先通过Integer.toBinaryString(int i)方法转换成二进制,然后遍历里面1的个数。
这里说一下不同进制之间的转换方法:
十进制转成十六进制:
Integer.toHexString(int i)
十进制转成八进制
Integer.toOctalString(int i)
十进制转成二进制
Integer.toBinaryString(int i)
十六进制转成十进制
Integer.valueOf("FFFF",16).toString()
八进制转成十进制
Integer.valueOf("876",8).toString()
二进制转十进制
Integer.valueOf("0101",2).toString()
其实通过位运算可以实现更快的方法。
public static void main(String[] args) { int c=0; int t=1234; while (t>0) { t=t&(t-1); c++; } System.out.print(c); }
说明一下,t&(t-1)其实是 t 的二进制进行移位运算,0&1=0,1&0=0,1&1=1。
说道移位运算还有一个:请用最有效率的方法计算出2乘以8的结果。
2<<3 一个数左移几位就相当于这个数乘以2的几次方,2*8、等价于2左移3位
5、Override和Overload的区别(重写和重载区别)。
重载是指同一个类中可以存在多个命名相同的方法,但是各个方法的参数个数或类型必须不同,并且不可以通过改变方法的返回值来表示两个方法的不通。
重写是指子类可以与父类的方法名称参数相同,等于完全把父类的方法覆盖了。但是对于异常,子类的方法必须比父类抛出更少的异常或者是父类异常的子异常。
6、java内存
java内存大概分为5块:寄存器,本地方法区,方法区,堆,栈。
栈:主要用来存储局部变量,当运算完成所在趋于结束,内存将被释放。
堆:存储数组和对象。
7、成员变量和局部变量区别
成员变量在类里面定义,只在类里面有效;局部变量存在方法中定义,只在方法所在的大括号有效。
成员变量存在于堆内存中;局部变量存在与栈内存中。
8、this 和 super
this 通常用来表示对象本身,super访问父类被子类隐藏的变量或覆盖的方法。
如果一个类继承自父类,那么子类的super()方法即对父类的初始化。当类中有两个同名变量,一个属于类(类的成员变量),而另一个属于某个特定的方法(方法中的局部变量),使用this区分成员变量和局部变量。
调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
super()和this()类似,区别是,super从子类中调用父类的构造方法,this()在同一类内调用其它方法。
super的本质是java的一个关键字,this的本质是一个指向本对象的指针。
9、单例模式
保证一个类在内存中的对象中唯一性。
步骤:1、私有化构造函数。2、创建一个静态的私有的本类对象。3、提供一个对外调用的方法。
单例模式的创建有两种:懒汉式,饿汉式
首先来看下懒汉式:
public class Single { public static Single single; private Single(){ } public static Single getInstance(){ if (single==null) { single=new Single(); } return single; } }
可以看出当需要Single的时候,随时用随时new,这样以后就不用实例了,再看下饿汉式:
public class Single { public static Single singleInstance=new Single(); private Single(){ } public static Single getInstance(){ return single; } }
比较推荐饿汉式,因为他不用考虑线程安全问题,懒汉模式下如果有两个线程同时访问,第一个线程走到if后还没有执行new,第二个线程也会判断为Null,重新创建一个实例,这样就创建了两个。当然有一种方法可以解决这个问题那就是synchronized,看一下代码:
public class Single { public static Single single; private Single(){ } public synchronized static Single getInstance(){ if (single==null) { single=new Single(); } return single; } }
当然这样是可以的,实现了同步锁,但是其实还可以进行优化,这样每次调用getInstance()都会受同步锁的影响,效率会降低。其实只要保证new的时候同步就可以了,这种方法叫双重锁定(Double-Check Locking)有兴趣的可以查一下,先看代码:
public class Single { public static Single single; private Single(){ } public static Single getInstance(){ if(sing==null){ synchronized (Single.class){ if (single==null) { single=new Single(); } } } return single; } }
10、abstract
抽象类的特点:
1:抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
2:抽象方法只定义方法声明,并不定义方法实现。
3:抽象类不可以被创建对象(实例化)。
4:只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
11、String、StringBuffer、StringBuilder区别
由于String类是final修饰的,所以string对象是不可变的,当前它也就是线程安全的。
StringBuffer和StringBuilder都是继承AbstractStringBuilder类,他们都是可变的。
由于StringBuffer对方法或者调用的方法加了同步锁,StringBuilder没有加,所以StringBuilder是非线程安全的,StringBuffer是线程安全的。
在不kao虑多线程的情况下,StringBuilder效率要高于StringBuffer。
12、==和equals区别
==表示两个对象在内存中存储的值相等,而equals是两个对象的内容相等。
比如:String a=new String("123");String =new String("123");
如果用a==b的话返回false,而a.equals(b)返回true,a和b的内容是相等的,但是a、b是不通的两个对象,他们的首地址是不一样的,所以a==b返回false。
13、JAVA集合类
有时候会有这样的题:请说出几个JAVA常用的集合类,他们都有什么区别?
集合大家并不陌生,常用的什么ArrayList、map等,下面来介绍一下JAVA的集合类。
总的来说总共分为两大部分一个是Collection接口,一个是Map接口。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
│├AbstractSet
│├HashSet
│└TreeSet
Map
├Hashtable
├HashMap
└WeakHashMap
先说下Collection,由Collection又派生出两个接口:List和set,两者最大的区别就是list允许有重复元素,set不允许有重复元素;List是有序的,而set除了TreeSet以外,都是无序的。
给定一个字符串(长度不会超过500,一定会有重复的字符),编程返回字符串内第一个重复的字符。例如:“tyuioqiwyss”,返回值:i 。
public static char getFirstRepeat(String str){ HashSet<Object> hs=new HashSet<>(); //定义一个hashset char[] m=str.toCharArray();//将字符串放进一个char数组 for (int i = 0; i < m.length; i++) { boolean b=hs.add(m[i]);//遍历数组元素放进hashset if (!b) { //由于hashset不允许有重复元素,所以当有重复元素被添加的时候就会返回false return m[i]; } } return 0; }
List下面又有ArrayList、LinkedList、Vector和stack,先说下ArrayList、LinkedList的区别:1、ArrayList是基于动态数组的数据结构,对于查找访问操作效率较高;LinkedList是基于链表的数据结构,对于元素的增加删除效率较高。两者都是非同步的。Vector和stack当然就是同步的了,stack继承自Vector,实现了一个后进先出的堆栈。
Set接口是不允许插入重复元素的。
Map的元素是用key,value的形式存储的,可以存入相同的value但是key要保证唯一。
14、Math
关于math有几个常用的方法。
Math.ceil():向上取整 Math.ceil(11.5)=12 Math.ceil(-11.5)=-11
Math.floor():向下取整 Math.floor(11.5)=11 Math.floor(-11.5)=-12
Math.round():四舍五入 Math.round(11.5)=12 Math.round(-11.5)=-11 Math.round(-11.6)=-12
15、关于异或运算
给定两个Int参数,编程返回两个参数二进制数有多少位不同。
异或运算是这样的:两位相同则为0,不同则为1。0000^0011=0011 1001^1100=0101
所以先把给定的两个参数做异或运算,得到的值在判断他的二进制里面有多少1即有多少个不同的位。
public static int Sample(int m,int n) { int s=m^n; //异或运算 int count=0;//统计1的个数 while (s>0) { s=s&(s-1);//进行与运算,类似移位运算 0&0=0 0&1=0 1&1=1 1&0=0 count++; } return count; }
16、面向对象的特点,说说对他们的理解。
封装:可以使程序实现“高内聚,低耦合”,对象是封装的最基本单位。
继承:提高软件的可重用性和可扩展性
抽象:把某些具有相同特征的事物抽象成一个具体的类,只关心我们需要的地方
多态:增加软件的灵活性和可扩展性
17、列举常见的异常及可能产生原因,异常解决方法?
NullPointerException 空指针异常 简单地说就是调用了未经初始化的对象或者是不存在的对象,比如调用了未初始化对象的方法
ArrayIndexOutOfBoundsException 数组越界 调用的数组长度与实际数组长度不一致,比如一个数组有3个元素,获取第四个元素时就会出现这个异常,所以最好先查看一下数组长度。
NumberFormatException 数字强转异常 常见于把一个字符串转为Int 类型是,Intenger.parseInt("123#")就会抛出异常。
ClassCastException 数据类型转换异常 如用户在输入日期的时候只输入月、日信息,而没有年份的信息。此时应用程序在进行数据类型转换的时候,就会出现异常。
IllegalArgumentException 方法的参数错误 比如g.setColor(int red,int green,int blue)这个方法中的三个值,如果有超过255的也会出现这个异常,因此一旦发现这个异常,我们要做的,就是赶紧去检查一下方法调用中的参数传递是不是出现了错误。
异常的处理通常我们使用try catch来处理可能出现的异常,当然我们应该是捕获这个异常来处理。下面是有关异常处理的几个关键字:
throws 捕获并向外抛出异常
throw 抛出异常
try catch 内部捕获异常并做自定义处理
finally 无论是否有异常都会被处理的语句
18、线程相关
1)实现一个线程
两种方法:继承thread和实现Runnable接口
2)sleep() wait()区别
sleep()是thread类的一个方法,他需要指定一个时间告诉线程暂停指定时间,时间到了继续执行,暂停期间依然占用CPU;wait()是Object类的一个方法,不需要指定时间,想恢复执行的话需要调用notify或者notifyAll方法,暂停期间不会占用CPU资源。
3)同步和异步
如果数据需要在线程间共享,一个线程在读取数据,另一个线程在插入数据,这个时候就需要保证同步读取。如果一个线程需要调用的方法需要执行很长时间,但其他线程又不需要该线程的数据返回时应该用异步。
4)启动一个线程是调用start()方法,run()方法是线程进入执行状态所执行的方法。
19、输入输出流
JAVA的输入输出流有两种 字节流和字符流。字节流继承InputOutStream、OutputStream,字符流继承自InputOutStreamReader、OutputStreamReader。字节流主要操作对象是byte,字符流主要操作对象是字符或者字符串。
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的
字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容。
20、内存溢出和内存泄漏(主要针对Android中的情况进行分析)
内存溢出:应用使用的内存大于机器的最大内存。
内存泄漏:cursor使用完了没有关闭;Bitmap没有回收;构造Adapter时,没有使用 convertView
21、抽象类和接口的区别
1、抽象类可以有成员变量,接口里一般没有。如果有的话默认必须是static final类型的。
2、一个类可以实现多个接口但只能继承一个抽象类
3、抽象类里面可以有非抽象的方法,接口里面的方法都是抽象的,也就是实现他的类里面必须实现他的方法。
4、抽象类中的抽象方法的访问类型可以是public、protected或者包访问类型。而接口中的抽象方法只能是public abstract类型,接口的方法前面可以不加修饰符,默认就是public abstract类型。
22、java中静态属性、静态代码块、非静态属性、非静态代码块、构造函数在new的时候执行顺序是怎样的?
对于没有继承其他类的执行顺序是这样的:
静态属性--静态代码块--非静态属性--非静态代码块--构造函数
如果继承了其他类:
父类的公共静态属性和静态代码块--自身的静态属性和静态代码块--父类的非静态属性和非静态代码块--父类的构造函数--自身的非静态属性和非静态代码块--自身的构造函数