java中基础知识
1、面向对象和面向过程的区别?
(1)面向过程:面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
1)优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
2)缺点:没有面向对象易维护、易复用、易扩展
(2)面向对象:面向对象是把构成问题事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
1)优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
2)缺点:因为面向对象需要封装成类,类调用时需要实例化成对象,开销比较大,性能比面向过程低。
2、如何理解万物皆对象:在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心.面向对象有三大特性,封装、继承和多态。封装和继承是为了使代码重用,那么多态则是为了实现接口重用。
(1)封装:就是将一类事物的属性和行为抽象成一个类,使其属性私有化,行为公开化,提高了数据的隐秘性的同时,使代码模块化。
(2)继承:继承则是进一步将一类事物共有的属性和行为抽象成一个父类,而每一个子类是一个特殊的父类--有父类的行为和属性,也有自己特有的行为和属性。这样做扩展了已存在的代码块,进一步提高了代码的复用性。子类会继承父类的所有东西,而修饰符只是影响属性或者方法对外是否可见
(3)多态:多态的一大作用就是为了解耦--为了解除父子类继承的耦合度。多态就是允许父类引用(或接口)指向子类(或实现类)对象。
1)多态的变量与方法:必须有子父类关系,使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
a、多态成员变量:编译和运行都看左边;子父类存在同名的成员变量时,默认访问的是父类的成员变量数据。
Fu f=new Zi();System.out.println(f.num);//f是Fu中的值,只能取到父中的值。
原因:堆中的实例对象中对象头存放的是对象运行时的数据,如hashcode,锁标识,类型指针,对象体中存放的是对象的成员变量,成员变量包括从父类继承过的成员变量和本类的成员变量,若父类和子类有重名的变量,则需要看对象的静态类型,静态类型就是声明类型,如果声明类型是父类那么就直接选取父类的成员变量,如果声明类型是自己的类那么就选取本身类的成员变量,所以选取同名的成员变量的时候是根据静态类型来的。
b、多态成员非静态方法:编译看左边,运行看右边;子父类存在同名的非静态函数的时候,默认是调用子类的成员函数。
Fu f1=new Zi();System.out.println(f1.show());//f1的门面类型是Fu,但实际类型是Zi,所以调用的是重写后的方法。
c、多态成员静态方法:子父类存在同名的静态函数时,默认是调用父类的成员函数。
原因:方法其实是在方法区存放着一张虚表,虚表中存放的是方法的实际入口地址,在实际对象调用的时候虚表会做动态的改变,若一个方法在子类中被重写了,而此时静态类型又是父类,那么就把虚表中被重写方法的实际入口地址,替换成被重写的方法的入口地址,所以在调用的时候是访问不到被重写的旧方法的。而静态方法直接就不能被重写,更别说调用被重写之后的方法,而且静态方法是属于类的,所以在调用的时候,直接调用的是本类的方法,与运行时对象是没关系的。
2)多态的实现方式
a、重写:就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
注意事项:发生在父类与子类之间;方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同,访问修饰符的限制一定要大于等于被重写方法的访问修饰符(public>protected>default>private)
b、重载:在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数类型顺序不同)则视为重载。重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
3)多态的转型
a、向上转型:多态本身就是向上转型过的过程即father exm=new child(),向上转型会丢失子类的新增方法,同时会保留子类重新的方法。
b、向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型即child exm=(exm) father();向下转型可以得到子类的所有方法(包含父类的方法),适用于使用子类特有功能时。
3、修饰符的解释:类之间的关系:public,protected,private;类之间的关系做一个简单的定义,对于继承自己的class,base class可以认为他们都是自己的子女,而对于和自己一个目录下的classes,认为都是自己的朋友。
(1)作用域
1)public:表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
2)private:表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
3)protected:对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
4)static:
a、被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享;被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。
b、静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块->非静态代码块->构造方法) 该类不管创建多少对象,静态代码块只执行一次.
c、非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有,意味着它的创建是不需要依赖外围类的创建。它不能使用任何外围类的非static成员变量和方法。
5)final:可变性
a、修饰类:final修饰的类不能被继承,final类中的所有成员方法都会被隐式的指定为final方法;
b、修饰方法:父类的final方法是不能被子类所覆盖的,也不能被重写.
c、修饰成员变量:final成员变量表示常量,只能被赋值一次,赋值后值不再改变。当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。因为引用是一个地址,final地址的值不发生变化。
6)volatile关键字
用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,则每个线程可以拷贝到不同的 CPU cache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。volatile的性能:volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。
a、保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过CPU cache
b、禁止指令重排序优化,重排序是指编译器和处理器为了优化程序性能而对指令序列不按程序规定的顺序分开发,而是对指令序列进行重新排序再来分发的一种手段。
7)transient:可否被序列化
8)super:用于从子类访问父类的变量和方法。super()
调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。
9)this:用于引用类的当前实例,this 调用本类中的其他构造方法时,也要放在首行;his和super是属于对象范畴的东西,而静态方法是属于类范畴的东西。
10)Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。
4、内部类与匿名类的区别?
1、内部类:一个类定义在另外一个类的内部,这个该类就被称为内部类。内部类分为成员内部类(定义在外部类的成员位置)和局部内部类(定义在外部类的方法里面)。
2、匿名内部类:没有名字的类,假如一个局部内部类只被用一次(只用它构建一个实例对象),就可以不用对其命名了,这种没有名字的类被称为匿名内部类;适用于一个对象只使用一次的场景,匿名对象使用完毕就变成垃圾被回收, 匿名对象可以作为实际参数传递,而匿名内部类没有类名,所以匿名类不能含有构造器。
5、java与C++的区别
(1)都是面向对象的语言,都支持封装、继承和多态。
(2)Java 不提供指针来直接访问内存,程序内存更加安全
(3)Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是可以实现多个接口
(4)Java 有自动内存管理机制,不需要程序员手动释放无用内存,C++需要手动释放
(6)在 C 语言中,字符串或字符数组最后都会有一个额外的字符‘\0’来表示结束。但是,Java 语言中没有结束符这一概念。Java里面一切都是对象,是对象的话,字符串肯定就有长度,即然有长度,编译器就可以确定要输出的字符个数,当然也就没有必要去浪费那1字节的空间用以标明字符串的结束了。
6、接口与抽象类的区别
(1)接口的方法默认是 public,可以有静态方法与抽象方法;而抽象类可以有非抽象的方法,抽象方法可以有 public、protected 和private(被private修饰的方法不能被继承)
(2)一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
(3)接口中的所有属性默认为:public static final,接口中的所有方法默认为:public;抽象类中的静态成员变量的访问类型可以任意
(4)从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。抽象类描述的是“是不是”的问题,而接口描述的是“有没有”的问题;
7、java中的值传递和引用传递
(1)按值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。其实是直接复制了一份数据过去,因为是直接复制,所以这种方式在传递时如果数据量非常大的话,运行效率自然就变低了,所以java在传递数据量很小的数据是值传递,比如java中的各种基本类型:int,float,double,boolean等类型的
(2)按引用传递:引用传递其实就弥补了上面说的不足,如果每次传参数的时候都复制一份的话,如果这个参数占用的内存空间太大的话,运行效率会很底下,所以引用传递就是直接把内存地址传过去,实际参数与形式参数指向同一个地址。也就是说引用传递时,操作的其实都是源数据。
8、Java中的深拷贝与浅拷贝
1、浅拷贝:对于基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝,此为浅拷贝
2、深拷贝:对于基本数据类型进行值传递,对引用数据类型,创建新对象,并复制此内容此为深拷贝
注意:无论浅深拷贝都需要实现clone()方法来完成操作。所有调用clone方法的对象,都必须实现Cloneable接口,否则会抛出异常。
9、Java中==与equals的区别有哪些
(1)"==" 的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
(2)类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
(3)类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
10、装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
装箱和拆箱存在的意义:值类型是数据的容器,它存储在堆栈上,不具备多态性,使用System.Object作为所有类型的基类,但是Obejct是引用类型,而作为值类型的基类System.ValueType,是从System.Object派生出来的,这就产生了矛盾,装箱和拆箱就是为了解决这两种类型之间的差异。
//自动装箱 Integer total = 99; //自动拆箱 int totalprim = total; Integer total = 99; 执行上面那句代码的时候,系统为我们执行了: Integer total = Integer.valueOf(99); int totalprim = total; 执行上面那句代码的时候,系统为我们执行了: int totalprim = total.intValue();