——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
忠告:这些零碎的小知识不能小看它,面试的时候老问这些,我知道有这个东西但说的时候说的不全面,一种似曾相识的感觉,这样并不好,它会降低你的分数,其实这也是给我自己提个醒。
1.位运算
通常我们交换两个变量都要通过第三方变量,现在通过异或运算也能实现变量的交换,请看以下代码:
//交换x和y的值
public static void swap(int x, int y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变。
即:a^b^b=a。将a=a^b代入b=a^b则得b=a^b^b=a;同理可以得到a=b^a^a=b;轻松搞定交换。
2.&和&&的区别。
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str != null && !str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException如果将&&改为&,则会抛出NullPointerException异常。If(x==33 & ++y>0) y会增长,If(x==33 && ++y>0)不会增长
&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f的结果为0x01。
3.Java 的 char型变量是两个字节,可以存储一个汉字!!
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
4、”==”和equals方法的区别
==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。
如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。
equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:
String a=new String("abc");
String b=new String("abc");
两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。
如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码如下:
boolean equals(Object o){
return this==o;
}
这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可认为两个对象的内容是相同的。
5.内存结构
Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,
有对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
5.1栈内存
用于存储局部变量,当数据使用完,所占空间会自动释放。
5.2堆内存
数组和对象,通过new建立的实例都存放在堆内存中。
每一个实体都有内存地址值
实体中的变量都有默认初始化值
实体不在被使用,会在不确定的时间内被垃圾回收器回收
5.3方法区,本地方法区,寄存器
6.static关键字:
用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
随着类的加载而加载
优先于对象存在
被所有对象所共享
可以直接被类名调用
使用注意
静态方法只能访问静态成员
静态方法中不可以写this,super关键字
主函数是静态的
7、作用域public,private,protected,以及不写时的区别
这四个作用域的可见范围如下表所示。
说明:如果在修饰的元素上面没有写任何访问修饰符,则表示friendly。
8.super和this的用法
this代表本类对象的引用
super代表父类的内存空间的标识。
当子父类出现同名成员时,可以用super进行区分
子类要调用父类构造函数时,可以使用super语句。
9.override覆盖
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写。
父类中的私有方法不可以被覆盖。
在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
覆盖注意事项:
覆盖时,子类方法权限一定要大于等于父类方法权限
静态只能覆盖静态。
覆盖的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
class Parent {
String name = "234";
public Parent() {
System.out.println("Parent 构造方法");
System.out.println(this.getClass());
info(); // 如果new的是子类对象 会优先使用子类的覆盖方法
}
public static void test() {
System.out.println("Parent static");
}
public void info() {
name = "CC";
System.out.println("Parent info");
}
}
class Child extends Parent {
public Child(int i) {
System.out.println("Child 构造方法");
}
public static void test() {
System.out.println("Child static");
}
public void info() {
System.out.println("Child -- info");
}
}
10、Overload和Override的区别
override可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
overload可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响;
4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
11.final关键字
final可以修饰类,方法,变量。
final修饰的类不可以被继承。
final修饰的方法不可以被覆盖。
final修饰的变量是一个常量。只能被赋值一次。
内部类只能访问被final修饰的局部变量。
12、使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:
final StringBuffer a=new StringBuffer(“immutable”);
执行如下语句将报告编译期错误:
a=new StringBuffer(“”);
但是,执行如下语句则可以通过编译:
a.append(“abc”);
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
publicvoid method(final StringBuffer param)
{
}
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append(“a”);
13.抽象类概述
1抽象定义:
抽象就是从多个事物中将共性的,本质的内容抽取出来。
例如:医生和教师共性都是人,人就是抽象出来的概念。
抽象类:
Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,
包含抽象方法的类就是抽象类。
抽象方法的由来:
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,
并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
例如:医生和教师都有工作的方法,可是工作内容是不一样的。所以抽象出来的人虽然有工作功能,
但是并不明确工作的细节。
2抽象类的特点
抽象类和抽象方法必须用abstract关键字来修饰。
抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符abstract返回值类型函数名(参数列表);
抽象类不可以被实例化,也就是不可以用new创建对象。原因如下:
抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。
例如:人是一个抽象的概念,真正存在的是医生和教师。
而且抽象类即使创建了对象,调用抽象方法也没有意义。
抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,
否则该子类也是抽象类。
public abstract class Employee {
public abstract void setName(String name);
public abstract String getName();
public abstract void setSex(char sex);
public abstract char getSex();
public abstract void setAge(int age);
public abstract int getAge();
public abstract void setJob(String job);
public abstract String getJob();
}
public class User extends Employee {
private String name;
private String job;
private char sex;
private int age;
public String like; // 爱好
public void worker() {
System.out.println(this.name + "工作中");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getLike() {
return like;
}
public void setLike(String like) {
this.like = like;
}
}
public class Company {
public String companyName;// 公司名称
public String companyAdress;// 公司地址
public String companyBoss;// 公司老总
public List<Employee> employees;// 公司员工
}
14.接口
格式:publicinterface接口名称{}
接口中的成员修饰符是固定的。
成员常量:publicstaticfinal
成员函数:publicabstract
发现接口中的成员都是public的。
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
1接口的特点
接口是对外暴露的规则。
接口是程序的功能扩展。
接口的出现降低耦合性。
接口可以用来多实现。
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系。
2接口与抽象的区别于共性
共性:都是不断抽取出来的抽象的概念
区别:
1)抽象类只能被继承,而且只能是单继承。
接口需要被实现,而且是多实现。
2)抽象类中可以定义非抽象方法,子类可以直接继承使用。
接口中的方法都是抽象的,需要子类去实现。
3)抽象类使用的是 “is a”关系
接口使用的是”like a”关系
4)抽象类中的成员修饰符可以自定义
接口中的成员修饰符是固定的。全都是public的。
15.多态
定义:函数本身就具备多态性,某一种事物有不同的具体体现。
体现:父类引用或者接口的引用指向了自己的子类对象。//Animal a=new Cat();
多态的好处:提高了程序的扩展性。
多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
多态的前提:
1:必须要有关系,比如继承、或者实现。
2:通常会有覆盖操作。
多态的特点
成员函数:
编译时:要查看引用变量所属的类中是否有所调用的成员。
在运行时:要查看对象所属的类中是否有所调用的成员。
成员变量:
只看引用变量所属的类。
简单示例:
class Animal {
public void eat() {
System.out.println("吃");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public static void main(String[] args) {
Animal a = new Cat();// 这个就是多态的实现
a.eat();
}
}
抽象类和接口的设计原则以及各特点:
1、抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
2、跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在”is-a”关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已。
3、设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。