201521123091 《Java程序设计》第3周学习总结
Java 第三周总结
第三周的作业。
- Java 第三周总结
- 1.本章学习总结
- 2.Java Q&A
- 1.代码阅读
- 2.构造函数有什么用?其编写格式是什么?如果一个类不写构造函数,它有构造函数吗?如果一个类中只有带参的构造函数,它有没有不带参数的默认构造函数?
- 3.使用java.lang.Math类的函数时,为什么不需要new?如果new Math()会产生什么错误?
- 4.什么是重载?什么是构造函数重载?在java.lang.Math类中举出函数重载的例子?怎么才能区分两个重载函数?
- 5.final修饰的变量不能改变,为什么如下代码可以正常运行?
- 6.阅读代码EmployeeTest.java,回答:
- 7.编写函数public static boolean isMondayToFriday()
- 3.使用码云管理Java代码
- 4.PTA实验
1.本章学习总结
2.Java Q&A
1.代码阅读
public class Test1 {
private int i = 1;//这行不能修改
private static int j = 2;
public static void main(String[] args) {
geti();
Test1getj();
}
public static void geti() {
System.out.println(i);
}
public static void getj() {
System.out.println(j);
}
}
以上代码可否编译通过?哪里会出错?为什么?尝试改正?
如果创建3个Test1对象,有内存中有几个i,几个j?
为了解决两种情形,static关键字应运而生:
> 只为某特定域分配单一存储空间
> 希望即使没有创建对象,也能够调用某方法
接下来回答问题:不能编译通过,(首先Test1getj())出错如图所示,即静态方法不能调用一个非静态的成员变量。修改的话在这个静态方法里面创建其自身的对象。
public static void geti() {
System.out.println((new Test1()).i);
}
那么我们来想想这是为什么。首先静态方法可以不通过对象进行调用,而且我们被推荐使用类名去引用static变量(或者方法),它强调了其static结构。由于在用static方法前不需要创建任何对象,所以对于static方法来说,不能直接访问非static的成员变量或者是方法,因为他们很有可能都不存在!
如果创建3个Test1变量,内存中有3个i,1个j。因为一个static字段对于每个类来说只有一份存储空间,而非static字段对于每个对象都会有一个存储空间。
2.构造函数有什么用?其编写格式是什么?如果一个类不写构造函数,它有构造函数吗?如果一个类中只有带参的构造函数,它有没有不带参数的默认构造函数?
构造函数(Constructor)的用处:
> 提醒我们要对变量进行初始化,C语言编程告诉我么一个惨痛教训,不初始化变量而去使用它是一件相!当!糟!糕!的事情。
> 减少重复的初始化代码,并可以使代码更加易于阅读。
编写格式:
class Rock {
int i;
Rock() {
//do something
}
}
或者是
class Rock2 {
int i;
Rock2(int i) {
/* do something
* such as...
* this.i = i;
*/
}
}
通过上述两个样例,我们可以看到构造函数的一些基本格式。比如可以带参或者不带参,有了形式参数,我们就可以在初始化对象时提供实际参数,so当然可以带形式参数。然后方法名和类名相同,还有,不指定返回类型。
如果我们不写构造函数呢?那么这个类里面会不会有构造函数呢?显然是应该有的,我们刚才说了,不初始化是一个非常不安全的行为,so肯定是要有的啦。然而我们并看不见,不过它确实是有,它大概长成这个样子,这样,这样……那到底是怎样,首先它没有参数,然后这个方法不干任何事情(??),可能是的。好像还是不大懂,那我们举个栗子吧。
public class Rock {
//好蠢的代码哦……
}
再次启用我们的反汇编!来看看到底发生了什么?
public class Rock {
public Rock();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
我们可以看到编译的时候自动给我们加上了一个构造函数,然后确实什么都没有干……
这在Java当中被称为默认构造器,同时它也是无参的构造器,一般我们不写任何构造函数的时候,它就会在编译的时候被自动调用。
然后我们再来扯扯刚才的编写格式:
①有无形式参数都可以,解释过了;
②构造函数与类同名,沿用C++的做法,这样不会和类的任何成员名称起冲突。而且我们一般在写方法名的时候,首字母一般小写,然而构造函数它可管不着你这种约定俗成啦~
③不指定返回类型,连void都不可以。
……
那Rock rock = new Rock();
呢?嗯……脸好疼,这个只是new表达式去返回对新建对象的引用,而不是构造函数去返回什么。不信往下看
For purposes other than simple initialization, classes can have constructors. Constructors are blocks of statements that can be used to initialize an object before the reference to the object is returned by new. Constructors have the same name as the class they initialize. Like methods, they take zero or more arguments, but constructors are not methods and thus have no return type. Arguments, if any, are provided between the parentheses that follow the type name when the object is created with new. Constructors are invoked after the instance variables of a newly created object of the class have been assigned their default initial values and after their explicit initializers are executed.
The Java Programming Language传送门到参考网站
我们可以从这段英文中看到,constructor不被当做一个常见的方法,我们可以认为它是特殊的方法。而且constructor在调用之前,对象就已经存在了,何谈是它来返回引用呢?
对于这个最后一小问,那就敲个代码吧,就拿上面的Rock2类来看看吧!
public class Rock2 {
int i;
Rock2(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field i:I
9: return
}
从反汇编结果来看,编译的时候就不会再自动建立默认构造函数了。
而且你看,的确没有无参的构造函数给你用了。真的想用,看到第三个fix了吗?Create constructor 'Rock2()',自己去搞一个啊!
3.使用java.lang.Math
类的函数时,为什么不需要new?如果new Math()会产生什么错误?
为什么不需要要new,因为Math类里面的所有成员变量和成员方法都是静态的,我们前面有说到过,这种用static修饰的都可以直接用类名去调用而且不需要任何对象在提前创建。
而且还不是不需要的问题,问题是你如果想new一个,会报错啊!报了个啥错?The constructor Math() is not visible
,也就是说它根本就不让你new一个,这就很尴尬了,所以让我们来看看Math类的源码。
/**
* Don't let anyone instantiate this class.
*/
private Math() {}
说的很明确了,根本就不想让你去实例化,死心吧。
如何正确的去看待Math类的这个做法,即只是把类当做一个名字空间而已,满足我们上面所说的,方法的调用和对象是否创建不需要建立联系。
4.什么是重载?什么是构造函数重载?在java.lang.Math类中举出函数重载的例子?怎么才能区分两个重载函数?
重载在英文维基中是个多义词,其中和计算机科学相关的就有:
我们这边就来谈谈方法的重载!
方法重载在英文wiki中的定义:
In some programming languages, function overloading or method overloading is the ability to create multiple methods of the same name with different implementations.Function overloading
就是创建同名的但是有不同实现的多个方法就是重载!
我们日常生活中也可以随处看到重载的影子,比如学习!学习语文、学习数学、学习英语、学习dota……
但是像下面这样说,别人会觉得很蠢
“以学习语文的方法学习语文”;
“以学习数学的方法学习数学”
……
很显然,我即使做一点省略,意思还是能够很好的表达出来。相反我要是完整地进行表述,就会让别人觉得很冗余。
构造函数重载,就是允许方法名相同而形式参数不同的构造器同时存在。因为在不同的情境下,我可能需要用不同的方式去初始化一个类,但是类名只有一个,而构造函数的名字只能是类名,所以构造函数也需要函数的重载!
Math类中函数重载的例子下面就有,写了四个abs,针对四个类型,double、float、int、 long:
怎样才能区分两个重载函数,这两个函数都具有相同的名字,Java如何知道我到底指的是哪一个嘞?
答案是参数列表!
不仅是参数类型的差异,而且参数顺序的不同都可以区分出不同的方法。
那么返回值可不可以,比如:
void f() {}
int f() {return 1;}
那这样怎么区分呢,或许可以搞一个int x = f();
这样。那如果有赋值那就是第二个,没有赋值的就是第一个。可是我们经常会这么编程:即使是一个返回值不为void,我们可能也不会把这个方法的返回值赋给任何变量,就像f();
这样做没有任何毛病,如果它本身只是一个返回值类型为int的函数,那最多编译器就是给个warning而已。那么如果我们写了这么两个重载函数,那Java到底该如何确定用哪一个函数呢?(能怎么办呢,我也很绝望啊!)所以根据返回值类型来区分重载方法显然是不行的。
5.final修饰的变量不能改变,为什么如下代码可以正常运行?
final int[] NUMBS= {1,2,3,4,5};
NUMBS[2] = 1;
这边贴出一段话来回答
当对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑。对于基本类型final使数值恒定不变;而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果)。这一限制同样适用数组,它也是对象。
——《Java编程思想》
这边举个栗子来类比一下
——“无论她将来是富有还是贫穷,或无论她将来身体健康或不适,你都愿意和她永远在一起吗?”
——“是的,我愿意。”
大概就是这么个感觉。
6.阅读代码EmployeeTest.java,回答:
1. 为什么其属性均为private?这样设计有什么好处?
2. 为Employee类增加一个无参构造函数,调用其有参构造函数,设置name为雇员1, salary为3000, hireDay的年月日为2017年3月5日。(粘贴代码)
3. 为Employee类再增加一个有参构造函数,参数只有name与salary,hideDay为当前日期。(粘贴代码)
- 属性设置为private,是不希望用户能够随意操作类的成员变量,或者希望用户可以通过自己所指定的方法来操作类成员变量。尤其是对这个employee类来说,要是随意加工资就算了,要是工资突然就被扣了,那不跟你急?
- 增加无参构造函数
直接调用现成的带参数列表的构造函数就好了
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public Employee() {
this("雇员1", 3000.0, 2017, 3, 5);
}
再增加一个有参构造函数
//唯一的问题就是构造函数必须得放在第一行,所以这边代码就只能丑陋一点了
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public Employee(String n, double s) {
this(n, s, Calendar.getInstance().get(Calendar.YEAR),
Calendar.getInstance().get(Calendar.MONTH) + 1,
Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
}
7.编写函数public static boolean isMondayToFriday()
功能:判断今天如果是周一到周五,直接输出如“上班时间”并返回true,否则输出“休息时间”。
提示:使用LocalDateTime, DayOfWeek
/*按着提示往下做就是了,没什么好说的,
*就是这个getDayOfWeek()的方法不是int
*不过只要在用这个getValue()方法就好了
*所以没什么差
*/
public static boolean isMondayToFriday() {
LocalDateTime localDateTime = LocalDateTime.now();
int day = localDateTime.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
3.使用码云管理Java代码
4.PTA实验
- 第一题:
- this关键字是为了在方法的内部获得对当前对象的引用。表示对调用方法的那个对象的引用。如果在方法内部调用同一个类中的另一个方法,则不需要使用this。
- toString()方法是用字符串来表示对象的一种方法,这个方法就是会返回一个用文本方式表达对象的字符串。推荐在所有的子类里面重载toString()方法。如果我们什么都不写,那么默认就会返回这么一个字符串
getClass().getName() + '@' + Integer.toHexString(hashCode())
- setter/getter,设置器和访问器。我们之前说到了,通常将成员变量声明为private,是为了防止直接访问成员变量而引起的恶意操作。但是,这并不是不允许访问,我们可以通过setter和getter方法来完成。Eclipse可以自动生成getter和setter方法,在左上角Source菜单栏里。
- 第二题
- 这道题希望每一个雇员都有个不同的ID,那么我们可以设置在Person类里面设置一个静态变量,然后每招来一个雇员,我们都把当前这个静态变量的值赋给这个雇员,然后静态变量再自增1。因为这个静态变量是这个类所持有的,而不属于任何一个雇员,作用其实就相当于全局变量。
- 如果有些事情要在.class加载后执行,那么就可以定义static块,这样只要.class加载进JVM中,默认就会执行该static块。
第三题,这边就简要地讲两点
toString()和deepToString()的区别
Returns a string representation of the contents of the specified array.If the array contains other arrays as elements, they are converted to strings by the {@link Object#toString} method inherited from Object, which describes their identities rather than their contents.
toString()的实现
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
测试代码:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int [][] a = {{1, 2}, {2, 3}};
int [] b = {1, 2};
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));
}
}
然后运行结果是这样的:
[[I@15db9742, [I@6d06d69c]
[1, 2]
对于一维数组打印出了我们所希望得到的东西,而对于一个多维数组(这边是二维数组),我们也如注释中所说的打印了从相应的类里所继承的toString()方法,那么我们之前也说了,默认就是getClass().getName() + '@' + Integer.toHexString(hashCode())
这样的格式,那这边也是。
为了解决这样的问题,另一个方法应运而生,它叫做deepToString()方法,方法如其名,它可以一直深入这个(多维)数组的内部,即便其维度会很高。
Returns a string representation of the "deep contents" of the specified array. If the array contains other arrays as elements, the string representation contains their contents and so on. This method is designed for converting multidimensional arrays to strings.
deepToString()的实现
//这边就上一个简单的,这个方法调用的那个同名方法其实挺复杂的
if (a == null)
return "null";
int bufLen = 20 * a.length;
if (a.length != 0 && bufLen <= 0)
bufLen = Integer.MAX_VALUE;
StringBuilder buf = new StringBuilder(bufLen);
deepToString(a, buf, new HashSet<Object[]>());
return buf.toString();
测试代码:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int [][] a = {{1, 2}, {2, 3}};
System.out.println(Arrays.toString(a));
System.out.println(Arrays.deepToString(a));
}
}
然后运行结果是这样的:
[[I@15db9742, [I@6d06d69c]
[[1, 2], [2, 3]]
当然就不一样咯,deepToString()方法专门针对多维数组的。
要不我试试直接贴html的代码?
import java.time.LocalDate;
public class Main {
public static boolean isMondayToFriday(String string) {
String[] strings = string.split("-");
int year = Integer.valueOf(strings[0]);
int month = Integer.valueOf(strings[1]);
int dayOfMonth = Integer.valueOf(strings[2]);
LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
int day = localDate.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
public static void main(String[] args) {
if (args.length > 0) {
String string = args[0];
if (isMondayToFriday(string)) {
System.out.println("上班时间");
} else {
System.out.println("休息时间");
}
}
}
}
import java.time.LocalDate;
public class Main {
public static boolean isMondayToFriday(String string) {
String[] strings = string.split("-");
int year = Integer.valueOf(strings[0]);
int month = Integer.valueOf(strings[1]);
int dayOfMonth = Integer.valueOf(strings[2]);
LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
int day = localDate.getDayOfWeek().getValue();
if (day >= 1 && day <= 5) {
return true;
}
return false;
}
public static void main(String[] args) {
if (args.length > 0) {
String string = args[0];
if (isMondayToFriday(string)) {
System.out.println("上班时间");
} else {
System.out.println("休息时间");
}
}
}
}
运行结果如截图所示:
![](http://images2015.cnblogs.com/blog/1109923/201703/1109923-20170311130631029-1098264225.png)