1、二维数组在内存中的存储情形:在堆中的实体在没有人为初始化时都会系统默认初始化,int类型默认为0 ,double默认为0.0,引用型默认初始值为null,字符串的默认值也是null。
因为堆里一位数组里放的还是引用型的实体数据数组,所以一维数组里最初值默认都是null。同时,每个二维数组里小数组的长度也可以不相同。输出语句System.out.println()打印出来的全是字符串!!!
凡是拿着引用型变量(没有指向即null,例如指向数组的变量里存的数组地址为null,也即不存在这个数组,自然无法去操作这个不存在的数组)去操作实体,报的异常都是:“NullPointerException”没有指向的错误。、
下图中由于int [ ] [ ] arr 在初始化时没有指定第二维数组,也即没有开辟第二维数组的空间,所以arr[ ]包括arr[0]里的值都为null,也即arr[0]没有指向小数组,自然无法操作小数组的第一个元素,所以写arr[0][0]会报错!
注意arr里的值存的是[ [ I@表明是二维整型数组的地址,arr[0]存的是一位数组的地址故为 [ I@
2、二维数组的静态初始化方式: int [ ][ ] arr = { [{1,2},{2,3}} 。
3、面向对象的思想对象就是实体的一种,而面向过程的强调过程(动作),动作也即各种函数的调用。面向对象强调对象(实体)。面向过程也可以理解为各种函数对数据进行操作,而面向对象则是把数据和函数封装为对象,有了对象就有了一切,直接操作对象,既可以操作里面的函数,也可以操作里面的数据,编程思想由注重动作变为对象整体,就是由分离开的数据和函数,转为两者合成一个整体。对象具有属性(数据)并可以执行动作(函数),把过程封装在对象里,这样只需找对象即可,这样更符合自然事物的特点。
有对象就去用对象,对象没你要的功能,就造一个具有这个功能的对象。
面向对象的三个特征:封装,继承,多态。
4、字符串String是类,并不是关键字。java的引用数据类型分为3种。
所谓引用数据类型型变量,指的是这个变量的值(存的内容)不是基本数据类型的数据,而是内存中的一个地址,而这个地址又指向堆中的实体;
Student s = new Student(),s就是个引用类型的变量(具体来说就是类 类型的变量),s这个引用型变量分配在栈空间里,但是s里存储的值(地址)使得s指向的值在堆空间里的实体(具体数据)。
5、不需要独立运行的类,不需要main函数,只要有合适的描述即可。可以有多个类,但只需在一个类中有main函数运行起来,产生各个类的对象即可。
6、凡是new出来的都是放在堆里。如果你没给它初始化,那么堆中的对象会自己初始化。
对象的出现是为了封装一个对象所具有的各个类型的数据,而数组是封装同各类型的数据。7、 成员变量可以不赋值(java会自动初始化为默认值),你也可以给成员变量指定一个初始值,那么在new出的新对象中,那个成员变量值默认就是你指定的数,但是后期还可以再修改,并非一成不变。
若成员函数中的局部变量和成员变量同名,成员函数进栈,会优先在栈里找局部变量,没有的情况下再去堆里找这个名字的成员变量。
8、函数什么时候要有返回值,什么时候不要返回值?:当函数只是一种操作,如打印之类就不需要返回值,或者对对象的成员变量值进行改变,本来这个对象就是有被某个引用变量所指,那么被改变成员变量参数后依旧还是被那个引用变量所指,所以不需要返回值;但若是函数操作后产生了全新的数据或(新类型)对象,可能就要返回值了。
9、匿名对象就是没起名字的对象。因为这个对象用的次数少,懒省事,此时对象只能调用它的方法一次,因为没有引用变量保存这个对象的地址,所以这个对象就在堆内存中找不到了,也就变成了垃圾,被回收了。new Car( ).run( ) 还有点意义至少能操作函数做点什么事,但是new Car( ).num = 5 ; 就没有意义了,你刚赋完值,这个对象就成垃圾被回收了,没意义。
10、基本数据类型参数传递和引用数据类型传递的区别
11、面向对象的第一个特征:封装,隐藏对象的属性和实现的细节,即把不需要对外直接提供的内容如属性隐藏起来,仅对外提供公共的访问成员变量的方式。(也即必须通过成员函数函数间接有限制的访问和修改成员变量,而不能直接的随意无限制修改成员变量。成员函数就是锁的作用,满足条件的可以打开,修改内容,不满足就不能修改)
成员变量如果不私有话,那么在别的类中也能访问这个类的对象的成员变量,有可能赋的值不满足条件,如年龄 age= -20,这样数据就安全存在隐患,私有类的成员变量保证这个成员变量只能被自己的这个类访问,别的类函数无法访问;于是乎,不加private,相当于任何一个别的类,都可以访问这个类的成员变量,没有安全保障,而private后,别的类全部都不能访问,相当于有堵墙,也不太好;所以令这个类的成员变量私有,但是成员函数公有public,这样相当于一把锁,保障了安全,有钥匙的就可以用,也保证了方便,这样对于类中的每一个成员变量值的得到和修改,都必须通过这个类的成员函数(每个成员变量都用2个对应的成员函数,如对age来说,有setAge来修改值,有getAge来获得值),这样做的好处在于,你想修改age值就必须借助setAge()这个函数。那么在setAge()这个函数中就可以通过添加 if 语句来判断你赋的新值是否合法,这样下来数据就比较安全了。封装就是隐藏对象的属性和实现的细节,又对外提供访问方式。
也可以举个机箱和内部零件的关系,把那么多的零件都封装在机箱里,对外提供了各个接口,以供访问,CPU,内存有是怎么变化的都是机箱内部的事情,使用者不用具体了解。东西就是这样,你不隐藏就会被外界使用修改,所以不需要对外提供的,就都隐藏起来,避免被外界访问修改。
12、注意私有是一种封装,但封装不限于私有,封装范围更广,泛指对细节的隐藏。只提供给类内部的函数,也可以私有化。java中最小的封装体就是函数,函数内部怎么实现不用关心,只需要关注需要提供什么参数,返回什么结果即可。类也是一种封装(内部隐藏起来的,不需要关心内部的,都是封装),生活中,笔记本电脑也可以理解为封装
13、构造函数
构造函数在对象new出来的时候已经运行了,构造函数就是构建创遭对象时调用的函数,可以给对象进行初始化。构造函数的名字和类名是一致的,不仅没有返回值,而且连void也不写,一写void就不是构造函数,而是普通函数。
只要你写个类,类里面就自动生成了默认的构造函数: 类名() { } 。你没写构造函数时,java提供默认的,你写了构造函数,java就不再给你提供默认的了,避免有两个相同参数的构造函数冲突。
14、函数调用都要进栈的,包括构造函数。下图为构造函数在内存中的详细图解:
第一句执行,首先是new Person在堆里new出一个Person类型对象并产生了这个对象的地址,对象(对象里有什么成员变量是由类在定义时决定的)里有name 和age ,此时由于还没调用构造函数,所以初始值默认name = null ,age= 0;之后调用了构造函数(来初始化对象),构造函数进栈,同时默认构造函数里有一个this变量保存了调用这个构造函数的对象的引用地址(也即最初生成的那个地址),这样做可以保证在给对象的成员变量赋值时不会赋错对象(this这些都是默默生成进行的),然后生成构造函数的局部变量n,a ,并分别接收到了传递进来的”小强“和10;也即n = "小强",a=10,之后在栈里的构造函数Person内部找有没有name和age这两个变量,没找到,于是就去堆中调用这个构造函数的对象里面找有没有name和age这两个变量,结果找到了,所以 name = "小强" ,age= 10;完成之后,构造函数Person出栈,这时对象初始化完毕,对象初始化完后,最后才把这个对象的地址再传递给栈中的Person类型的引用变量p2。这时第一句执行结束。
第二句执行,首先p2的speak()函数进栈(压栈),执行完操作后,出栈(弹栈)。
15、对象是先 生成出来(new出来,并默认初始化,因为你后面的构造函数可能是默认的,未必给对象赋了指定的值),之后才由构造函数初始化。构造函数里也是有return(没写也是有的,隐身而已)的,用来结束函数(包括构造函数)。
16、关键字this一定是存在与函数中,关键字this特指调用当前函数的这个对象,this就是一个引用。
注意两个name是不同的,一个存在于堆中的对象里,一个存在于栈中的构造函数里,因此,想赋值就必须把两者分开,用this.name来表示堆中对象的成员变量name,这样就可以把栈中的name赋值给堆中对象的name了。。不重名时一般没写this(没写不代表不存在),因为先在栈中找想要的变量,找不到就去this指向的堆中对象里找想要的变量,由于没重名,所以没影响,但是重名时,就必须用this来区别两个重名的变量了。同时注意:当对象调用自身的成员函数时,成员函数进栈,函数里也会有个this来指着调用这个成员函数的对象,否则的话就操作错对象了(原理和构造函数那里一样)。其实只要是这个对象的成员函数被调用,进栈的成员函数都会有个this来指向对象,否则成员函数该去改变哪个对象呢?
严谨的讲构造函数和成员函数应该写成下面的样子,但是有时候因为没有重名,就懒省事,没写this。
17、一个对象可以有多个构造函数,而这些构造函数也可以相互调用,只不过要注意些细节:对象的所有成员变量及成员函数都是要通过对象才能调用,所以在Person(String name ,int age)里调用Person(String name)函数,相当于这样的写法 this.Person(String name ),但这样写也不合适,因为构造函数Person(String)是用来初始化对象的,既然对象都还没初始化完成,那怎么调用里面的函数?所以写成 this(name)比较合适。
同时还要注意下图,因为放到后面的话就会导致你指定的些语句被 后面的一句this(name) 又给更改了,而这个过程又是封装起来的,所以你看不到出错的原因,错误就莫名其妙的。
18、类本身也是一个对象。·
函数方法也存储在方法区(内存的另一块区域)。
19、静态的先存在,非静态的后存在,后存在的可以访问先存在的,但是先的不能访问后面的,因为可能还没创建。
上图中的country是静态变量属于类的,所以前面省略的其实是类名,name前面的是this也可以省略,但是性质还是不同。
上图中的静态方法只能访问静态成员,静态成员包括:静态变量和静态函数。
由于main函数为静态的,所以main函数不能调用非静态的show()函数,除非把show()函数定义为静态,但是这样的话由于num是非静态的,所以静态的show()函数有无法使用num这个非静态变量,这样又必须把变量num定义为静态static,但如果不想定义静态num,那还可以通过new一个新的类StaticDemo对象来调用非静态的show()函数。
20、main函数的注意事项public static void main(String[ ] args)参数为字符串是因为字符串的适用范围最广,任何类型数据都可以转换为字符串,当有特定需求时才会传参数,java 类名 就是java虚拟机调用这个类的主函数,这个时候后面是可以加一下字符串什么的(别的数据也会转换为字符串)
21、下图细讲代码在内存中的实现过程。
java 类名(.class)运行后,首先把代码加载到方法区,遇到一个类,加载一个类,静态的变量和函数都放在方法区的静态区,加载完类,运行到Person.method( ),此时method()方法进栈,由于没有局部变量,所以method()执行一句Sop就出栈了。(非静态的才有this,归属对象,静态的都归属于类名)
Person.method()执行完之后,执行下一句,new出一个Person类的对象,默认赋值,之后Person类的构造函数Person(“java”,20)进栈,并根据方法区的代码指示,来初始化堆中的对象 ,对象初始化完成后,构造函数出栈。
之后类似的show()函数入栈,出栈,main函数在出栈,执行完毕。
22、对象是为了封装特有数据,方法没有访问特有数据,对象就没必要创建,所以方法用静态比较好,不用创建对象。关于在静态函数里为什么不能访问非静态的成员变量,可以理解为那个静态函数进栈时不会生成this指针,也即不会指向对象,所以自然无法访问对象里才有的成员变量。
从内存角度来看:
静态方法不可以调非静态全局变量,从内存的角度看,就是当类被加载的时候静态方法和静态属性(全局静态变量)被加载到内存中,就是在内存中开辟一段内存地址用来保存静态方法和静态属性,这时候没有被实例化的类在内存中是不存在的,所以静态方法是不能访问非静态的全局变量,在类被实例化的时候,就是new 某个类的时候,在内存中给这个类开辟一些内存空间保存这个类的对象,这个时候就可以用静态方法去访问非静态的全部变量
23、静态代码块适用于初始化成员全是静态的类,这种类不需要创建对象来初始化,所以可以借助静态代码块。类里有非静态,就用正常的对象来初始化。
下面是构造代码块(和上面对比就是不要static这个词)
静态代码块和构造代码块一样随着类的加载就开始执行,在构造函数执行的前面。静态代码块只执行一次,而构造代码块,new几个新对象就执行几次而且是在构造函数执行之前就已经执行了。
(跟对象无关)静态代码块先执行,(跟对象有关)构造代码块执行,(跟对象有关)构造函数最后执行