一、OOP(Object-oriented Programming)面向对象程序编程 初谈类和对象,所谓万物皆对象,类和对象有什么区别和联系? 类,是对某一种类型的定义,比如字符串,动物,人,飞机等等,而对象是指具体的字符串,动物,人... 如:猪是类,定义了,猪,有体重,有年龄,可以吃饭,可以睡觉,而对象是这只猪,那只猪,或者某只猪起个名字,叫小白,那么这种具体的猪,就是对象,而小白这个名字,则称为对象的引用,因为通过小白,我们可以找到指定的那头猪。 package com.yq;public class Pig { private int height; private int age; void eat() { System.out.println('eat()'); } void sleep() { System.out.println('sleep()'); } public String toString() { return getClass().getName() + '===@===' + Integer.toHexString(hashCode()); }// public String toString() {// return getClass().getName() + '@' + Integer.toHexString(hashCode());// } public static void main(String[] args) { Pig xiaoBai = new Pig(); Pig xiaoHei = new Pig(); xiaoBai.eat(); xiaoHei.sleep(); System.out.println(xiaoBai); System.out.println(xiaoHei); System.out.println('xiaoBai==xiaoHei?' + (xiaoBai == xiaoHei)); System.out.println(xiaoBai.getClass()); System.out.println(xiaoHei.getClass()); System.out.println('xiaoBai.getClass()==xiaoHei.getClass()?' + (xiaoBai.getClass() == xiaoHei.getClass())); } } eat() sleep() com.yq.Pig===@===74a14482 com.yq.Pig===@===1540e19d xiaoBai==xiaoHei?falseclass com.yq.Pigclass com.yq.PigxiaoBai.getClass()==xiaoHei.getClass()?true 关于 '==' 对于引用变量而言,比较的时候两个引用变量引用的是不是同一个对象,即比较的是两个引用中存储的对象地址是不是一样的。 对于基本数据类型而言,比较的就是两个数据是不是相等。 对于String类型,它是特殊而有趣的。 package com.yq;public class Test { public static void main(String[] args) { //String作为一个对象来使用 System.out.println('String作为一个对象来使用'); String str = new String('hello'); String str2 = 'he' + new String('llo'); System.out.println('str==str2?' + (str == str2)); System.out.println(str + '@' + str.hashCode()); System.out.println(str2 + '@' + str2.hashCode()); //String作为一个基本类型来使用 //如果String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。 //如果String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。 System.out.println('String作为一个基本类型来使用'); String s1 = 'java'; String s2 = 'java'; System.out.println('s1==s2?' + (s1 == s2)); System.out.println(s1 + '@' + s1.hashCode()); System.out.println(s2 + '@' + s2.hashCode()); System.out.println('String混合使用'); String st1 = 'hello java'; String st2 = new String('hello java'); System.out.println('st1==s2?' + (st1 == st2)); System.out.println(st1 + '@' + st1.hashCode()); System.out.println(st2 + '@' + st2.hashCode()); } } String作为一个对象来使用 str==str2?falsehello@99162322hello@99162322String作为一个基本类型来使用 s1==s2?truejava@3254818java@3254818String混合使用 st1==s2?falsehello java@-1605094224hello java@-1605094224 输出结果中,明明hashCode相等,为何对象不相同? public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } 上述String源码中,可以看出,String类是使用它的 value值作为参数然后进行运算得出hashcode的, 换句话说, 只要值相同的String不管是不是一个对象,hash值全部相等。 在Object超类中,hashCode()是一个native本地方法,看不到实现。 对象存放在内存的堆中,而基本类型则存放在内存的栈中。 二、操作符 移位操作符<<,>>,>>> 移位操作符操作的对象是二进制的'位',只能用来处理整数类型,如果对char、byte、或者short类型的数值进行移位处理,那么他们在移位之前会自动转换为int类型,结果也是int,对long处理,结果也是long。 << : 左移运算符,低位补0,num << 1,相当于num乘以2 >> : 右移运算符,若符号为正,高位补0,若符号为负,高位补1,num >> 1,相当于num除以2 >>> : 无符号右移,忽略符号位,空位都以0补齐。 三元操作符 exp?value1:value2 若exp表达式为true,返回value1的值,否则返回value2的值,三元表达式可以嵌套。 '+'操作符,即字符串操作符,用来连接字符串。 '()type'类型转换操作符,用来强制类型转换,一般自动向上转型,不需要强转,向下转型才需要强转。 ','逗号操作符,java里面唯一用到逗号操作符的地方就是for循环的控制表达式,在表达式的初始化和步进控制部分,可以使用逗号分隔的语句,但是初始化时,使用逗号表达式,必须具有想同的类型。 三、流程控制 if-else、while、do-while、for、foreach、switch。 if语句不加{},默认范围为下一条语句。 else if不是关键字,只是else后面加了一个if语句。 do-while相比while只是会先执行一次,再判断条件。 for(;;)效果和while(true)一样。 foreach语法用于数组和容器。 package com.yq; import java.util.ArrayList; import java.util.List;public class Test { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5}; for (int i : arr) System.out.print(i + ' '); System.out.println(); List ls.add('a'); ls.add('b'); ls.add('c'); ls.add('d'); ls.add('e'); for (String str : ls) System.out.print(str + ' '); } }//out1 2 3 4 5 a b c d e 关键字return、break、continue。 return结束整个方法,开始下一个方法。 break结束当前层的循环,开始当前层的外层的下一次循环。 continue结束当前层的当次循环,开始当前层的下一次循环。 switch的选择因子,是一个能够产生整数值的表达式,char本身就是整数。 四、类初始化 this关键字 1、表示当前类对象的引用 2、表示对调用当前方法的那个对象的引用 3、构造器调用类中的其他构造器,但是不能调用两次,并且必须放在构造器的第一行。 static关键字 static静态方法,可以通过类本身直接调用,当然类对象也可以调用static方法,但是static方法里面不能调用非静态方法,也不能出现this。 对于static修饰的类属性来说,内存只为该属性第一次初始化时分配一个地址,不管该类new了多少个对象,该属性都是同一个引用。 虽然地址是唯一的,即引用是唯一的,但是引用的值还是可以改变的。 final关键字 final作用于方式,则该方法不能被继承时覆盖,作用于参数,如果是引用类型,则该引用不能改变,但是引用的对象可以改变,如果是基本类型,则只能被赋值一次,并且不能改变。 对于一个类说来,该类的成员属性,可以不初始化,因为系统会为各成员属性设置默认值,String、对象等默认为null,数值类型为0,对应folat或者double,也有相应的小数点,char也为0,显示为空白,boolean为false,String、对象则为null。 只有类成员属性会被默认初始化,方法中必须对定义的属性或变量初始化。 初始化顺序 构造器不指定时,系统将会设置一个默认无参构造器,若指定了一个构造器,无论是否有参数,系统都不会自动设置默认构造器。 下面的例子,详细分析了,各种情况的初始化顺序: package com.yq;class Animal { static int level; int type; static { System.out.println('Animal static'); level = 1; System.out.println('Animal level:' + level); } { System.out.println('Animal'); type = 10; } Animal(int i) { System.out.println('Animal constructor'); } }class Pig extends Animal { static int level; int height; Eye eye = new Eye(2); static { System.out.println('Pig static'); level = 2; System.out.println('Pig level:' + level); } { System.out.println('Pig'); height = 20; super.level = 3; } Pig(int i) { super(i); System.out.println('Pig constructor'); } }class Eye { Eye(int i) { System.out.println(i + '只眼睛'); } }public class Test { public static void main(String[] args) { //测试1 Pig pg1 = new Pig(2); System.out.println(Animal.level); //测试2 Pig pg2 = new Pig(2); Pig pg3 = new Pig(2); System.out.println(Animal.level); //测试3 System.out.println(Pig.level); } } //测试1输出Animal staticAnimal level:1Pig staticPig level:2Animal Animal constructor2只眼睛PigPig constructor3 //测试2输出Animal staticAnimal level:1Pig staticPig level:2Animal Animal constructor2只眼睛PigPig constructorAnimalAnimal constructor2只眼睛PigPig constructor3 //测试3输出Animal staticAnimal level:1Pig staticPig level:22 由测试1可知,初始化顺序为:父类静态成员、父类静态代码块、子类静态成员,子类静态代码块、父类成员,父类代码块,父类构造器、子类成员、子类代码块、子类构造器。 由测试2可知,静态域只初始化一次。 由测试3可知,只调用静态域时,只初始化静态域,和对象无关,静态域只由类决定。 若父类没有无参构造器,则必须用super显式调用父类构造器,不然会报错。 可变参数列表 package com.yq;public class Pig { static void test(String... args) { for (String str : args) System.out.print(str + ' '); System.out.println(); } public static void main(String[] args) { test('小明', '小红', '小军', '小华'); } }//输出小明 小红 小军 小华 其实可变参数列表,是编译器自动填充了数组。 五、访问控制权限 public、protected、包访问权限(没有关键字,默认权限)、private 对于类来说,如果一个java文件,存在public类,则java文件名必须和类型一致,而且这个文件只能存在一个public类,若整个文件都不存在public类,则java文件名和类名没有关系。 对于成员属性和方法来说, public是最大权限,任何类都可以访问。 protected,继承访问权限,在该类的继承类中可以访问,同一个包下可以访问。 包访问权限,当没有指明权限时,默认是包访问权限,只有在同一个包下的类才能访问。 private是最小的权限修饰符,只能在本类中访问。 六、复用类。 组合:在新的类中放置任意对象,一般是private修饰该对象 继承:在新的类中隐式的放置父级对象。 组合技术通常用于想在新类中使用现有类的功能,而非他的接口。 继承技术通常用于保持现有类的形式,并可以添加新代码。 一般来说判断是否使用继承,问一问自己是否需要从新类像父类进行向上转型,如果需要则继承是必须的,否则考虑组合技术应该更合适。 七、多态 因为多态的存在,程序有了可扩展性。 package com.yq;class Animal { public void eat(String args) { System.out.println('Animal eat():' + args); } }class Pig extends Animal { public void eat(String args) { System.out.println('Pig eat():' + args); } }class Cat extends Animal { public void eat(String args) { System.out.println('Cat eat():' + args); } } public class Test { public static void action(Animal anl) { anl.eat('苹果'); } public static void main(String[] args) { Pig pg = new Pig(); Cat ct = new Cat(); action(pg); action(ct); } }//输出Pig eat():苹果Cat eat():苹果 如果程序中,需要新加动物种类,则只需定义这个类,继承Animal类即可,主程序不用做任何修改。 那么为什么编译器能这么智能的找到对应的子类对象,并且正确的调用方法呢? 原因就是:方法后期绑定,也叫作动态绑定。 意思是,在运行时根据对象的类型进行绑定,而不是在定义方法就进行前期绑定。 java中除了static和final方法(private方法也属于final方法,因为final是隐式申明的)之外,其他所有的方法都是后期绑定。 学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:454297367【长按复制】 我们一起学Java! |
|