Java笔记--随笔

Java 随笔

@purpose:随手记下基础的一些知识,这些知识都是自己以前没怎么注意的基础知识,加深印象

1. static 方法:

  • 静态方法中只能引用外部的静态属性
  • 静态方法中也只能引用静态的内部类(即被 static 修饰的类)

2. this关键字: this 关键字是不能在 static 方法或者 static 代码块中使用的

  原因static 类型的方法或者代码块是属于类本身不属于某个对象,this 本身就代表当前对象,而静态方法或者块调用的时候是不用初始化对象的

3. 异常处理:

问题:在程序猿编码时,常常要进行异常处理,不过处理异常的方法有两种(捕获 和 抛出),那么什么时候该"捕获异常"?什么时候又该"抛出"异常呢?
解答:当方法带返回值时,就捕获异常;方法不带返回值时,就抛出
  • 带返回值的方法:
public boolean save(String filename){
	/*
	* 保存文件
	*/
	try{
		File file = new File(path + filename);
		...

		// 未产生异常
		return true;
	} catch (FileNotFoundException e){
		// 产生异常
		return false;
	}
}
  • 不带返回值的方法:
public void save(String filename) throws FileNotFoundException{
	File file = new File(path + filename);
	...
}

4. 内部类访问外部类的属性需要加 final 关键字:

  • 错误代码:
public class Test {
	
	private String name;							

	private class Test1{

		public void sayHello(){
			System.out.println("Hello, My name is " + name);		// 会报错
		}

	}
}
  • 正确代码:
public class Test {
	
	private final String name;		// 加上了 final 关键字修饰	

	private class Test1{

		public void sayHello(){
			System.out.println("Hello, My name is " + name);		
		}

	}
}

5. new 关键字:

      创建对象的操作(即:new 操作)是非常耗费资源的操作,若是进行大量的 new 操作,系统效率低下。 比如 for 循 环中 new 一个对象,若是循环成千上万次亦或更多,那么系统的速度会很慢很慢,效率 也是很低。因此,若是当我们要进行 new 操作时,为了提高效率,尽可能的想办法去解决重复多次的 创建新对象。
举例:
for(int i = 0; i < 1000; i ++){
   	Message msg = new Message();
   	msg.what = UPDATE_PROGRESS;		// 更新进度
   	// ....
}
      此处的 for 循环中进行了重复的 new 操作,为了避免这种情况,减少系统资源的消耗,提供效率,我们可以使用 官方的 api 来处理。如下:
for(int i = 0; i < 1000; i ++){
   	Message msg = Message.obtain(); // 使用 api,以避免创建新对象
   	// ...
}

6. 抽象类:

  • 抽象类中不仅可以有被 abstract 修饰的抽象方法,还可以有非抽象方法。抽象方法不能实现,而非抽象方法可以实现
  • 抽象类和接口的通俗规则:[摘自《设计模式之禅》]
    • 接口:负责定义 public 属性和方法以及声明与其他对象的依赖关系
    • 抽象类:负责公共构造部分的实现,实现业务逻辑以及适当时对父类进行细化
  • 若基类是抽象类,且该类的某方法已经得以实现,则子类尽量不要重写该方法[摘自《设计模式之禅》]

7.final关键字:

  • 若 final 修饰的是基础数据类型的变量,如 final int i = 5;则该变量就是常量,其值不可变
  • 若 final 修饰的是集合类型变量,如此处的 List 集合,则该变量的引用不可变,但是可以进行增删改的操作
  • 若 final 修饰的是,则该类不可以被继承
  • 若 final 修饰的是方法,则该方法可以被继承,但是不可以被修改(private 方法被隐式的指定为 final 方法)

