五、匿名对象
匿名对象:没有名的实例对象
【1】创建一次性匿名对象
class person{ public void eat(){ System.out.println("吃饭啦"); } } public class Main{ public static void main(String[] args){ new Person().eat();//匿名对象 } }
说明:为什么要叫做匿名对象呢?对象在堆中创建好之后,栈中没有变量指向这个内存空间地址,自然而然下次就无法找到并操作这个对象了。
【2】方法传参匿名参数:
public class Main { public static void main(String[] args) throws UnknownHostException { //传递匿名参数 Main.usePerson(new Person()); } public static void usePerson(Person person){ person.eat(); } } class Person{ public void eat(){ System.out.println("吃饭啦"); } }
实际上就是将这个内存地址传递给了方法参数中的person来接收,但仅局限在方法中进行使用该对象。一般是我只想调用一次方法并不想额外创建变量名称,就可以使用这种方式传参。
六、值传递与地址传递
概念介绍
方法中的参数叫做形式参数,调用方法进行传参的叫做实际参数。
形式参数:又叫形参,方法定义时,声明的小括号内的参数。
实际参数:又叫实参,实际传递给形参的数据。
方法中传参包含值传递与地址传递
值传递:方法中传递参数为基本数据类型,进行值拷贝
地址传递:方法中传递参数为引用数据类型
实际案例说明
值传递案例
传递内容:基本数据类型int
public class Main { public static void main(String[] args) throws UnknownHostException { int i = 10; int j = 20; System.out.println("值传递前:i="+i+" "+"j="+j);//地址传递前:i=10 j=20 Main.swap(i,j); System.out.println("值传递后:i="+i+" "+"j="+j);//地址传递后:i=10 j=20 } //交换i与j值 public static void swap(int i,int j){ int sum = i+j; i = sum - i; j = sum - i; } }
结果:可以看到方法中进行值传递调用前后的值都没有进行交换。
详细说明:
基本数据类型存放的值称为自动变量,自动变量存储的值是字面值,与在堆中创建类的实例与数组开辟内存空间不同,每当栈中定义了一个自动变量时,会直接赋一个字面值,下一次若定义一个自动变量时会首先去栈中找是否有相同的字面值,若是有直接指向这个字面值。与引用类型引用地址有所不同。
回到上面代码中,这里是值传递,仅仅是传递字面值(非地址引用),那么在其方法中对i与j操作就与外面操作i与j就无任何关系。所以前后值肯定没有变。
地址传递案例
传递内容:引用数据类型,数组
public class Main { public static void main(String[] args){ int[] arr = {1,2}; System.out.println("地址传递前:arr[0]="+arr[0]+" "+"arr[1]="+arr[1]);//地址传递前:arr[0]=1 arr[1]=2 Main.swap(arr,0,1); System.out.println("地址传递后:arr[0]="+arr[0]+" "+"arr[1]="+arr[1]);//地址传递后:arr[0]=2 arr[1]=1 } //交换数组arr中下标为i与j的值 public static void swap(int[] arr,int i,int j){ int x = arr[i]; arr[i] = arr[j]; arr[j] = x; } }
结果:使用地址传递,数组中指定i与j下标位置值发生了调换。
详细说明:
方法中传递的是引用数据类型,传到形参中的arr中的实际上就是实参arr的引用地址,那么顺理成章形参就能够进行操控这个数组进行指定位置的交换操作,回到main方法中,因为引用的都是相同地址,确确实实进行了交换,那么就如结果一样了。
地址传递(特殊),包装类
传递内容:包装类,为啥说特殊呢,因为传递过去进行交换值,最终结果并不是我所看到的
public static void main(String[] args){ Integer i = 11; Integer j = 12; int a = 10; System.out.println("地址传递前:i="+i+" "+"j="+j);//地址传递前:i=11 j=12 Main.swap(i,j); System.out.println("地址传递后:i="+i+" "+"j="+j);//地址传递后:i=11 j=12 } public static void swap(Integer i,Integer j){ Integer sum = i+j; i = sum-i; j = sum-i; }
结果:Integer明明是引用数据类型,最终输出展示却并没有交换。
详细说明:
针对于包装类,首先得知道装箱拆箱,下面直接上例子:
Integer i = 11:将一个字面值直接赋值给Integer包装类,会有装箱操作,其中包含了隐含的操作就是Integer i = Integer.valueOf(11);
int j = i:直接使用上面包装类i给基本类型赋值,会有拆箱的操作,包含隐含的操作就是i.intValue();
接着我们进行分析方法中的操作,首先第10行,我们将引用数据类型传递给形参i与形参j,当前状态下形参i、j与实参i、j都引用相同的内存地址,接下来注意看下面旁边的注释:
public static void swap(Integer i,Integer j){ Integer sum = i+j; i = sum-i;//隐藏装箱操作:i = Integer.valueOf(sum-i); j = sum-i;//隐藏装箱操作:i = Integer.valueOf(sum-i); }
这里我们能看到此时形参i与j又进行了不同的内存地址引用,简单说就是它换了个内存地址,并不是说它将以前是实参的引用内存地址中的value值进行更改了。(况且Integer源码中value值为final,常量无法修改)。
Debug:
说了一堆,我们再进行debug就更加明了,首先我们看下在main方法中包装类i与j的引用地址:
此时是刚进入到swap方法中,看一下形参i与j的引用地址,发现引用地址与实参的相同,确实是地址传参
我们执行到第28行,可以看到形参i与j仅仅是引用内存地址发生了改变,此时更加明朗了。
此时我又抛出一个疑问,这个内存地址咋这么眼熟啊,咋Integer包装类相同数值对应的引用内存地址一样呢???
难道是字面值相同,引向同一个地址嘛,不对不对,字面值又没有在堆中开辟空间,又哪里来的内存地址呢,有了这些个疑问,我们点开源码一探究竟,点开Integer的valueOf()方法:
IntegerCache:Integer包装类的静态内部类
Integer.cache:static final Integer cache[];内部类的一个静态常量一维数组,数组会提前存放好对象。下标0开始依次放置-128的包装类到127。
low:static final int low = -128;静态整型常量值为-128。
这里我简单描述一下即可,当调用valueOf()方法时,会先判断传入的int数字是否在**-128-127**之间,如果是的话直接从cache数组中拿即可,若超过范围,就new一个对象出去。
此时我们解开了为啥内存引用地址与之前相同的疑惑,本来只是想记录一下地址传递的知识点,偶然使用了Integer包装类,偶然发现了自动装箱过程同样是new对象,又偶然发现Integer的缓存数组,让我体会到每个知识点都能够深挖,就看你有没有这个耐心与不断钻研的兴趣。
MVC设计模式
经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现分离。
不同层做的事以及定义的包名:
整个流程:
框架内容介绍:
强制性的使应用程序的输入、处理与输出分开,将MVC应用程序分为三个核心部件:模型、视图、控制器。
最经典的MVC就是JSP+servlet+javaBean,也就是初始阶段学习的javaweb。
JSP相当于view视图(前端视图显示)
servlet相当于controller控制器(获取请求,处理业务)
javabean相当于model(会向数据库查询封装数据到javabean中)
框架包含模式:工厂模式、适配器模式、策略模式等。
常见框架如:ssm(spring,springmvc,mybatis)、ssh(spring,struts,hibernate)、springboot(简化spring与springmvc,提供各种应用场景及启动器)。
简而言之:框架是大智慧,用来对软件设计进行分工;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
优缺点:
优点:耦合度低、重用性高、部署快、生命周期成本低、可维护性高
缺点:理解MVC复杂、调试困难、不适合小型,中等规模的应用程序、系统结构与实现的复杂度高
参考资料
[1]. Java虚拟机(JVM)的方法区(Method Area)存储了什么内容? 详细信息可查阅博客
[2]. Java基本数据类型与引用数据类型内存分配 对基本数据类型与引用数据类型的产生与内存分配有很好的总结,建议一看
[3]. Integer的自动装箱过程
[4]. 包装类,通过自动装箱后部分对象地址值相同的问题
[5]. 百度百科—MVC设计模式