方法区(静态区)的特点是:
- JVM只有一个方法区,被所有线程使用;
- 方法区实际上也是堆,只是用于存储类,常量相关的信息;
- 用来存放程序是永远不变或唯一的内容。
堆的特点如下:
- 堆用于存储创建好的对象和数组(数组也是对象),
- JVM只有一个堆,被所有线程分享;
- 堆是一个不连续的内存空间,分配灵活,速度慢
栈的特点如下:
- 栈的描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量,操作数,方法出口等);
- JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数,局部变量);
- 栈属于线程私有,不能实现线程间的共享;
- 栈的存储特点是“后进先出,先进后出”;
- 栈是由系统自动分配,速度快!栈就是一个连续的存储空间。
构造器:也称构造方法,用于对象的初始化。
要点:
- 通过new关键字调用
- 构造器虽有返回值,但是不能定义返回值类型(返回值类型肯定是本类),不能再构造器里使用return返回某个值。
- 如果我们没有定义构造器,则编译器会自动定义一个无参构造器。如果已经定义,则编译器不会自动添加!
- 构造器方法名必须与类名一致。
垃圾回收机制:
- 发现无用对象;
- 回收无用对象占用的内存空间
垃圾回收相关算法:
- 引用计数法
堆中每个对象都有一个引用计数器,被引用一次,计数加一,被引用变量值变为null,则计数减1,直到计数为0,则变成无用对象,优点是短发算法简单,缺点是“引用无用对象”,无法识别。
- 引用可达法(根搜索算法)
程序把所有的引用关系看作是一张图,从一个节点GC ROOT,寻找对应应用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是个没有被引用的节点,即无用的节点。
开发中容易造成内存泄漏的操作:
- 创建无用的对象
- 静态集合类的使用
- 各种连接对象(IO流对象,数据库连接对象,网络连接对象)未关闭
- 监听器的使用
创建对象的步骤:
- 分配对象空间,并将对象成员变量初始化0或空
- 执行属性值的显示初始化
- 执行构造方法
- 返回对象的地址给相关的变量。
this的本质是“创建好的对象地址”!由于在构造器方法调用前,对象已经创建。因此,在构造方法中也可以是使用this代表“当前对象”。
p
ackage ci.sk.ls;
public class User{
int id;
String name;
String pwd;
public User(){
}
public User(int id,String neme){
super();
this.id=id;
this.name=name;
}
public User(int id,String name,String pwd){
this.id=id;
this.name=name;
this.pwd=pwd;
}
public static void main(String[] args) {
User u1=new User();
User u2=new User(101,"高三");
User u3=new User(100,"高四","123456");
}
}
this最常用的用法:
- 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法,构造方法中,this总是指向正要初始化的对象。
- 使用this关键字调用重载的构造方法,避免使用相同的初始化代码块,但只能在构造方法中用,并且必须位于构造方法的第一句。
- this不能用于static方法中。
static关键字:
在类中,用static声明成员变量为静态成员变量,也称为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
(static修饰的成员变量和方法,从属于类。普通变量和方法从属于对象)
java的参数传值机制:
- 基本数据类型参数传值
传递是值的副本,副本改变不后悔影响原件
- 引用类型参数的传递
传递的是值的副本,但是引用类型指的就是“对象的地址”,因此,副本和原参数都指向了同一个“地址”,改变副本指向地址对象的值,也就意味着原参数指向对象的值也发生了改变。
引用数据类型:包括类引用,接口引用,数组引用
包机制:
包机制是java中管理类的重要手段。开发中,我们会遇到大量同名的类,通过包我们很容易解决类重名的问题,也可以实现对类有效管理。包对于类,相当于文件夹对于文件的作用。
继承使用的要点:
- 父类也叫做超类,基类,派生类
- java只有单继承,没有像C++那样的多继承,多继承会引起混乱,使得继承过于复杂,系统难以维护。
- java中类没有多继承,接口有多继承。
- 字类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法,父类私有的属性和方法)。
- 如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object.
instanceof运算符:
instanceof是二元运算符,左边是对象,右边是类:当对象是右面类或子类所创建对象时,返回true;否则返回false
方法的重写Override:
子类通过重写父类的方法,可以用自身的行为替换父类的行为。
方法重写的三个要点:
- “==”:方法名,形参列表相同
- “<=”:返回值类型和声明异常类型,子类小于等于父类
- “>=”:访问权限,子类大于父类。
Object是所有java类的根基类,也就意味着所有的java对象都拥有Object类的属性和方法。
“==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。
Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相同等”的逻辑。
Object的equals方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回true否则返回false,但是,我们可以根据我们自己的要求重写equals方法
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
若是构造放啊的第一行代码没有显式的super(....)或者this(....);那么java默认都会调用super(),含义是调用父类的无参构造方法。这里的super()可以省略。
p
ackage ci.sk.ls;
public class TestSome {
public static void main(String[] args) {
new ChildClass().f();
}
}
class FatherClass{
public int value;
public void f(){
value=100;
System.out.println("fatherclass.value="+value);
}
}
class ChildClass extends FatherClass{
public int value;
public void f(){
super.f();
value=200;
System.out.println("childClass.valule="+value);
System.out.println(value);
System.out.println(super.value);
}
}
属性/方法查找顺序;(比如:查找变量h)
- 查找当前中有没有属性h;
- 一次上溯每个父类,查看每个父类中是否有h,直到Object
- 如果没找到,则出现编译错误。
- 上面步骤,只有找到h变量,则这个过程终止。
构造方法调用顺序:
构造方法第一句总是:super(...)来调用父类对应的构造方法。所以,流程就是:先追溯到Object,然后在一次向下执行类的初始化块和构造方法,直到当前子类为止。
注:静态初始化调用顺序,与构造方法调用顺序一样,不再重复。
静态初始化块:
- 构造方法用于对象的初始化!静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止。
- 构造方法执行顺序和上页执行顺序一样。
继承树追溯:
属性/方法查找顺序(比如查找变量h)
- 查找当前类中没有属性h
- 一次上溯每个父类,查看每个父类中是否有h,直到object
- 如果没找到,则出现编译错误
- 上面步骤,只要找到h变量,则这个也终止
构造方法调用顺序:
构造方法第一句总是:super(。。)来调用父类对应的构造方法。所以,流程就是:先向上追溯到object,然后在依次向下执行类的初始化和构造方法,直到子类为止。
注:静态初始化快调用顺序,与构造方法调用顺序一样,不再重复。
封装的意义:
高内聚,低耦合:高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。
编程中封装的具体优点:
- 提高代码的安全性。
- 提高代码的复用性。
- 高内聚:封装袭击,便于修改内部代码,提高可维护性。
- 低耦合:简化外部调用,便于调用者使用,便于扩展和协议
修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
- private表示私有,只能自己类访问。
- default表示没有修饰符修饰,只有同一个包的类能访问
- protected表示可以被同一个包的类以及其他包中的所有类访问
- public表示可以被该项目的所有包中的所有类访问
类的属性的处理:
- 一般使用private处理
- 提供相应的get/set方法来访问相关的属性,这些方法通常都是public修饰的,一提供对属性的复制与读取操作(Boolean变量的get方法是is开头的)
- 一些只用于本类的辅助性的方法可以用private修饰,希望其他类调用的方法用public来修饰
pa
ckage ci.sk.ls;
public class TestSome {
private int id;
private String name;
private int age;
private boolean man;
public void setName(){
this.name=name;
}
public void setAge(){
if (age>=1&&age<=130){
this.age=age;
}else{
System.out.println("请输入正常的年龄!!!");
}
}
public static void main(String[] args) {
}
}
多态:指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现可能会不同。
多态的要点:
- 多态是方法的多态,不同属性的多态(多态与属性无关)
- 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
对象的转型:
父类引用指向子类对象,我们称这个过程为向上转型,不能调用它运行时类型方法。这时,我们就需要进行类型的强制转换。
public class Demo {
public static void main(String[] args) {
Object obj=new String("北京");//向上可以自动转型
//obj.charAt(0)无法调用。编译器认为obj是object类型而不是String类型
String str=(String)obj;//向下转型
System.out.println(str.charAt(0));//位于0索引位置的字符
System.out.println(obj==str);//true,他们两运行时是同一个对象
}
}
final关键字的作用:
- 修饰变量:被其他修饰的变量不可改变。一旦服了初值,就不能被重新赋值。
final int MAX_SPEED=120;
2,修饰方法:该方法下不可被子类重写。但是可以被重载!
fiansl void stay(){}
3,修饰类:修饰的类不能被继承。比如:Math,String等
final class A{}
代码如下:
public class Demo {
public final void shout(){
System.out.println("叫了一声");
}
}
class Dog extends Demo{
public void shout(){//出错,不能重写final修饰的方法。
System.out.println("往往能");
}
}
数组:数组是相同类型的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下表)来访问它们。
数组的三个基本特点:
- 长度是确定的。数组一旦被创建,它的大小就是不可改变的。
- 其元素必须是相同类型,不允许出现混合类型
- 数组类型可以使任何数据类型,包括基本类型和引用类型。
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。
数组的初始化方式总共有单重:静态初始化,动态初始化,默认初始化。
- 静态初始化
除了用new 关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素元素分配空间并赋值
int [] a={1,2,3};//静态初始化基本类型数据
Man[] mans={new Man(1,1),new Man(2,2)};//静态初始化引用类型数组。
- 动态初始化数组:数据定义与数组分配空间并赋值的操作分开进行
int[]
a1=new int [2];//动态初始化数组,先分配空间
a1[0]=1;//给数组元素赋值;
a1[1]=2;//给数组元素赋值;
- 数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
i
nt a2[]=new int[2];//默认值:0,0;
boolean[] b=new boolean[2];//默认值:false,false
String[] s=new String[2];//默认值:null,null
数组的遍历:
数组元素下标区间[0,length-1].我们可以通过下标来遍历数组中的元素,遍历时可以读取元素或修改元素的值。
p
ublic static void main(String[] args) {
int []a=new int [4];
//初始化数组元素的值
for(int i=0;i<a.length;i++){
a[i]=100*i;
}
//读取元素的值
for (int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
增强for循环for-each,是用来读取数组或几种所有的元素,即对数组进行遍历。
public static void main(String[] args) {
String[]ss={"aa","bb","cc"};
for (String temp:ss){//循环遍历数组把每个元素取出来放在temp变量里面
System.out.println(temp);
}
}
抽象方法:使用abstract修饰的方法,没有方法体,只有生命。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类:包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后子类必须定义具体实现。通过抽象类,
我们就可以做到严格限制子类的设计,使子类之间更加通用。
用法要点:只有抽象方法的类只能定义成抽象类
抽象类不能实例化,即不能用new实例,只能用来被自类调用,
抽象类只能用来被继承
抽象方法必须被子类实现。
public abstract class Demo {
public abstract int aa();
class iu extends Demo{
@Override
public int aa() {
return 0;
}
}
}
为什么需要接口:接口和抽象类的区别:
接口就是比“抽象类”还:“抽象”的抽象类,可以更加规范的堆子类进行约束。全面专业的实现了:规范和具体实现的分离。
抽象列还提供了某些具体实现,接口不提供任何实现,接口中所有的方法都是抽象方法。接口是完全面向规范的,规定了一批具有公共方法规范。
从接口的角度看,接口定义了可以向外部提供的服务。
从接口调用者的角度看,接口定义了实现着能提供的那些服务。
接口是两个模块之间通信的标准,通信的规范。比如能把你要设计的模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。做系统时往往采用“面向接口”的思想来设计系统。
接口和实现类不是父子关系,是实现规则的关系。比如定义一个接口Runnable,car实现它就能在地上跑,Train实现它也能在地上跑,飞机实现它也能在地上跑,Train实现它也能在地上跑,也就是说,如果他是交通工具,就一定囊在地上跑,但是一定要实现Runnable接口。
接口本质讨论:
接口就是规范,定义的一组规则,体现了现实世界中“如果你是。。。则必须能。。。”的思想。如果你是天使,则必须能飞。
接口的本质是七月,就像我们人间法律一样。制定好后大家都遵守。
面向对象的精髓,是对对象的抽象,能实现这一点就是接口。设计模式所研究的,实际上就是如何理解的去抽象。
区别:普通类:具体实现
抽象类:具体实现,规范(抽象方法)
接口:规范
抽象类和抽象方法的基本用法:
public abstract class Animal {
abstract public void shout();
}
class Dog extends Animal{
public void shout(){
System.out.println("旺旺");
}
public void seeDoor(){
System.out.println("看门");
}
}
class TestAbstractClass{
public static void main(String[] args) {
Dog a=new Dog();
a.shout();
a.seeDoor();
}
}