8.接口的种类:(2种)

  • 实例接口:Object Interface,即使用Class声明的类,然后用 new 实例化,这个类就是一个接口。如 Person p = new Person(); 产生了一个实例 p,那么该 Person 类就是 p 的接口[记住 Java 中的类也是一种接口
  • 类接口:Class Interface,即被 interface 关键字修饰的接口

9. 尽量避免在循环体中创建对象:

先给出错误编码:
for(int i = 0; i < 10000; i ++){			
	Object obj = new Object();		// 坏处:这样会在内存中产生大量的对象引用,浪费大量的内存空间,增大 GC 的负荷
	System.out.println("wrong, obj=" + obj);
}
正确的编码方式:
Object obj = null;
for(int i = 0; i < 10000; i ++){
	obj = new Object();				// 好处:无论循环多少次,依旧仅在内存中保存一份对该对象的引用
	System.out.println("right. Obj = " + obj);
}
即:正确的方法时,先在循环体外声明好需要使用的变量并置为空,然后再在循环体内初始化

10. 使用完一个对象后,将其置为 null,释放其强引用,这样可以帮助 JVM 及时的回收垃圾对象:

public class Test {

    public static void main(String[] args){
    	Object obj = new Object();
		// 使用 obj 
		// ....

		// 使用完毕
		obj = null;			// 不要忽视这句代码的作用,它可以帮助你更好的管理内存,优化系统运行速度
	}

}


11. Java引用和C、C++ 中指针的区别:

Java的引用几乎等同于C、C++ 中的指针,但是并不完全等同于。比如对于一个 int 类型的数组 arr,在C 中,指向该数组的指针 p 可以进行 ++ 操作,移动到下一个内存单元;但是Java中却不可以,绝对是通不过的。因此,可以说:Java 的引用是不可计数的指针

12. Java 数组的注意事项:

Person[] persons = new Person[4];

for(int i = 0; i < 4; i ++){
	persons[i] = new Person();
}

对于上面的这一小段代码,是否存在疑问?为什么会要有 2 次 new 操作

我们可以回想一下在 C 语言中,进行内存时的场景,就可以理解了。为什么会进行 2 次 new 操作呢?

因为第一次的 new 操作,是给数组 persons 分配了一个连续的内存空间,但是 persons[1] 等,它们却是没有值的,即其值为 null。第二次的 new 操作,才是对每一个小的内存单元进行实例化,这些内存单元才会存在实际数据。[关于数组,一定要注意这一点]

13.  在产品级研发中,方法的过时或废弃应该使用 @Deprecated 注解,而不是直接删除

不要有删除投产中代码的念头,如果方法或类确实不能在使用了,增加该注解

我们应该保持历史原貌,同时也有助于版本向下兼容,特别是在产品级研发中

/**
* 旧方法
*/
@Deprecated
public static Person getPerson(){
	return new Person();
}

/**
* 新的方法
*/
public static Person getPerson(String key){
	// ...
}

14. 子线程中是无法使用 return 语句来返回数据的

一定要注意,在子线程中是无法通过 return 语句来返回数据的。当然你会想,我可以使用全局变量、静态变量来接受啊。虽然可以,但是不建议这么做,因为在多线程中,多线程共享一个变量,很有可能产生线程安全问题。因此若一定要返回某一数据,那么建议使用Java 回调机制

15. Java Swing编程错误之关闭当前窗体时关闭了系统

再Java Swing编程中,可以使用 setDefaultCloseOperation() 方法来实现关闭当前窗体但不退出系统。但是在我使用该方法时却不依旧退出了系统。这是为什么呢?经过不断调试,才发现,我对 setDefaultCloseOperation() 的调用是写在自定义的 JFrame 中的。若是将该方法的调用写在创建自定义窗体的类中,才有效果。小结:不能写成 this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);  即不能再自定义 Frame 组件中调用该方法,只能在创建该窗体的类中进行设置,否则无效 new MyFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)

16. 异常java.lang.UnsupportedOperationException

发生场景:在使用 Arrays.asList() 后调用add,remove这些method时出现抛出 java.lang.UnsupportedOperationException异常。
问题原因:由于 Arrays.asList() 返 java.util.Arrays$ArrayList, 而不是ArrayList。 Arrays$ArrayList 这种情况下,其底层表示的是数组,因此不能去调整尺寸。也即:任何改变该数组尺寸的操作,都将报错。
解决办法: 转换为ArrayList或者使用迭代器
List list = Arrays.asList(a[]);
List arrayList = new ArrayList(list);

17. 关于继承中的private修饰

Java中的有与权限相关的有三个修饰关键字(public、private、protected)和包访问权限。其中private是仅对本类对象公开;protected是对本类及其子类对象公开,并提供包访问权限。在类间继承时,得注意 private 所修饰的属性:private 修饰的属性是可以继承的,但子类不可以直接访问父类中被 private 修饰的属性
class A {
    
    private String name;

    public String getName () {
        return name;
    }

}

class B extends A {
    
    public void test () {
        System.out.println(name);       // 这是错误的,编译器报错,没有 name 属性
    }

}
虽然不可以直接访问,但是可以使用父类提供的 getter、setter 方法来进行操作(Getter、Setter都是 public 的)
class A {
    
    private String name;

    public String getName () {
        return name;
    }

}

class B extends A {
    
    public void test () {
        System.out.println(super.getName());       // 这样才是正确的
    }

18. Java中的基本数据类型

8种基本数据类型:int、short、long、float、double、char、byte、boolean--->5个与数字相关的

各自所占的字节数:

  • char:2 byte
  • boolean:1 bit
  • short:2 byte
  • int、float:4 byte
  • long、double:8 byte
除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)

19.Java 整数默认为 int,小数默认为 double

//		float f = 3.4;			// 编译报错
		float f = 3.04f;		// 若要使用单精度需在后面加上字母f或F
		f = 3;
		
		short s = 1;
//		s = s + 1;				// 编译报错,因为 s + 1  是的结果是 int 的,需要转型为 short
		s = (short) (s + 1);
		s += 1;					// 相当于s1 = (short)(s1 + 1); 其中进行了隐含的强制类型转换

20. Java中“字符数据池”的内存管理机制

该机制会在对一个字符串赋值之前,检查池中有没有相同的数据,若有,则直接引用,否则,会 new 一个 String 对象,在堆上新开辟一个空间,存储该对象,并在栈上存储指向该对象的引用。

String s1 = "Hello,World";
String s2 = new String("Hello,World");          // 这句话创建了2个字符串对象,一个静态区的"Hello,World",一个new在堆上的对象
String s3 = "Hello" + ",World";
System.out.println(s1 == s2);			// false
System.out.println(s1 == s3);			// true
System.out.println(s1 == s2.intern());	        // true,注意 intern() 方法

21. Java 不能根据返回值类型来区分重载

public void f () {
	System.out.println("1");
}
	
public int f () {
	return 1;
}
上面那段代码是会出现编译报错的,报错内容为:Duplicate method f() in type Test。为什么会这样呢?因为有时候我们并不关心方法的返回值,想要的只是方法调用的其他效果。如我们在使用时,直接使用 f(); 而不用一个变量来接受其参数值,那么此时 Java 不知道该去调用哪一个 f() 方法,因此根据方法的返回值来区分重载方法是行不通的。

22. Java 类的初始化顺序

初始化顺序如下:类的静态成员(从父类到子类)---> 父类构造器--->子类非静态成员---->子类构造器



你可能感兴趣的:(java,笔记)