锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
2:涓涓叿浣撳疄鐜?br />3:涓涓櫄鎷熸満瀹炰緥
java铏氭嫙鏈虹殑鐢熷懡鍛ㄦ湡
java铏氭嫙鏈虹殑澶╄亴灝辨槸:榪愯涓涓猨ava紼嬪簭.褰撲竴涓猨ava紼嬪簭榪愯寮濮嬭繍琛屾椂,涓涓櫄鎷熸満瀹炰緥灝變駭鐢熶簡(jiǎn).褰撲竴涓綆楁満涓婂悓鏃惰繍琛屼笁涓猨ava紼嬪簭.鍒欏皢浜х敓涓変釜java铏氭嫙鏈哄疄渚?姣忎釜紼嬪簭榪愯鍦ㄨ嚜宸辯殑铏氭嫙鏈洪噷闈?涓嶄細(xì)騫叉壈.褰撶▼搴忚繍琛屽畬姣曟椂,铏氭嫙鏈哄皢鑷姩閫鍑?
java铏氭嫙鏈洪噷闈㈡湁涓ょ綰跨▼,瀹堟姢綰跨▼鍜岄潪瀹堟姢綰跨▼.瀹堟姢綰跨▼鏄java铏氭嫙鏈鴻嚜宸辯殑綰跨▼,濡傚瀮鍦炬敹闆嗙嚎紼?鑰岄潪瀹堟姢綰跨▼鍒欐槸java涓繍琛岀殑紼嬪簭綰跨▼.褰撻潪瀹堟姢綰跨▼閮借繍琛屽畬浜?java铏氭嫙鏈哄皢閫鍑?
涓涓猨ava铏氭嫙鏈轟富瑕佸寘鎷簡(jiǎn):綾昏漿杞藉瓙緋葷粺,榪愯鏃舵暟鎹尯,鎵ц寮曟搸,鍐呭瓨鍖虹瓑絳?
榪愯鏃舵暟鎹尯------涓昏鏄?1 鍫?2聽(tīng) 鏂規(guī)硶鍖?3 java鏍?br />
鍫嗗拰鏂規(guī)硶鍖哄铏氭嫙鏈哄疄渚嬩腑鎵鏈夌殑瀵硅薄閮芥槸鍏變韓鐨?鑰宩ava鏍堝尯,鏄姣忎釜綰跨▼閮芥槸鐙珛鐨? 褰撲竴涓猚lass琚澆鍏ュ埌 classloader涓椂,浼?xì)瑙f瀽瀹冪殑绫诲瀷淇℃?鎶婅繖浜涚被鍨嬩俊鎭斁鍒版柟娉曞尯,鑰屾妸紼嬪簭涓繍琛岀殑瀵硅薄,鏀懼埌鍫嗗尯.褰撲竴涓柊綰跨▼琚垱寤?灝卞垎閰嶄竴涓柊鐨刯ava鏍?java鏍堜腑淇濆瓨鐨?鏄柟娉曚腑鐨勪竴浜涘彉閲?鐘舵?java鏍堟槸鐢卞緢澶氱殑java鏍堝撫緇勬垚鐨?涓涓爤甯у寘鍚簡(jiǎn)涓涓柟娉曡繍琛岀殑鐘舵?褰撲竴涓柟娉曡鎵ц鐨勬椂鍊?灝卞帇鍏ヤ竴涓柊鐨刯ava鏍堝撫鍒癹ava鏍堜腑,鏂規(guī)硶榪斿洖鐨勬椂鍊?灝辨妸鏍堝撫寮瑰嚭鏉?鎶涘純鎺?
鏂規(guī)硶鍖?br />
鍦╦ava铏氭嫙鏈轟腑,琚杞界殑綾葷殑綾誨瀷淇℃伅鍜岀被鐨勯潤(rùn)鎬佸彉閲忚瀛樺偍鍦ㄦ柟娉曞尯榪欐牱鐨勫唴瀛橀噷闈?java紼嬪簭榪愯鏃?浼?xì)鏌ユ壘杩欎簺涓俊鎭?鏂規(guī)硶鍖虹殑澶у皬,鏄姩鎬佺殑.涔熷彲浠ヤ笉鏄繛緇殑.鍙嚜鐢卞湪鍫嗕腑鍒嗛厤.涔熷彲浠ョ敱鐢ㄦ埛鎴栬呯▼搴忓憳鎸囧畾.鏂規(guī)硶鍖哄彲琚瀮鍦炬敹闆?
鏂規(guī)硶鍖哄彲浠ヤ繚瀛樹(shù)互涓嬩俊鎭?br />榪欎釜綾誨瀷鐨勫叏闄愬畾鍚?br />鐩存帴瓚呯被鐨勫叏闄愬畾鍚?br />鏄被綾誨瀷榪樻槸鎺ュ彛
綾誨瀷鐨勮闂慨楗扮
浠諱綍鐩存帴瓚呯被鎺ュ彛鐨勫叏闄愬畾鍚嶇殑鏈夊簭鍒楄〃.
璇ョ被鍨嬬殑甯擱噺姹?br />瀛楁淇℃伅 綾諱腑澹版槑鐨勬瘡涓瓧孌靛強(qiáng)鍏墮『搴?濡傚瓧孌靛悕,綾誨瀷.淇グ絎﹀彿.
鏂規(guī)硶淇℃伅:濡傛柟娉曞悕,榪斿洖綾誨瀷.鍙傛暟琛ㄥ垪.淇グ絎﹀彿.瀛楄妭鐮?鎿嶄綔鏁版爤鍜屾爤甯т腑灞閮ㄥ彉閲忓尯澶у皬絳夌瓑
綾婚潤(rùn)鎬佸彉閲?br />涓涓埌綾籧lassloader鐨勫紩鐢?br />涓涓埌class綾葷殑寮曠敤
鍫?br />鐢ㄦ潵瀛樺偍榪愯鏃剁殑瀵硅薄瀹炰緥
java鏍?br />姣忓惎鍔ㄤ竴涓柊鐨勭嚎紼?灝變細(xì)鍒嗛厤鍒頒竴涓猨ava鏍?java鏍堜互甯т負(fù)鍗曚綅淇濆瓨綰跨▼鐨勮繍琛岀姸鎬?瀹冩湁涓ょ鎿嶄綔.鍏ユ爤,鍑烘爤.
褰撲竴涓柟娉曡璋冪敤鏃?鍏ユ爤,褰撲竴涓柟娉曡繑鍥炴椂,鍑烘爤,鎴栬呭綋鏂規(guī)硶鍑虹幇寮傚父.涔熷嚭鏍?
鏍堝撫
緇勬垚閮ㄥ垎 灞閮ㄥ彉閲忓尯,鎿嶄綔鏁版爤,甯ф暟鎹尯.
]]>
java涓篃瑕佹敞鎰忓唴瀛樼鐞?br />
// Can you spot the "memory leak"?
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity) {
this.elements = new Object[initialCapacity];
}
Effective Java: Programming Language Guide
17
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size) {
Object[] oldElements = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
}
榪欓噷鏈変竴涓唴瀛樻硠婕?br />濡傛灉涓涓爤鍏堟槸澧為暱錛岀劧鍚庡啀鏀剁緝錛岄偅涔堬紝浠庢爤涓脊鍑烘潵鐨勫璞″皢涓嶄細(xì)琚綋鍋氬瀮鍦懼洖鏀訛紝鍗充嬌浣跨敤鏍堢殑瀹㈡埛紼嬪簭涓嶅啀寮曠敤榪欎簺瀵硅薄錛屽畠浠篃涓嶄細(xì)琚洖鏀躲傝繖鏄洜涓猴紝鏍堝唴閮ㄧ淮鎶ょ潃瀵硅繖浜涘璞$殑榪囨湡寮曠敤錛坥bsolete re f e re n c e錛夈傛墍璋撹繃鏈熷紩鐢紝鏄寚姘歌繙涔熶笉浼?xì)鍐嶈瑙i櫎鐨勫紩鐢?br />
鏀規(guī)
public Object pop() {
if (size==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference娓呴櫎寮曠敤
return result;
}
褰撶▼搴忓憳絎竴嬈¤綾諱技榪欐牱鐨勯棶棰樺洶鎵扮殑鏃跺欙紝浠栦滑寰寰浼?xì)杩囧垎灏忓績(jī)锛?xì)瀵逛簬姣忎竴涓璞″紩鐢紝涓鏃︾▼搴忎笉鍐嶇敤鍒板畠錛屽氨鎶婂畠娓呯┖銆傝繖鏍峰仛鏃㈡病蹇呰錛屼篃涓嶆槸鎴戜滑鎵鏈熸湜鐨勶紝鍥犱負(fù)榪欐牱鍋氫細(xì)鎶婄▼搴忎唬鐮佸紕寰楀緢涔憋紝騫朵笖鍙互鎯沖儚榪樹(shù)細(xì)闄嶄綆紼嬪簭鐨勬ц兘銆傗滄竻絀哄璞″紩鐢ㄢ濊繖鏍風(fēng)殑鎿嶄綔搴旇鏄竴縐嶄緥澶栵紝鑰屼笉鏄竴縐嶈鑼冭涓恒傛秷闄よ繃鏈熷紩鐢ㄦ渶濂界殑鏂規(guī)硶鏄噸鐢ㄤ竴涓湰鏉ュ凡緇忓寘鍚璞″紩鐢ㄧ殑鍙橀噺錛屾垨鑰呰榪欎釜鍙橀噺緇撴潫鍏剁敓鍛藉懆鏈熴傚鏋滀綘鏄湪鏈绱у噾鐨勪綔鐢ㄥ煙鑼冨洿鍐呭畾涔夋瘡涓涓彉閲忥紙瑙佺2 9鏉★級(jí)錛屽垯榪欑鎯呭艦灝變細(xì)鑷劧鑰岀劧鍦板彂鐢熴傚簲璇ユ敞鎰忓埌錛屽湪鐩墠鐨凧 V M瀹炵幇騫沖彴涓婏紝浠呬粎閫鍑哄畾涔夊彉閲忕殑浠g爜鍧楁槸涓嶅鐨勶紝瑕佹兂浣垮紩鐢ㄦ秷澶憋紝蹇呴』閫鍑哄寘鍚鍙橀噺鐨勬柟娉曘?br />
涓鑸岃█錛屽彧瑕佷竴涓被鑷繁綆$悊瀹冪殑鍐呭瓨錛岀▼搴忓憳灝卞簲璇ヨ鎯曞唴瀛樻硠婕忛棶棰樸備竴鏃︿竴涓厓绱犺閲婃斁鎺夛紝鍒欒鍏冪礌涓寘鍚殑浠諱綍瀵硅薄寮曠敤搴旇瑕佽娓呯┖銆?br />
鍐呭瓨娉勬紡鐨勫彟涓涓父瑙佹潵婧愭槸緙撳瓨銆備竴鏃︿綘鎶婁竴涓璞″紩鐢ㄦ斁鍒頒竴涓紦瀛樹(shù)腑錛屽畠?yōu)寰堝?guī)槗琚仐蹇樻帀錛屼粠鑰屼嬌寰楀畠涓嶅啀鏈夌敤涔嬪悗寰堥暱涓孌墊椂闂村唴浠嶇劧鐣欏湪緙撳瓨?shù)?br />
]]>
String s = new String("silly"); // DON'T DO THIS!
String s = "No longer silly";//do this
In addition to reusing immutable objects, you can also reuse mutable objects that you know will not be modified. Here is a slightly more subtle and much more common example of what not to do, involving mutable objects that are never modified once their values have been computed:
闄や簡(jiǎn)閲嶇敤闈炲彲鍙樼殑瀵硅薄涔嬪錛屽浜庨偅浜涘凡鐭ヤ笉浼?xì)琚慨鏀圭殑鍙彉瀵硅薄锛屼綘涔熷彲浠ラ噸鐢ㄥ畠浠?br />
such as
public class Person {
private final Date birthDate;
// Other fields omitted
public Person(Date birthDate) {
this.birthDate = birthDate;
}
// DON'T DO THIS!
public boolean isBabyBoomer() {
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
鏀硅壇鐨勭増鏈?
The isBabyBoomer method unnecessarily creates a new Calendar, TimeZone, and two Date instances each time it is invoked. The version that follows avoids this inefficiency with a static initializer:
class Person {
private final Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
/**
* The starting and ending dates of the baby boom.
*/
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
}
public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0 &&
birthDate.compareTo(BOOM_END) < 0;
}
}
銆備竴涓傞厤鍣ㄦ槸鎸囪繖鏍蜂竴涓璞★細(xì)瀹冩妸鍔熻兘濮?br />鎵樼粰鍚庨潰鐨勪竴涓璞★紝浠庤屼負(fù)鍚庨潰鐨勫璞℃彁渚涗竴涓彲閫夌殑鎺ュ彛銆傜敱浜庨傞厤鍣ㄩ櫎浜?jiǎn)鍚庨潰鐨勫璞′箣澶栧Q屾病鏈夊叾浠栫殑鐘舵佷俊鎭紝鎵浠ラ拡瀵規(guī)煇涓粰瀹氬璞$殑鐗瑰畾閫傞厤鍣ㄨ岃█錛屽畠涓嶉渶瑕佸垱寤哄涓傞厤鍣ㄥ疄渚嬨?br />
This item should not be misconstrued to imply that object creation is expensive and should be avoided. On the contrary, the creation and reclamation of small objects whose constructors do little explicit work is cheap, especially on modern JVM implementations. Creating additional objects to enhance the clarity, simplicity, or power of a program is generally a good thing.
Conversely, avoiding object creation by maintaining your own object pool is a bad idea unless the objects in the pool are extremely heavyweight. A prototypical example of an object that does justify an object pool is a database connection. The cost of establishing the connection is sufficiently high that it makes sense to reuse these objects. Generally speaking, however, maintaining your own object pools clutters up your code, increases memory footprint, and harms performance. Modern JVM implementations have highly optimized garbage collectors that easily outperform such object pools on lightweight objects.
]]>
Occasionally you'll want to write a class that is just a grouping of static methods and static fields.鏈夋椂鍊?浣犳兂鍐欎竴涓被,鍙槸闇瑕佷粬鎻愪緵浜?jiǎn)涓緋誨垪鐨勫嚱鏁版搷浣滅瓑,鑰屼笉鎯寵瀹冨疄渚嬪寲.濡?java.lang.Math or java.util.Arrays.
浣嗘槸濡傛灉浣犱笉鎻愪緵鏋勯犲嚱鏁?緙栬瘧鍣ㄤ細(xì)鑷姩娣誨姞涓涓?
鎵浠ュ繀欏繪彁渚涗竴涓?姝ゆ椂,鎶婃瀯閫犲嚱鏁拌緗負(fù)private.灝卞彲浠ヨ揪鍒扮洰鐨?
涓鑸敤涓庡伐鍏風(fēng)被.
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
// This constructor will never be invoked
}
... // Remainder omitted
}
鐢變簬private鐨勬瀯鍜卞嚱鏁?璇ョ被涓嶈兘琚疄渚嬪寲.鍚屾椂.涓嶈兘琚戶鎵夸簡(jiǎn).
]]>
A singleton is simply a class that is instantiated exactly once [Gamma98, p. 127].濡傛暟鎹簱璧勬簮絳?
There are two approaches to implementing singletons. Both are based on keeping the
constructor private and providing a public static member to allow clients access to the sole
instance of the class. In one approach, the public static member is a final field:
// Singleton with final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
...
}
... // Remainder omitted
}
1:鏋勯犲嚱鏁扮鏈?br />2:鎻愪緵涓涓潤(rùn)鎬乻tatic鐨刦inal鎴愬憳
The private constructor is called only once, to initialize the public static final field
Elvis.INSTANCE.聽(tīng)
Exactly one Elvis instance will exist once the Elvis class is initialized鈥攏o more,
no less. Nothing that a client does can change this.
In a second approach, a public static factory method is provided instead of the public static final field:
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
...
}
public static Elvis getInstance() {
return INSTANCE;
}
... // Remainder omitted
}
鐢ㄩ潤(rùn)鎬佹柟娉曡繑鍥?鑰屼笉鏄洿鎺ヨ繑鍥炰竴涓疄渚?
濡傛灉瀵瑰璞″簭鍒楀寲,瑕佸仛澶氫竴鐐瑰伐浣?濡?br />To make a singleton class serializable (Chapter 10), it is not sufficient merely to add
implements Serializable to its declaration. To maintain the singleton guarantee, you must
also provide a readResolve method (Item 57). Otherwise, each deserialization of a serialized instance will result in the creation of a new instance, 鍚﹀垯,緇忚繃瀵硅薄鍙嶅簭鍒楀寲鍚?浼?xì)瀵艰嚧鍒涘晦Z簡(jiǎn)涓涓柊鐨勫璞?leading, in the case of our example, to spurious Elvis sightings. To prevent this, add the following readResolve method to the Elvis class:
// readResolve method to preserve singleton property
private Object readResolve() throws ObjectStreamException {
/*
* Return the one true Elvis and let the garbage collector
* take care of the Elvis impersonator.
*/
return INSTANCE;
}
]]>
constructors 鑰冭檻浠ラ潤(rùn)鎬佸伐鍘傛柟娉曞彇浠f瀯閫犲嚱鏁?br />
The normal way for a class to allow a client to obtain an instance is to provide a public
constructor.
A class can provide a public static factory method, which is simply
a static method that returns an instance of the class
such as
public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
}
advantages
1:One advantage of static factory methods is that, unlike constructors, they have names.
涓涓被闈?rùn)鎬佸伐鍘傛柟娉曟槸鏈夊悕瀛楃殑.If the parameters to a constructor do not, in and of themselves, describe the object being returned, a static factory with a well-chosen name can make a class easier to use and the resulting client code easier to read. 濡侭igInteger(int, int,Random)鍙煡閬搉ew浜?jiǎn)涓涓狟igInteger瀵硅薄,鑰孊igInteger.probablePrime()鑳借鏄庤繑鍥炵殑鍙兘鏄竴涓礌鏁板璞?鍙﹀,濡傛灉涓涓瀯閫犲嚱鏁頒粎浠呯敱浜庡弬鏁伴『搴忎笉鍚岃屾剰鎬濅篃涓嶅悓.闈?rùn)鎬佸伐鍘傛柟娉曞氨鏇村姞鏈夌敤浜?瀹冩洿鑳借鏄庡嚱鏁扮殑鎰忔?鍥犱負(fù)瀹冩湁涓涓悕瀛?
the reason聽(tīng)are : If the parameters to a constructor do not, describe the object being
returned, a static factory with a well-chosen name can make a class easier to use and the
resulting client code easier to read.
2:A second advantage of static factory methods is that, unlike constructors, they are not
required to create a new object each time they're invoked.姣忔璇鋒眰鏃?涓嶉渶瑕侀噸鏂板垱寤轟竴涓璞?---鍗曚緥鐨勬剰鎬?榪欏彲浠ュ湪浠諱綍鏃跺埢鎺у埗榪欎釜瀵硅薄,鍚屾椂鍦ㄦ瘮杈冪殑鏃跺欎篃涓嶉渶瑕佺敤equals鑰岀敤= =灝卞彲浠ヨВ鍐充簡(jiǎn).
3:A third advantage of static factory methods is that, unlike constructors, they can return
an object of any subtype of their return type.鍙互榪斿洖涓涓師榪斿洖綾誨瀷鐨勫瓙綾誨瀷瀵硅薄.
One application of this flexibility is that an API can return objects without making their
classes public.Hiding implementation classes in this fashion can lead to a very compact API.榪欑鐏墊椿鎬х殑涓涓簲鐢ㄦ槸錛屼竴涓狝 P I鍙互榪斿洖涓涓璞★紝鍚屾椂鍙堜笉浣胯瀵硅薄鐨勭被鎴愪負(fù)鍏湁鐨勩備互榪欑鏂瑰紡鎶婂叿浣撶殑瀹炵幇綾婚殣钘忚搗鏉ワ紝鍙互寰楀埌涓涓潪甯哥畝媧佺殑A P I銆傝繖欏規(guī)妧鏈潪甯擱傚悎
浜庡熀浜庢帴鍙g殑妗嗘灦緇撴瀯錛屽洜涓哄湪榪欐牱鐨勬鏋剁粨鏋勪腑錛屾帴鍙f垚涓洪潤(rùn)鎬佸伐鍘傛柟娉曠殑鑷劧榪斿洖綾誨瀷
sample:
// Provider framework sketch
public abstract class Foo {
// Maps String key to corresponding Class object
private static Map implementations = null;
// Initializes implementations map the first time it's called
private static synchronized void initMapIfNecessary() {
if (implementations == null) {
implementations = new HashMap();
// Load implementation class names and keys from
// Properties file, translate names into Class
// objects using Class.forName and store mappings.
...
}
}
public static Foo getInstance(String key) {
initMapIfNecessary();
Class c = (Class) implementations.get(key);
if (c == null)
return new DefaultFoo();
try {
return (Foo) c.newInstance();
} catch (Exception e) {
return new DefaultFoo();
}
}
}
鍏湁鐨勯潤(rùn)鎬佸伐鍘傛柟娉曟墍榪斿洖鐨勫璞$殑綾諱笉浠呭彲浠ユ槸闈炲叕鏈夌殑錛岃屼笖璇ョ被鍙互闅忕潃姣忔璋冪敤
鑰屽彂鐢熷彉鍖栵紝榪欏彇鍐充簬闈?rùn)鎬佸伐鍘傛柟娉曠殑鍙傛暟鍊箋傚彧瑕佹槸宸插0鏄庣殑榪斿洖綾誨瀷鐨勫瓙綾誨瀷錛岄兘鏄厑
璁哥殑銆傝屼笖錛屼負(fù)浜?jiǎn)澧炲鉴櫴Y浠剁殑鍙淮鎶ゆэ紝榪斿洖瀵硅薄鐨勭被涔熷彲浠ラ殢鐫涓嶅悓鐨勫彂琛岀増鏈屼笉鍚屻?br />disadvantages:
1:The main disadvantage of static factory methods is that classes without public or protected constructors cannot be subclassed.
綾誨鏋滀笉鍚叕鏈夌殑鎴栬呭彈淇濇姢鐨勬瀯閫犲嚱鏁幫紝灝變笉鑳借瀛愮被鍖栥傚浜庡叕鏈夌殑闈?rùn)鎬佸伐鍘傛墍榪斿洖鐨勯潪鍏湁綾伙紝涔熷悓鏍峰姝?br />
2:A second disadvantage of static factory methods is that they are not readily distinguishable from other static methods.
In summary, static factory methods and public constructors both have their uses, and it pays to understand their relative merits. Avoid the reflex to provide constructors without first considering static factories because static factories are often more appropriate. If you've weighed the two options and nothing pushes you strongly in either direction, it's probably best to provide a constructor simply because it's the norm.